Compare commits

...

43 Commits

Author SHA1 Message Date
Athou
99130d0181 combine EnvironmentSubstitutor and SubstitutingSourceProvider (#1050) 2023-04-25 10:40:14 +02:00
Athou
90e2036cbe build for armv7 too 2023-04-25 10:24:22 +02:00
Athou
c2f3e42867 Readme update 2023-04-25 08:57:55 +02:00
Athou
bd33369a41 Merge branch 'develop' 2023-04-25 08:48:49 +02:00
Athou
4f625d8ed5 add docker support 2023-04-25 07:48:52 +02:00
Athou
866fe56dd2 remove warning "HV000254: Missing parameter metadata" 2023-04-25 07:48:11 +02:00
Athou
5f37dbca4c fix env variables support, now works without having to change yml file 2023-04-24 20:28:58 +02:00
Athou
c49e617dfe lombok update 2023-04-24 18:56:47 +02:00
Athou
e763ffd4cf allow access to entries in json format with api key (fixes #1049) 2023-04-22 07:27:59 +02:00
Athou
20ab7dd3e1 readd bitcoin address 2023-03-16 17:09:12 +01:00
Jérémie Panzer
55741c6332 Merge pull request #1046 from dayuer/patch-1
Update zh.js
2023-03-13 16:41:01 +01:00
dayuer
42d85336a8 Update zh.js
Added some entry translations.
2023-03-13 23:33:05 +08:00
Athou
639b82f494 add goal to start typescript in watch mode, helps during big refactorings 2023-03-10 11:37:29 +01:00
Athou
5003c176a2 display parent category name in category selects (fixes #1045) 2023-03-07 08:40:02 +01:00
Athou
10bfbbec17 restore one-click list refresh (#1040) 2023-03-06 14:08:48 +01:00
Athou
3da900db7f add missing parenthesis in feed entry filtering example 2023-03-01 13:50:07 +01:00
Athou
9f421ec3b0 add context menu to navigate to feed if a category is displayed 2023-02-27 19:56:32 +01:00
Athou
69fb11eee0 add context menu item to open link in new tab 2023-02-27 07:41:13 +01:00
Athou
ffbb85df43 add context menu on entry headers 2023-02-24 15:18:04 +01:00
Athou
a4e78c4e0d throttle event 2023-02-24 14:53:30 +01:00
Jérémie Panzer
274c5ae165 Merge pull request #1039 from bartschinski/patch-1
Update german translation in de.js
2023-02-10 10:35:31 +01:00
Phillip Bartschinski
39c4012a1a Update de.js
Add missing translations
2023-02-10 10:28:21 +01:00
Athou
6d4b0cbdef bump all dependencies 2023-02-04 08:34:23 +01:00
Athou
ea4b120a85 prevent full feed fetching next time we fetch it if caching header values changed but content did not (#1037) 2023-01-27 08:49:59 +01:00
Athou
5c2454c331 bring back "refresh all my feeds" (#1036) 2023-01-19 07:29:19 +01:00
Athou
4ff46965c4 add websocket support to immediately refresh tree when new entries are available 2023-01-18 20:58:45 +01:00
Athou
33e3f7ea3c feeds added manually to the queue now refresh immediately instead of waiting up to 15s (#1036) 2023-01-18 18:53:38 +01:00
Athou
347fc4f2c8 allow to force refresh feed anytime (#1036) 2023-01-18 18:53:38 +01:00
Athou
2b4ff4a8a5 on fetch error and not under heavy load, don't increase refresh interval exponentially 2023-01-18 18:53:38 +01:00
Jorengarenar
f7d34983e0 Allow API key for count of unread 2023-01-10 00:34:25 +01:00
Athou
3271d69fcb allow session configuration (#1028) 2023-01-04 11:32:08 +01:00
Jérémie Panzer
7ea24b21f8 Merge pull request #1023 from Jorengarenar/develop
Enable environment variables in config.yml
2023-01-04 07:26:23 +01:00
Jorengarenar
b2b608e8c3 Enable environment variables in config.yml 2023-01-03 19:46:01 +01:00
Athou
e44ea5bc96 re-add dropwizard-migrations to be able to use liquibase from command line 2022-11-15 10:48:55 +01:00
Athou
fa58b1e53f reload entries after marking everything as read, as commafeed 2.x does 2022-11-15 08:24:18 +01:00
Athou
9466bc544c show placeholder when favicon is loading 2022-11-08 11:57:59 +01:00
Athou
9e65f5726c require src and alt for images 2022-11-08 11:52:47 +01:00
Athou
fc2c3740a0 dependencies update 2022-11-08 11:52:47 +01:00
Athou
2095a6512b make compact mode more compact 2022-11-05 14:13:19 +01:00
Athou
a461a72224 enable maven cache to speed up build 2022-11-04 09:08:21 +01:00
Athou
f9e7653901 restore swipe-to-right to toggle read/unread status (#1019) 2022-11-04 08:57:33 +01:00
Jérémie Panzer
754ac166e0 Merge pull request #1017 from tristianc/bugfix/builddir
Determine build directory from variable instead of hardcoding it.
2022-09-29 15:54:14 +02:00
Tristian Celestin
0b18334236 Determine build directory from variable instead of hardcoding it. 2022-09-29 09:49:20 -04:00
90 changed files with 5488 additions and 1979 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
# ignore everything
*
# allow only what we need
!commafeed-server/target/commafeed.jar
!commafeed-server/config.yml.example

View File

@@ -1,27 +1,57 @@
name: Java CI name: Java CI
on: [push] on: [ push ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
java: ["8", "11", "17"] java: [ "8", "11", "17" ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
# Setup
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Set up Java - name: Set up Java
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
distribution: "temurin" distribution: "temurin"
cache: "maven"
# Build
- name: Build with Maven - name: Build with Maven
run: mvn --batch-mode --update-snapshots verify run: mvn --batch-mode --update-snapshots verify
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: ${{ matrix.java == '8' }} if: ${{ matrix.java == '8' }}
with: with:
name: commafeed.jar name: commafeed.jar
path: commafeed-server/target/commafeed.jar path: commafeed-server/target/commafeed.jar
# Docker
- name: Login to Container Registry
if: ${{ github.ref_type == 'tag' && matrix.java == '8' }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm/v7
push: ${{ github.ref_type == 'tag' && matrix.java == '8' }}
tags: |
athou/commafeed:latest
athou/commafeed:${{ github.ref_name }}

12
Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM eclipse-temurin:17-jre
RUN mkdir -p /commafeed/data
VOLUME /commafeed/data
ENV CF_SESSION_PATH=/commafeed/data/sessions
COPY commafeed-server/target/commafeed.jar .
COPY commafeed-server/config.yml.example config.yml
EXPOSE 8082
CMD ["java", "-Djava.net.preferIPv4Stack=true", "-jar", "commafeed.jar", "server", "config.yml"]

View File

@@ -16,7 +16,11 @@ Browser extensions:
## Deployment on your own server ## Deployment on your own server
### The very short version (download precompiled package) ### Docker
Docker images are built automatically and are available at https://hub.docker.com/r/athou/commafeed
### Download precompiled package
mkdir commafeed && cd commafeed mkdir commafeed && cd commafeed
wget https://github.com/Athou/commafeed/releases/download/3.0.0/commafeed.jar wget https://github.com/Athou/commafeed/releases/download/3.0.0/commafeed.jar
@@ -24,7 +28,10 @@ Browser extensions:
vi config.yml vi config.yml
java -Djava.net.preferIPv4Stack=true -jar commafeed.jar server config.yml java -Djava.net.preferIPv4Stack=true -jar commafeed.jar server config.yml
### The short version (build from sources) The server will listen on http://localhost:8082. The default
user is `admin` and the default password is `admin`.
### Build from sources
git clone https://github.com/Athou/commafeed.git git clone https://github.com/Athou/commafeed.git
cd commafeed cd commafeed
@@ -33,67 +40,33 @@ Browser extensions:
vi config.yml vi config.yml
java -Djava.net.preferIPv4Stack=true -jar commafeed-server/target/commafeed.jar server config.yml java -Djava.net.preferIPv4Stack=true -jar commafeed-server/target/commafeed.jar server config.yml
### The long version (same as the short version, but more detailed) The server will listen on http://localhost:8082. The default
user is `admin` and the default password is `admin`.
CommaFeed 2.0 has been rewritten to use Dropwizard and gulp instead of using tomee and wro4j. The latest version of the 1.x branch is available [here](https://github.com/Athou/commafeed/tree/1.x). ## Translation
For storage, you can either use an embedded file-based H2 database or an external MySQL, PostgreSQL or SQLServer database. Files for internationalization are
You also need the Java 1.8+ JDK in order to build the application. located [here](https://github.com/Athou/commafeed/tree/master/commafeed-client/src/locales).
To install the required packages to build CommaFeed on Ubuntu, issue the following commands
# if this commands works and returns a version >= 1.8.0 you're good to go and you can skip JDK installation
javac -version
# if openjdk-8-jdk is not available on your ubuntu version (14.04 LTS), add the following repo first
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install g++ build-essential openjdk-8-jdk
# Make sure java8 is the selected java version
sudo update-alternatives --config java
sudo update-alternatives --config javac
Clone this repository. If you don't have git you can download the sources as a zip file from [here](https://github.com/Athou/commafeed/archive/master.zip)
git clone https://github.com/Athou/commafeed.git
cd commafeed
Now build the application
./mvnw clean package
Copy `commafeed-server/config.yml.example` to `./config.yml` then edit the file to your liking.
Issue the following command to run the app, the server will listen by default on `http://localhost:8082`. The default user is `admin` and the default password is `admin`.
java -Djava.net.preferIPv4Stack=true -jar commafeed-server/target/commafeed.jar server config.yml
You can use a proxy http server such as nginx or apache.
## Translate CommaFeed into your language
Files for internationalization are located [here](https://github.com/Athou/commafeed/tree/master/commafeed-client/src/locales).
To add a new language: To add a new language:
- edit `commafeed-client/src/i18n.ts` - edit `commafeed-client/src/i18n.ts`
- add the new locale to the `locales` array. - add the new locale to the `locales` array.
- import the dayjs locale - import the dayjs locale
- edit `commafeed-client/.linguirc` and add the new locale to the `locales` array. - edit `commafeed-client/.linguirc` and add the new locale to the `locales` array.
- run `npm run i18n` and add translations to the newly created `commafeed-client/src/locales/[locale]/messages.po` file - run `npm run i18n` and add translations to the newly created `commafeed-client/src/locales/[locale]/messages.po` file
The name of the locale should be the two-letters [ISO-639-1 language code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). The name of the locale should be the
two-letters [ISO-639-1 language code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
## Local development ## Local development
- `git clone https://github.com/Athou/CommaFeed`
### Backend ### Backend
- Open `commafeed-server` in your preferred Java IDE. - Open `commafeed-server` in your preferred Java IDE.
- CommaFeed uses Lombok, you need the Lombok plugin for your IDE. - CommaFeed uses Lombok, you need the Lombok plugin for your IDE.
- If using Eclipse, Go to Window → Preferences → Maven → Annotation Processing and check "Automatically configure JDT APT" - If using Eclipse, Go to Window → Preferences → Maven → Annotation Processing and check "Automatically configure
JDT APT"
- Start `CommaFeedApplication.java` in debug mode with `server config.dev.yml` as arguments - Start `CommaFeedApplication.java` in debug mode with `server config.dev.yml` as arguments
### Frontend ### Frontend
@@ -101,11 +74,12 @@ The name of the locale should be the two-letters [ISO-639-1 language code](http:
- Open `commafeed-client` in your preferred JavaScript IDE. - Open `commafeed-client` in your preferred JavaScript IDE.
- run `npm install` - run `npm install`
- run `npm run dev` - run `npm run dev`
- the frontend server is now running at http://localhost:8082 and is proxying REST requests to the backend running on port 8083 - the frontend server is now running at http://localhost:8082 and is proxying REST requests to the backend running on
port 8083
## Copyright and license ## Copyright and license
Copyright 2013-2022 CommaFeed. Copyright 2013-2023 CommaFeed.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this work except in compliance with the License. you may not use this work except in compliance with the License.

View File

@@ -70,8 +70,6 @@
"groups": [ "groups": [
"useLocation", "useLocation",
"useParams", "useParams",
"useStyles",
"useMantineTheme",
"useState", "useState",
"useAppSelector", "useAppSelector",
"useAppDispatch", "useAppDispatch",

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,8 @@
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --host",
"dev:typescript": "tsc --watch",
"build": "npm run i18n:compile && tsc && vite build", "build": "npm run i18n:compile && tsc && vite build",
"preview": "vite preview", "preview": "vite preview",
"test": "vitest", "test": "vitest",
@@ -17,60 +18,63 @@
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.10.5", "@emotion/react": "^11.10.5",
"@fontsource/open-sans": "^4.5.13", "@fontsource/open-sans": "^4.5.14",
"@lingui/core": "^3.14.0", "@lingui/core": "^3.17.0",
"@lingui/macro": "^3.14.0", "@lingui/macro": "^3.17.0",
"@lingui/react": "^3.14.0", "@lingui/react": "^3.17.0",
"@mantine/core": "^5.6.3", "@mantine/core": "^5.10.3",
"@mantine/form": "^5.6.3", "@mantine/form": "^5.10.3",
"@mantine/hooks": "^5.6.3", "@mantine/hooks": "^5.10.3",
"@mantine/modals": "^5.6.3", "@mantine/modals": "^5.10.3",
"@mantine/notifications": "^5.6.3", "@mantine/notifications": "^5.10.3",
"@mantine/spotlight": "^5.6.3", "@mantine/spotlight": "^5.10.3",
"@reduxjs/toolkit": "^1.8.6", "@reduxjs/toolkit": "^1.9.2",
"axios": "^1.1.3", "axios": "^1.3.2",
"dayjs": "^1.11.6", "dayjs": "^1.11.7",
"interweave": "^13.0.0", "interweave": "^13.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"make-plural": "^7.1.0", "make-plural": "^7.2.0",
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-async-hook": "^4.0.0", "react-async-hook": "^4.0.0",
"react-contexify": "^6.0.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-icons": "^4.6.0", "react-icons": "^4.7.1",
"react-infinite-scroller": "^1.2.6", "react-infinite-scroller": "^1.2.6",
"react-redux": "^8.0.4", "react-redux": "^8.0.5",
"react-router-dom": "^6.4.2", "react-router-dom": "^6.8.0",
"swagger-ui-react": "^4.15.2", "react-swipeable": "^7.0.0",
"tinycon": "^0.6.8" "swagger-ui-react": "^4.15.5",
"tinycon": "^0.6.8",
"websocket-heartbeat-js": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
"@lingui/cli": "^3.14.0", "@lingui/cli": "^3.17.0",
"@types/eslint": "^8.4.8", "@types/eslint": "^8.21.0",
"@types/lodash": "^4.14.186", "@types/lodash": "^4.14.191",
"@types/mousetrap": "^1.6.10", "@types/mousetrap": "^1.6.11",
"@types/react": "^18.0.24", "@types/react": "^18.0.27",
"@types/react-dom": "^18.0.8", "@types/react-dom": "^18.0.10",
"@types/react-infinite-scroller": "^1.2.3", "@types/react-infinite-scroller": "^1.2.3",
"@types/swagger-ui-react": "^4.11.0", "@types/swagger-ui-react": "^4.11.0",
"@types/tinycon": "^0.6.3", "@types/tinycon": "^0.6.3",
"@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.41.0", "@typescript-eslint/parser": "^5.50.0",
"@vitejs/plugin-react": "^2.2.0", "@vitejs/plugin-react": "^3.1.0",
"eslint": "^8.26.0", "eslint": "^8.33.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.6.0",
"eslint-config-react-app": "^7.0.1", "eslint-config-react-app": "^7.0.1",
"eslint-plugin-hooks": "^0.4.3", "eslint-plugin-hooks": "^0.4.3",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rollup-plugin-visualizer": "^5.8.3", "rollup-plugin-visualizer": "^5.9.0",
"typescript": "^4.8.4", "typescript": "^4.9.5",
"vite": "^3.2.0", "vite": "^4.1.1",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^3.5.2", "vite-tsconfig-paths": "^4.0.5",
"vitest": "^0.24.3", "vitest": "^0.28.4",
"vitest-mock-extended": "^1.0.3" "vitest-mock-extended": "^1.0.9"
} }
} }

View File

@@ -58,6 +58,7 @@ export const client = {
getEntries: (req: GetEntriesPaginatedRequest) => axiosInstance.get<Entries>("feed/entries", { params: req }), getEntries: (req: GetEntriesPaginatedRequest) => axiosInstance.get<Entries>("feed/entries", { params: req }),
markEntries: (req: MarkRequest) => axiosInstance.post("feed/mark", req), markEntries: (req: MarkRequest) => axiosInstance.post("feed/mark", req),
fetchFeed: (req: FeedInfoRequest) => axiosInstance.post<FeedInfo>("feed/fetch", req), fetchFeed: (req: FeedInfoRequest) => axiosInstance.post<FeedInfo>("feed/fetch", req),
refreshAll: () => axiosInstance.get("feed/refreshAll"),
subscribe: (req: SubscribeRequest) => axiosInstance.post<number>("feed/subscribe", req), subscribe: (req: SubscribeRequest) => axiosInstance.post<number>("feed/subscribe", req),
unsubscribe: (req: IDRequest) => axiosInstance.post("feed/unsubscribe", req), unsubscribe: (req: IDRequest) => axiosInstance.post("feed/unsubscribe", req),
importOpml: (req: File) => { importOpml: (req: File) => {

View File

@@ -97,4 +97,5 @@ export const Constants = {
mainScrollAreaId: "main-scroll-area-id", mainScrollAreaId: "main-scroll-area-id",
entryId: (entry: Entry) => `entry-id-${entry.id}`, entryId: (entry: Entry) => `entry-id-${entry.id}`,
}, },
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
} }

View File

@@ -128,6 +128,7 @@ export const markAllEntries = createAsyncThunk<void, { sourceType: EntrySourceTy
async (arg, thunkApi) => { async (arg, thunkApi) => {
const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries
await endpoint(arg.req) await endpoint(arg.req)
thunkApi.dispatch(reloadEntries())
thunkApi.dispatch(reloadTree()) thunkApi.dispatch(reloadTree())
} }
) )

View File

@@ -38,6 +38,7 @@ export interface ApplicationSettings {
export interface Category { export interface Category {
id: string id: string
parentId?: string parentId?: string
parentName?: string
name: string name: string
children: Category[] children: Category[]
feeds: Subscription[] feeds: Subscription[]

View File

@@ -51,3 +51,16 @@ export const scrollToWithCallback = ({
element.scrollTo(options) element.scrollTo(options)
} }
export const openLinkInBackgroundTab = (url: string) => {
// simulate ctrl+click to open tab in background
const a = document.createElement("a")
a.href = url
a.rel = "noreferrer"
a.dispatchEvent(
new MouseEvent("click", {
ctrlKey: true,
metaKey: true,
})
)
}

View File

@@ -3,13 +3,16 @@ import { useState } from "react"
import { TbPhoto } from "react-icons/tb" import { TbPhoto } from "react-icons/tb"
interface ImageWithPlaceholderWhileLoadingProps { interface ImageWithPlaceholderWhileLoadingProps {
src?: string src: string
alt?: string alt: string
title?: string title?: string
width?: number width?: number
height?: number | "auto" height?: number | "auto"
placeholderWidth?: number placeholderWidth?: number
placeholderHeight?: number placeholderHeight?: number
placeholderBackgroundColor?: string
placeholderIconSize?: number
placeholderIconColor?: string
} }
const useStyles = createStyles((theme, props: ImageWithPlaceholderWhileLoadingProps) => ({ const useStyles = createStyles((theme, props: ImageWithPlaceholderWhileLoadingProps) => ({
@@ -17,8 +20,8 @@ const useStyles = createStyles((theme, props: ImageWithPlaceholderWhileLoadingPr
width: props.placeholderWidth ?? 400, width: props.placeholderWidth ?? 400,
height: props.placeholderHeight ?? 600, height: props.placeholderHeight ?? 600,
maxWidth: "100%", maxWidth: "100%",
color: theme.fn.variant({ color: theme.primaryColor, variant: "subtle" }).color, color: props.placeholderIconColor ?? theme.fn.variant({ color: theme.primaryColor, variant: "subtle" }).color,
backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[1], backgroundColor: props.placeholderBackgroundColor ?? (theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[1]),
}, },
})) }))
@@ -32,7 +35,7 @@ export function ImageWithPlaceholderWhileLoading(props: ImageWithPlaceholderWhil
<Box> <Box>
<Center className={classes.placeholder}> <Center className={classes.placeholder}>
<div> <div>
<TbPhoto size={48} /> <TbPhoto size={props.placeholderIconSize ?? 48} />
</div> </div>
</Center> </Center>
</Box> </Box>

View File

@@ -95,6 +95,10 @@ export function KeyboardShortcutsHelp() {
</td> </td>
<td> <td>
<Kbd>B</Kbd> <Kbd>B</Kbd>
<span>, </span>
<Kbd>
<Trans>Middle click</Trans>
</Kbd>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -103,6 +107,8 @@ export function KeyboardShortcutsHelp() {
</td> </td>
<td> <td>
<Kbd>M</Kbd> <Kbd>M</Kbd>
<span>, </span>
<Trans>Swipe header to the right</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -143,6 +149,26 @@ export function KeyboardShortcutsHelp() {
<Kbd>U</Kbd> <Kbd>U</Kbd>
</td> </td>
</tr> </tr>
<tr>
<td>
<Trans>Show entry menu (desktop)</Trans>
</td>
<td>
<Kbd>
<Trans>Right click</Trans>
</Kbd>
</td>
</tr>
<tr>
<td>
<Trans>Show entry menu (mobile)</Trans>
</td>
<td>
<Kbd>
<Trans>Long press</Trans>
</Kbd>
</td>
</tr>
<tr> <tr>
<td> <td>
<Trans>Show keyboard shortcut help</Trans> <Trans>Show keyboard shortcut help</Trans>

View File

@@ -29,7 +29,9 @@ const transform: TransformCallback = node => {
if (node.tagName === "IMG") { if (node.tagName === "IMG") {
// show placeholders for loading img tags, this allows the entry to have its final height immediately // show placeholders for loading img tags, this allows the entry to have its final height immediately
const src = node.getAttribute("src") ?? undefined const src = node.getAttribute("src") ?? undefined
const alt = node.getAttribute("alt") ?? undefined if (!src) return undefined
const alt = node.getAttribute("alt") ?? "image"
const title = node.getAttribute("title") ?? undefined const title = node.getAttribute("title") ?? undefined
const nodeWidth = node.getAttribute("width") const nodeWidth = node.getAttribute("width")
const nodeHeight = node.getAttribute("height") const nodeHeight = node.getAttribute("height")
@@ -40,6 +42,7 @@ const transform: TransformCallback = node => {
height, height,
maxWidth: Constants.layout.entryMaxWidth, maxWidth: Constants.layout.entryMaxWidth,
}) })
return ( return (
<ImageWithPlaceholderWhileLoading <ImageWithPlaceholderWhileLoading
src={src} src={src}

View File

@@ -1,7 +1,7 @@
import { TypographyStylesProvider } from "@mantine/core" import { TypographyStylesProvider } from "@mantine/core"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
export function Enclosure(props: { enclosureType?: string; enclosureUrl?: string }) { export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) {
const hasVideo = props.enclosureType && props.enclosureType.indexOf("video") === 0 const hasVideo = props.enclosureType && props.enclosureType.indexOf("video") === 0
const hasAudio = props.enclosureType && props.enclosureType.indexOf("audio") === 0 const hasAudio = props.enclosureType && props.enclosureType.indexOf("audio") === 0
const hasImage = props.enclosureType && props.enclosureType.indexOf("image") === 0 const hasImage = props.enclosureType && props.enclosureType.indexOf("image") === 0

View File

@@ -13,6 +13,7 @@ import {
} from "app/slices/entries" } from "app/slices/entries"
import { redirectToRootCategory } from "app/slices/redirect" import { redirectToRootCategory } from "app/slices/redirect"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { openLinkInBackgroundTab } from "app/utils"
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp" import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
import { Loader } from "components/Loader" import { Loader } from "components/Loader"
import { useMousetrap } from "hooks/useMousetrap" import { useMousetrap } from "hooks/useMousetrap"
@@ -211,15 +212,7 @@ export function FeedEntries() {
useMousetrap("b", () => { useMousetrap("b", () => {
// simulate ctrl+click to open tab in background // simulate ctrl+click to open tab in background
if (!selectedEntry) return if (!selectedEntry) return
const a = document.createElement("a") openLinkInBackgroundTab(selectedEntry.url)
a.href = selectedEntry.url
a.rel = "noreferrer"
a.dispatchEvent(
new MouseEvent("click", {
ctrlKey: true,
metaKey: true,
})
)
}) })
useMousetrap("m", () => { useMousetrap("m", () => {
// toggle read status // toggle read status

View File

@@ -1,10 +1,13 @@
import { Anchor, Box, createStyles, Divider, Paper } from "@mantine/core" import { Anchor, Box, createStyles, Divider, Paper } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useAppSelector } from "app/store" import { markEntry } from "app/slices/entries"
import { useAppDispatch, useAppSelector } from "app/store"
import { Entry } from "app/types" import { Entry } from "app/types"
import React from "react" import React from "react"
import { useSwipeable } from "react-swipeable"
import { FeedEntryBody } from "./FeedEntryBody" import { FeedEntryBody } from "./FeedEntryBody"
import { FeedEntryCompactHeader } from "./FeedEntryCompactHeader" import { FeedEntryCompactHeader } from "./FeedEntryCompactHeader"
import { FeedEntryContextMenu, useFeedEntryContextMenu } from "./FeedEntryContextMenu"
import { FeedEntryFooter } from "./FeedEntryFooter" import { FeedEntryFooter } from "./FeedEntryFooter"
import { FeedEntryHeader } from "./FeedEntryHeader" import { FeedEntryHeader } from "./FeedEntryHeader"
@@ -15,19 +18,25 @@ interface FeedEntryProps {
onHeaderClick: (e: React.MouseEvent) => void onHeaderClick: (e: React.MouseEvent) => void
} }
const useStyles = createStyles((theme, props: FeedEntryProps) => { const useStyles = createStyles((theme, props: FeedEntryProps & { compact: boolean }) => {
let backgroundColor let backgroundColor
if (theme.colorScheme === "dark") backgroundColor = props.entry.read ? "inherit" : theme.colors.dark[5] if (theme.colorScheme === "dark") backgroundColor = props.entry.read ? "inherit" : theme.colors.dark[5]
else backgroundColor = props.entry.read && !props.expanded ? theme.colors.gray[0] : "inherit" else backgroundColor = props.entry.read && !props.expanded ? theme.colors.gray[0] : "inherit"
let marginY = theme.spacing.xs
if (props.compact) marginY = 6
let mobileMarginY = 6
if (props.compact) mobileMarginY = 4
const styles = { const styles = {
paper: { paper: {
backgroundColor, backgroundColor,
marginTop: theme.spacing.xs, marginTop: marginY,
marginBottom: theme.spacing.xs, marginBottom: marginY,
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: { [theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
marginTop: "6px", marginTop: mobileMarginY,
marginBottom: "6px", marginBottom: mobileMarginY,
}, },
}, },
body: { body: {
@@ -43,12 +52,25 @@ const useStyles = createStyles((theme, props: FeedEntryProps) => {
}) })
export function FeedEntry(props: FeedEntryProps) { export function FeedEntry(props: FeedEntryProps) {
const { classes } = useStyles(props)
const viewMode = useAppSelector(state => state.user.settings?.viewMode) const viewMode = useAppSelector(state => state.user.settings?.viewMode)
const compactHeader = viewMode === "title" && !props.expanded const compact = viewMode === "title"
const compactHeader = compact && !props.expanded
const { classes } = useStyles({ ...props, compact })
const dispatch = useAppDispatch()
const swipeHandlers = useSwipeable({
onSwipedRight: () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read })),
})
const { onContextMenu } = useFeedEntryContextMenu(props.entry)
const spacing = compact ? 8 : "xs"
const borderRadius = compact ? "xs" : "sm"
return ( return (
<Paper withBorder className={classes.paper}> <Paper withBorder radius={borderRadius} className={classes.paper}>
<Anchor <Anchor
variant="text" variant="text"
href={props.entry.url} href={props.entry.url}
@@ -56,21 +78,24 @@ export function FeedEntry(props: FeedEntryProps) {
rel="noreferrer" rel="noreferrer"
onClick={props.onHeaderClick} onClick={props.onHeaderClick}
onAuxClick={props.onHeaderClick} onAuxClick={props.onHeaderClick}
onContextMenu={onContextMenu}
> >
<Box p="xs"> <Box p={spacing} {...swipeHandlers}>
{compactHeader && <FeedEntryCompactHeader entry={props.entry} />} {compactHeader && <FeedEntryCompactHeader entry={props.entry} />}
{!compactHeader && <FeedEntryHeader entry={props.entry} expanded={props.expanded} />} {!compactHeader && <FeedEntryHeader entry={props.entry} expanded={props.expanded} />}
</Box> </Box>
</Anchor> </Anchor>
{props.expanded && ( {props.expanded && (
<Box px="xs" pb="xs"> <Box px={spacing} pb={spacing}>
<Box className={classes.body} sx={{ direction: props.entry.rtl ? "rtl" : "ltr" }}> <Box className={classes.body} sx={{ direction: props.entry.rtl ? "rtl" : "ltr" }}>
<FeedEntryBody entry={props.entry} /> <FeedEntryBody entry={props.entry} />
</Box> </Box>
<Divider variant="dashed" my="xs" /> <Divider variant="dashed" my={spacing} />
<FeedEntryFooter entry={props.entry} /> <FeedEntryFooter entry={props.entry} />
</Box> </Box>
)} )}
<FeedEntryContextMenu entry={props.entry} />
</Paper> </Paper>
) )
} }

View File

@@ -14,7 +14,7 @@ export function FeedEntryBody(props: FeedEntryBodyProps) {
<Box> <Box>
<Content content={props.entry.content} /> <Content content={props.entry.content} />
</Box> </Box>
{props.entry.enclosureUrl && ( {props.entry.enclosureType && props.entry.enclosureUrl && (
<Box pt="md"> <Box pt="md">
<Enclosure enclosureType={props.entry.enclosureType} enclosureUrl={props.entry.enclosureUrl} /> <Enclosure enclosureType={props.entry.enclosureType} enclosureUrl={props.entry.enclosureUrl} />
</Box> </Box>

View File

@@ -1,8 +1,9 @@
import { Box, createStyles, Image, Text } from "@mantine/core" import { Box, createStyles, Text } from "@mantine/core"
import { Entry } from "app/types" import { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate" import { RelativeDate } from "components/RelativeDate"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "components/responsive/OnDesktop"
import { FeedEntryTitle } from "./FeedEntryTitle" import { FeedEntryTitle } from "./FeedEntryTitle"
import { FeedFavicon } from "./FeedFavicon"
export interface FeedEntryHeaderProps { export interface FeedEntryHeaderProps {
entry: Entry entry: Entry
@@ -37,7 +38,7 @@ export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
return ( return (
<Box className={classes.wrapper}> <Box className={classes.wrapper}>
<Box> <Box>
<Image withPlaceholder src={props.entry.iconUrl} alt="feed icon" width={18} height={18} /> <FeedFavicon url={props.entry.iconUrl} />
</Box> </Box>
<OnDesktop> <OnDesktop>
<Text color="dimmed" className={classes.feedName}> <Text color="dimmed" className={classes.feedName}>

View File

@@ -0,0 +1,127 @@
import { t, Trans } from "@lingui/macro"
import { createStyles, Group } from "@mantine/core"
import { Constants } from "app/constants"
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
import { redirectToFeed } from "app/slices/redirect"
import { useAppDispatch, useAppSelector } from "app/store"
import { Entry } from "app/types"
import { openLinkInBackgroundTab } from "app/utils"
import { throttle, truncate } from "lodash"
import { useEffect } from "react"
import { Item, Menu, Separator, useContextMenu } from "react-contexify"
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbRss, TbStar, TbStarOff } from "react-icons/tb"
interface FeedEntryContextMenuProps {
entry: Entry
}
const iconSize = 16
const useStyles = createStyles(theme => ({
menu: {
// apply mantine theme from MenuItem.styles.ts
fontSize: theme.fontSizes.sm,
"--contexify-item-color": `${theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black} !important`,
"--contexify-activeItem-color": `${theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black} !important`,
"--contexify-activeItem-bgColor": `${
theme.colorScheme === "dark" ? theme.fn.rgba(theme.colors.dark[3], 0.35) : theme.colors.gray[1]
} !important`,
},
}))
const menuId = (entry: Entry) => entry.id
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
const { classes, theme } = useStyles()
const sourceType = useAppSelector(state => state.entries.source.type)
const dispatch = useAppDispatch()
return (
<Menu id={menuId(props.entry)} theme={theme.colorScheme} animation={false} className={classes.menu}>
<Item
onClick={() => {
window.open(props.entry.url, "_blank", "noreferrer")
dispatch(markEntry({ entry: props.entry, read: true }))
}}
>
<Group>
<TbExternalLink size={iconSize} />
<Trans>Open link in new tab</Trans>
</Group>
</Item>
<Item
onClick={() => {
openLinkInBackgroundTab(props.entry.url)
dispatch(markEntry({ entry: props.entry, read: true }))
}}
>
<Group>
<TbExternalLink size={iconSize} />
<Trans>Open link in new background tab</Trans>
</Group>
</Item>
<Separator />
<Item onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}>
<Group>
{props.entry.starred ? <TbStarOff size={iconSize} /> : <TbStar size={iconSize} />}
{props.entry.starred ? t`Unstar` : t`Star`}
</Group>
</Item>
<Item onClick={() => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))}>
<Group>
{props.entry.read ? <TbEyeOff size={iconSize} /> : <TbEyeCheck size={iconSize} />}
{props.entry.read ? t`Keep unread` : t`Mark as read`}
</Group>
</Item>
<Item onClick={() => dispatch(markEntriesUpToEntry(props.entry))}>
<Group>
<TbArrowBarToDown size={iconSize} />
<Trans>Mark as read up to here</Trans>
</Group>
</Item>
{sourceType === "category" && (
<>
<Separator />
<Item
onClick={() => {
dispatch(redirectToFeed(props.entry.feedId))
}}
>
<Group>
<TbRss size={iconSize} />
<Trans>Go to {truncate(props.entry.feedName)}</Trans>
</Group>
</Item>
</>
)}
</Menu>
)
}
export function useFeedEntryContextMenu(entry: Entry) {
const contextMenu = useContextMenu({
id: menuId(entry),
})
const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault()
contextMenu.show({
event,
})
}
// close context menu on scroll
useEffect(() => {
const scrollArea = document.getElementById(Constants.dom.mainScrollAreaId)
const listener = () => contextMenu.hideAll()
const throttledListener = throttle(listener, 100)
scrollArea?.addEventListener("scroll", throttledListener)
return () => scrollArea?.removeEventListener("scroll", throttledListener)
}, [contextMenu])
return { onContextMenu }
}

View File

@@ -7,6 +7,7 @@ import { useAppDispatch, useAppSelector } from "app/store"
import { Entry } from "app/types" import { Entry } from "app/types"
import { ActionButton } from "components/ActionButtton" import { ActionButton } from "components/ActionButtton"
import { ButtonToolbar } from "components/ButtonToolbar" import { ButtonToolbar } from "components/ButtonToolbar"
import { throttle } from "lodash"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbShare, TbStar, TbStarOff, TbTag } from "react-icons/tb" import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbShare, TbStar, TbStarOff, TbTag } from "react-icons/tb"
import { ShareButtons } from "./ShareButtons" import { ShareButtons } from "./ShareButtons"
@@ -22,8 +23,7 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
const mobile = !useMediaQuery(`(min-width: ${Constants.layout.mobileBreakpoint}px)`) const mobile = !useMediaQuery(`(min-width: ${Constants.layout.mobileBreakpoint}px)`)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const showSharingButtons = const showSharingButtons = sharingSettings && Object.values(sharingSettings).some(v => v)
sharingSettings && (Object.values(sharingSettings) as Array<typeof sharingSettings[keyof typeof sharingSettings]>).some(v => v)
const readStatusButtonClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read })) const readStatusButtonClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))
const onTagsChange = (values: string[]) => const onTagsChange = (values: string[]) =>
@@ -38,8 +38,10 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
const scrollArea = document.getElementById(Constants.dom.mainScrollAreaId) const scrollArea = document.getElementById(Constants.dom.mainScrollAreaId)
const listener = () => setScrollPosition(scrollArea ? scrollArea.scrollTop : 0) const listener = () => setScrollPosition(scrollArea ? scrollArea.scrollTop : 0)
scrollArea?.addEventListener("scroll", listener) const throttledListener = throttle(listener, 100)
return () => scrollArea?.removeEventListener("scroll", listener)
scrollArea?.addEventListener("scroll", throttledListener)
return () => scrollArea?.removeEventListener("scroll", throttledListener)
}, []) }, [])
return ( return (

View File

@@ -1,7 +1,8 @@
import { Box, createStyles, Image, Text } from "@mantine/core" import { Box, createStyles, Text } from "@mantine/core"
import { Entry } from "app/types" import { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate" import { RelativeDate } from "components/RelativeDate"
import { FeedEntryTitle } from "./FeedEntryTitle" import { FeedEntryTitle } from "./FeedEntryTitle"
import { FeedFavicon } from "./FeedFavicon"
export interface FeedEntryHeaderProps { export interface FeedEntryHeaderProps {
entry: Entry entry: Entry
@@ -33,7 +34,7 @@ export function FeedEntryHeader(props: FeedEntryHeaderProps) {
</Box> </Box>
<Box className={classes.headerSubtext}> <Box className={classes.headerSubtext}>
<Box mr={6}> <Box mr={6}>
<Image withPlaceholder src={props.entry.iconUrl} alt="feed icon" width={18} height={18} /> <FeedFavicon url={props.entry.iconUrl} />
</Box> </Box>
<Box> <Box>
<Text color="dimmed">{props.entry.feedName}</Text> <Text color="dimmed">{props.entry.feedName}</Text>

View File

@@ -0,0 +1,22 @@
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
export interface FeedFaviconProps {
url: string
size?: number
}
export function FeedFavicon({ url, size = 18 }: FeedFaviconProps) {
return (
<ImageWithPlaceholderWhileLoading
src={url}
alt="feed favicon"
width={size}
height={size}
placeholderWidth={size}
placeholderHeight={size}
placeholderBackgroundColor="inherit"
placeholderIconSize={size}
placeholderIconColor="inherit"
/>
)
}

View File

@@ -4,16 +4,20 @@ import { Constants } from "app/constants"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { flattenCategoryTree } from "app/utils" import { flattenCategoryTree } from "app/utils"
type CategorySelectProps = Partial<SelectProps> & { withAll?: boolean } type CategorySelectProps = Partial<SelectProps> & {
withAll?: boolean
withoutCategoryIds?: string[]
}
export function CategorySelect(props: CategorySelectProps) { export function CategorySelect(props: CategorySelectProps) {
const rootCategory = useAppSelector(state => state.tree.rootCategory) const rootCategory = useAppSelector(state => state.tree.rootCategory)
const categories = rootCategory && flattenCategoryTree(rootCategory) const categories = rootCategory && flattenCategoryTree(rootCategory)
const selectData: SelectItem[] | undefined = categories const selectData: SelectItem[] | undefined = categories
?.filter(c => c.id !== Constants.categories.all.id) ?.filter(c => c.id !== Constants.categories.all.id)
.filter(c => !props.withoutCategoryIds || !props.withoutCategoryIds.includes(c.id))
.sort((c1, c2) => c1.name.localeCompare(c2.name)) .sort((c1, c2) => c1.name.localeCompare(c2.name))
.map(c => ({ .map(c => ({
label: c.name, label: c.parentName ? t`${c.name} (in ${c.parentName})` : c.name,
value: c.id, value: c.id,
})) }))
if (props.withAll) { if (props.withAll) {

View File

@@ -1,11 +1,25 @@
import { Trans } from "@lingui/macro" import { t, Trans } from "@lingui/macro"
import { Box, Divider, Group, Menu, SegmentedControl, SegmentedControlItem, useMantineColorScheme } from "@mantine/core" import { Box, Divider, Group, Menu, SegmentedControl, SegmentedControlItem, useMantineColorScheme } from "@mantine/core"
import { showNotification } from "@mantine/notifications"
import { client } from "app/client"
import { redirectToAbout, redirectToAdminUsers, redirectToMetrics, redirectToSettings } from "app/slices/redirect" import { redirectToAbout, redirectToAdminUsers, redirectToMetrics, redirectToSettings } from "app/slices/redirect"
import { changeViewMode } from "app/slices/user" import { changeViewMode } from "app/slices/user"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { ViewMode } from "app/types" import { ViewMode } from "app/types"
import { useState } from "react" import { useState } from "react"
import { TbChartLine, TbHelp, TbLayoutList, TbList, TbMoon, TbNotes, TbPower, TbSettings, TbSun, TbUsers } from "react-icons/tb" import {
TbChartLine,
TbHelp,
TbLayoutList,
TbList,
TbMoon,
TbNotes,
TbPower,
TbSettings,
TbSun,
TbUsers,
TbWorldDownload,
} from "react-icons/tb"
interface ProfileMenuProps { interface ProfileMenuProps {
control: React.ReactElement control: React.ReactElement
@@ -56,6 +70,7 @@ const viewModeData: ViewModeControlItem[] = [
export function ProfileMenu(props: ProfileMenuProps) { export function ProfileMenu(props: ProfileMenuProps) {
const [opened, setOpened] = useState(false) const [opened, setOpened] = useState(false)
const viewMode = useAppSelector(state => state.user.settings?.viewMode) const viewMode = useAppSelector(state => state.user.settings?.viewMode)
const profile = useAppSelector(state => state.user.profile)
const admin = useAppSelector(state => state.user.profile?.admin) const admin = useAppSelector(state => state.user.profile?.admin)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { colorScheme, toggleColorScheme } = useMantineColorScheme() const { colorScheme, toggleColorScheme } = useMantineColorScheme()
@@ -69,6 +84,7 @@ export function ProfileMenu(props: ProfileMenuProps) {
<Menu position="bottom-end" closeOnItemClick={false} opened={opened} onChange={setOpened}> <Menu position="bottom-end" closeOnItemClick={false} opened={opened} onChange={setOpened}>
<Menu.Target>{props.control}</Menu.Target> <Menu.Target>{props.control}</Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
{profile && <Menu.Label>{profile.name}</Menu.Label>}
<Menu.Item <Menu.Item
icon={<TbSettings size={iconSize} />} icon={<TbSettings size={iconSize} />}
onClick={() => { onClick={() => {
@@ -78,6 +94,21 @@ export function ProfileMenu(props: ProfileMenuProps) {
> >
<Trans>Settings</Trans> <Trans>Settings</Trans>
</Menu.Item> </Menu.Item>
<Menu.Item
icon={<TbWorldDownload size={iconSize} />}
onClick={() =>
client.feed.refreshAll().then(() => {
showNotification({
message: t`Your feeds have been queued for refresh.`,
color: "green",
autoClose: 1000,
})
setOpened(false)
})
}
>
<Trans>Fetch all my feeds now</Trans>
</Menu.Item>
<Divider /> <Divider />
<Menu.Label> <Menu.Label>

View File

@@ -22,7 +22,7 @@ export function ProfileSettings() {
const form = useForm<FormData>({ const form = useForm<FormData>({
validate: { validate: {
newPasswordConfirmation: (value: string, values: FormData) => (value !== values.newPassword ? t`Passwords do not match` : null), newPasswordConfirmation: (value, values) => (value !== values.newPassword ? t`Passwords do not match` : null),
}, },
}) })
const { setValues } = form const { setValues } = form

View File

@@ -1,4 +1,5 @@
import { Box, createStyles, Image } from "@mantine/core" import { Box, createStyles } from "@mantine/core"
import { FeedFavicon } from "components/content/FeedFavicon"
import React, { ReactNode } from "react" import React, { ReactNode } from "react"
import { UnreadCount } from "./UnreadCount" import { UnreadCount } from "./UnreadCount"
@@ -49,11 +50,7 @@ export function TreeNode(props: TreeNodeProps) {
return ( return (
<Box py={1} pl={props.level * 20} className={classes.node} onClick={(e: React.MouseEvent) => props.onClick(e, props.id)}> <Box py={1} pl={props.level * 20} className={classes.node} onClick={(e: React.MouseEvent) => props.onClick(e, props.id)}>
<Box mr={6} onClick={(e: React.MouseEvent) => props.onIconClick && props.onIconClick(e, props.id)}> <Box mr={6} onClick={(e: React.MouseEvent) => props.onIconClick && props.onIconClick(e, props.id)}>
{typeof props.icon === "string" ? ( {typeof props.icon === "string" ? <FeedFavicon url={props.icon} /> : props.icon}
<Image withPlaceholder src={props.icon} alt="favicon" width={18} height={18} />
) : (
props.icon
)}
</Box> </Box>
<Box className={classes.nodeText}>{props.name}</Box> <Box className={classes.nodeText}>{props.name}</Box>
{!props.expanded && ( {!props.expanded && (

View File

@@ -1,9 +1,10 @@
import { t } from "@lingui/macro" import { t } from "@lingui/macro"
import { Box, Center, Image, Kbd, TextInput } from "@mantine/core" import { Box, Center, Kbd, TextInput } from "@mantine/core"
import { openSpotlight, SpotlightAction, SpotlightProvider } from "@mantine/spotlight" import { openSpotlight, SpotlightAction, SpotlightProvider } from "@mantine/spotlight"
import { redirectToFeed } from "app/slices/redirect" import { redirectToFeed } from "app/slices/redirect"
import { useAppDispatch } from "app/store" import { useAppDispatch } from "app/store"
import { Subscription } from "app/types" import { Subscription } from "app/types"
import { FeedFavicon } from "components/content/FeedFavicon"
import { useMousetrap } from "hooks/useMousetrap" import { useMousetrap } from "hooks/useMousetrap"
import { TbSearch } from "react-icons/tb" import { TbSearch } from "react-icons/tb"
@@ -17,7 +18,7 @@ export function TreeSearch(props: TreeSearchProps) {
.sort((f1, f2) => f1.name.localeCompare(f2.name)) .sort((f1, f2) => f1.name.localeCompare(f2.name))
.map(f => ({ .map(f => ({
title: f.name, title: f.name,
icon: <Image withPlaceholder src={f.iconUrl} alt="favicon" width={18} height={18} />, icon: <FeedFavicon url={f.iconUrl} />,
onTrigger: () => dispatch(redirectToFeed(f.id)), onTrigger: () => dispatch(redirectToFeed(f.id)),
})) }))

View File

@@ -0,0 +1,24 @@
import { reloadTree } from "app/slices/tree"
import { useAppDispatch } from "app/store"
import { useEffect } from "react"
import WebsocketHeartbeatJs from "websocket-heartbeat-js"
export const useWebSocket = () => {
const dispatch = useAppDispatch()
useEffect(() => {
const currentUrl = new URL(window.location.href)
const wsProtocol = currentUrl.protocol === "http:" ? "ws" : "wss"
const wsUrl = `${wsProtocol}://${currentUrl.hostname}:${currentUrl.port}/ws`
const ws = new WebsocketHeartbeatJs({ url: wsUrl, pingMsg: "ping" })
ws.onmessage = event => {
const { data } = event
if (typeof data === "string") {
if (data.startsWith("new-feed-entries:")) dispatch(reloadTree())
}
}
return () => ws.close()
}, [dispatch])
}

View File

@@ -29,7 +29,37 @@ import "dayjs/locale/sk"
import "dayjs/locale/sv" import "dayjs/locale/sv"
import "dayjs/locale/tr" import "dayjs/locale/tr"
import "dayjs/locale/zh" import "dayjs/locale/zh"
import { ar, ca, cs, cy, da, de, en, es, fa, fi, fr, gl, hu, id, it, ja, ko, ms, nb, nl, nn, pl, pt, ru, sk, sv, tr, zh } from "make-plural" import {
ar,
ca,
cs,
cy,
da,
de,
en,
es,
fa,
fi,
fr,
gl,
hu,
id,
it,
ja,
ko,
ms,
nb,
nl,
nn,
pl,
PluralCategory,
pt,
ru,
sk,
sv,
tr,
zh,
} from "make-plural"
import { useEffect } from "react" import { useEffect } from "react"
import { messages as arMessages } from "./locales/ar/messages" import { messages as arMessages } from "./locales/ar/messages"
import { messages as caMessages } from "./locales/ca/messages" import { messages as caMessages } from "./locales/ca/messages"
@@ -64,7 +94,7 @@ interface Locale {
key: string key: string
label: string label: string
messages: Messages messages: Messages
plurals?: (n: number | string, ord?: boolean) => string plurals?: (n: number | string, ord?: boolean) => PluralCategory
} }
// add an object to the array to add a new locale // add an object to the array to add a new locale

View File

@@ -288,10 +288,18 @@ msgstr "موجز URL"
msgid "Feed name" msgid "Feed name"
msgstr "اسم الخلاصة" msgstr "اسم الخلاصة"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "تصفية التعبير" msgstr "تصفية التعبير"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "هل نسيت كلمة المرور؟" msgstr "هل نسيت كلمة المرور؟"
@@ -320,6 +328,10 @@ msgstr "انتقل إلى وثائق API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "اذهب إلى طريقة العرض \"الكل\"" msgstr "اذهب إلى طريقة العرض \"الكل\""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "الأشياء الجيدة" msgstr "الأشياء الجيدة"
@@ -348,6 +360,7 @@ msgstr "استيراد"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "في العرض الموسع ، التمرير عبر الإدخالات وضع علامة عليها كمقروءة" msgstr "في العرض الموسع ، التمرير عبر الإدخالات وضع علامة عليها كمقروءة"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "إبقاء غير مقروءة" msgstr "إبقاء غير مقروءة"
@@ -404,6 +417,10 @@ msgstr "تسجيل الدخول"
msgid "Logout" msgid "Logout"
msgstr "تسجيل الخروج" msgstr "تسجيل الخروج"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "تعليم الكل كمقروء"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "تعليم كافة الإدخالات كمقروءة" msgstr "تعليم كافة الإدخالات كمقروءة"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "وضع علامة كمقروء" msgstr "وضع علامة كمقروء"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "وضع علامة كمقروءة حتى هنا" msgstr "وضع علامة كمقروءة حتى هنا"
@@ -430,6 +449,10 @@ msgstr "وضع علامة كمقروءة حتى هنا"
msgid "Metrics" msgid "Metrics"
msgstr "المقاييس" msgstr "المقاييس"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "تحريك الصفحة لأسفل" msgstr "تحريك الصفحة لأسفل"
@@ -515,6 +538,14 @@ msgstr "فتح الإدخال الحالي في علامة تبويب جديدة
msgid "Open link" msgid "Open link"
msgstr "افتح الرابط" msgstr "افتح الرابط"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "فتح الإدخال التالي" msgstr "فتح الإدخال التالي"
@@ -581,6 +612,10 @@ msgstr "تحديث"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا" msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "مشاركة المواقع"
msgid "Shift" msgid "Shift"
msgstr "الحلقة" msgstr "الحلقة"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "إظهار موجز ويب والفئات التي لا تحتوي على إدخالات غير مقروءة" msgstr "إظهار موجز ويب والفئات التي لا تحتوي على إدخالات غير مقروءة"
@@ -654,6 +697,7 @@ msgstr "شيء سيء حدث للتو ..."
msgid "Space" msgid "Space"
msgstr "فضاء" msgstr "فضاء"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "النجم" msgstr "النجم"
@@ -681,6 +725,10 @@ msgstr "الاشتراك في موجز ويب"
msgid "Success" msgid "Success"
msgstr "النجاح" msgstr "النجاح"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "التبديل إلى النسق الداكن" msgstr "التبديل إلى النسق الداكن"
@@ -713,6 +761,7 @@ msgstr "جرب CommaFeed باستخدام الحساب التجريبي: تجر
msgid "Unread" msgid "Unread"
msgstr "غير مقروءة" msgstr "غير مقروءة"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "إلغاء النجم" msgstr "إلغاء النجم"
@@ -743,6 +792,14 @@ msgstr "موقع الكتروني"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "ليس لديك أي اشتراكات حتى الآن. " msgstr "ليس لديك أي اشتراكات حتى الآن. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "الملف مطلوب" msgstr "الملف مطلوب"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL del canal"
msgid "Feed name" msgid "Feed name"
msgstr "Nom del canal" msgstr "Nom del canal"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Expressió de filtratge" msgstr "Expressió de filtratge"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Heu oblidat la contrasenya?" msgstr "Heu oblidat la contrasenya?"
@@ -320,6 +328,10 @@ msgstr "Vés a la documentació de l'API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Vés a la vista Tot" msgstr "Vés a la vista Tot"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Bones" msgstr "Bones"
@@ -348,6 +360,7 @@ msgstr "Importació"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "a la vista ampliada, desplaçant-se per les entrades les marqueu com a llegides" msgstr "a la vista ampliada, desplaçant-se per les entrades les marqueu com a llegides"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Mantenir sense llegir" msgstr "Mantenir sense llegir"
@@ -404,6 +417,10 @@ msgstr "Inicia sessió"
msgid "Logout" msgid "Logout"
msgstr "Tanca sessió" msgstr "Tanca sessió"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Marca-ho tot com a llegit"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marqueu totes les entrades com a llegides" msgstr "Marqueu totes les entrades com a llegides"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Marca com a llegit" msgstr "Marca com a llegit"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Marca com a llegit fins aquí" msgstr "Marca com a llegit fins aquí"
@@ -430,6 +449,10 @@ msgstr "Marca com a llegit fins aquí"
msgid "Metrics" msgid "Metrics"
msgstr "mètriques" msgstr "mètriques"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Mou la pàgina cap avall" msgstr "Mou la pàgina cap avall"
@@ -515,6 +538,14 @@ msgstr "Obre l'entrada actual en una pestanya nova al fons"
msgid "Open link" msgid "Open link"
msgstr "Enllaç obert" msgstr "Enllaç obert"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Obre la següent entrada" msgstr "Obre la següent entrada"
@@ -581,6 +612,10 @@ msgstr "Actualitzar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Els registres estan tancats en aquesta instància de CommaFeed" msgstr "Els registres estan tancats en aquesta instància de CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Compartir llocs"
msgid "Shift" msgid "Shift"
msgstr "canvi" msgstr "canvi"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Mostra feeds i categories sense entrades no llegides" msgstr "Mostra feeds i categories sense entrades no llegides"
@@ -654,6 +697,7 @@ msgstr "Acaba de passar una cosa dolenta..."
msgid "Space" msgid "Space"
msgstr "Espai" msgstr "Espai"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Estrella" msgstr "Estrella"
@@ -681,6 +725,10 @@ msgstr "Subscriu-te al canal"
msgid "Success" msgid "Success"
msgstr "Éxit" msgstr "Éxit"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Canvia al tema fosc" msgstr "Canvia al tema fosc"
@@ -713,6 +761,7 @@ msgstr "Proveu CommaFeed amb el compte de demostració: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Sense llegir" msgstr "Sense llegir"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Desestrellar" msgstr "Desestrellar"
@@ -743,6 +792,14 @@ msgstr "Lloc web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Encara no teniu cap subscripció. " msgstr "Encara no teniu cap subscripció. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "el fitxer és necessari" msgstr "el fitxer és necessari"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL zdroje"
msgid "Feed name" msgid "Feed name"
msgstr "Název zdroje" msgstr "Název zdroje"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrování výrazu" msgstr "Filtrování výrazu"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Zapomněli jste heslo?" msgstr "Zapomněli jste heslo?"
@@ -320,6 +328,10 @@ msgstr "Přejděte na dokumentaci API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Přejděte do zobrazení Vše" msgstr "Přejděte do zobrazení Vše"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Dobroty" msgstr "Dobroty"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "V rozšířeném zobrazení je procházením označíte jako přečtené" msgstr "V rozšířeném zobrazení je procházením označíte jako přečtené"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Ponechat nepřečtené" msgstr "Ponechat nepřečtené"
@@ -404,6 +417,10 @@ msgstr "Přihlaste se"
msgid "Logout" msgid "Logout"
msgstr "Odhlášení" msgstr "Odhlášení"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Označit vše jako přečtené"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Označte všechny položky jako přečtené" msgstr "Označte všechny položky jako přečtené"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Označit jako přečtené" msgstr "Označit jako přečtené"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Označit jako přečtené až sem" msgstr "Označit jako přečtené až sem"
@@ -430,6 +449,10 @@ msgstr "Označit jako přečtené až sem"
msgid "Metrics" msgid "Metrics"
msgstr "Metriky" msgstr "Metriky"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Přesuňte stránku dolů" msgstr "Přesuňte stránku dolů"
@@ -515,6 +538,14 @@ msgstr "Otevřít aktuální položku na nové kartě na pozadí"
msgid "Open link" msgid "Open link"
msgstr "Otevřít odkaz" msgstr "Otevřít odkaz"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Otevřete další položku" msgstr "Otevřete další položku"
@@ -581,6 +612,10 @@ msgstr "Obnovit"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "V této instanci CommaFeed jsou registrace uzavřeny" msgstr "V této instanci CommaFeed jsou registrace uzavřeny"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Stránky pro sdílení"
msgid "Shift" msgid "Shift"
msgstr "Směna" msgstr "Směna"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Zobrazit kanály a kategorie bez nepřečtených položek" msgstr "Zobrazit kanály a kategorie bez nepřečtených položek"
@@ -654,6 +697,7 @@ msgstr "Právě se stalo něco špatného..."
msgid "Space" msgid "Space"
msgstr "Vesmír" msgstr "Vesmír"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Hvězda" msgstr "Hvězda"
@@ -681,6 +725,10 @@ msgstr "Přihlaste se k odběru kanálu"
msgid "Success" msgid "Success"
msgstr "Úspěch" msgstr "Úspěch"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Přepněte na tmavý motiv" msgstr "Přepněte na tmavý motiv"
@@ -713,6 +761,7 @@ msgstr "Vyzkoušejte CommaFeed s demo účtem: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Nepřečteno" msgstr "Nepřečteno"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Odstranit hvězdu" msgstr "Odstranit hvězdu"
@@ -743,6 +792,14 @@ msgstr "Webové stránky"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Zatím nemáte žádné předplatné. " msgstr "Zatím nemáte žádné předplatné. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "" msgstr ""
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL porthiant"
msgid "Feed name" msgid "Feed name"
msgstr "Enw porthiant" msgstr "Enw porthiant"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Hidlo mynegiant" msgstr "Hidlo mynegiant"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Wedi anghofio cyfrinair?" msgstr "Wedi anghofio cyfrinair?"
@@ -320,6 +328,10 @@ msgstr "Ewch i'r ddogfennaeth API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Ewch i'r golwg Pawb" msgstr "Ewch i'r golwg Pawb"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "nwyddau" msgstr "nwyddau"
@@ -348,6 +360,7 @@ msgstr "Mewnforio"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Mewn gwedd estynedig, mae sgrolio trwy gofnodion yn nodi eu bod wedi'u darllen" msgstr "Mewn gwedd estynedig, mae sgrolio trwy gofnodion yn nodi eu bod wedi'u darllen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Cadwch heb ei ddarllen" msgstr "Cadwch heb ei ddarllen"
@@ -404,6 +417,10 @@ msgstr "Mewngofnodi"
msgid "Logout" msgid "Logout"
msgstr "Allgofnodi" msgstr "Allgofnodi"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Marciwch y cyfan wedi'i ddarllen"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marciwch bob cofnod wedi'i ddarllen" msgstr "Marciwch bob cofnod wedi'i ddarllen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Marciwch ei fod wedi'i ddarllen" msgstr "Marciwch ei fod wedi'i ddarllen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Marciwch fel y darllenwyd hyd yma" msgstr "Marciwch fel y darllenwyd hyd yma"
@@ -430,6 +449,10 @@ msgstr "Marciwch fel y darllenwyd hyd yma"
msgid "Metrics" msgid "Metrics"
msgstr "metrigau" msgstr "metrigau"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Symudwch y dudalen i lawr" msgstr "Symudwch y dudalen i lawr"
@@ -515,6 +538,14 @@ msgstr "Agorwch y cofnod cyfredol mewn tab newydd yn y cefndir"
msgid "Open link" msgid "Open link"
msgstr "Dolen agored" msgstr "Dolen agored"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Agor y cofnod nesaf" msgstr "Agor y cofnod nesaf"
@@ -581,6 +612,10 @@ msgstr "Adnewyddu"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn" msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Rhannu gwefannau"
msgid "Shift" msgid "Shift"
msgstr "shifft" msgstr "shifft"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Dangos ffrydiau a chategorïau heb unrhyw gofnodion heb eu darllen" msgstr "Dangos ffrydiau a chategorïau heb unrhyw gofnodion heb eu darllen"
@@ -654,6 +697,7 @@ msgstr "Mae rhywbeth drwg newydd ddigwydd ..."
msgid "Space" msgid "Space"
msgstr "Gofod" msgstr "Gofod"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "seren" msgstr "seren"
@@ -681,6 +725,10 @@ msgstr "Tanysgrifio i'r porthiant"
msgid "Success" msgid "Success"
msgstr "Llwyddiant" msgstr "Llwyddiant"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Newid i thema dywyll" msgstr "Newid i thema dywyll"
@@ -713,6 +761,7 @@ msgstr "Rhowch gynnig ar CommaFeed gyda'r cyfrif demo: demo / demo"
msgid "Unread" msgid "Unread"
msgstr "Heb ei ddarllen" msgstr "Heb ei ddarllen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "dad-seren" msgstr "dad-seren"
@@ -743,6 +792,14 @@ msgstr "Gwefan"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Nid oes gennych unrhyw danysgrifiadau eto. " msgstr "Nid oes gennych unrhyw danysgrifiadau eto. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "mae angen y ffeil" msgstr "mae angen y ffeil"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr ""
msgid "Feed name" msgid "Feed name"
msgstr "Feednavn" msgstr "Feednavn"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrerende udtryk" msgstr "Filtrerende udtryk"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Glemt adgangskode?" msgstr "Glemt adgangskode?"
@@ -320,6 +328,10 @@ msgstr "Gå til API-dokumentationen."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Gå til visningen Alle" msgstr "Gå til visningen Alle"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Godbidder" msgstr "Godbidder"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "I udvidet visning markerer du dem som læst, når du ruller gennem poster" msgstr "I udvidet visning markerer du dem som læst, når du ruller gennem poster"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Forbehold ulæst" msgstr "Forbehold ulæst"
@@ -404,6 +417,10 @@ msgstr "Log ind"
msgid "Logout" msgid "Logout"
msgstr "Log ud" msgstr "Log ud"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Marker alle som læst"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marker alle poster som læst" msgstr "Marker alle poster som læst"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Markér som læst" msgstr "Markér som læst"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Markér som læst indtil her" msgstr "Markér som læst indtil her"
@@ -430,6 +449,10 @@ msgstr "Markér som læst indtil her"
msgid "Metrics" msgid "Metrics"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Flyt siden ned" msgstr "Flyt siden ned"
@@ -515,6 +538,14 @@ msgstr "Åbn den aktuelle post i en ny fane i baggrunden"
msgid "Open link" msgid "Open link"
msgstr "Åbent link" msgstr "Åbent link"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Åbn næste post" msgstr "Åbn næste post"
@@ -581,6 +612,10 @@ msgstr "Opdater"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er lukket på denne CommaFeed-instans" msgstr "Registreringer er lukket på denne CommaFeed-instans"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Delingssider"
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Vis feeds og kategorier uden ulæste poster" msgstr "Vis feeds og kategorier uden ulæste poster"
@@ -654,6 +697,7 @@ msgstr "Der er lige sket noget slemt..."
msgid "Space" msgid "Space"
msgstr "Rum" msgstr "Rum"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Stjerne" msgstr "Stjerne"
@@ -681,6 +725,10 @@ msgstr "Abonner på feedet"
msgid "Success" msgid "Success"
msgstr "Succes" msgstr "Succes"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Skift til mørkt tema" msgstr "Skift til mørkt tema"
@@ -713,6 +761,7 @@ msgstr "Prøv CommaFeed med demokontoen: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Ulæst" msgstr "Ulæst"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "" msgstr ""
@@ -743,6 +792,14 @@ msgstr "Hjemmeside"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Du har ingen abonnementer endnu. " msgstr "Du har ingen abonnementer endnu. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fil er påkrævet" msgstr "fil er påkrævet"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Feed-URL"
msgid "Feed name" msgid "Feed name"
msgstr "Feedname" msgstr "Feedname"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filterausdruck" msgstr "Filterausdruck"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Passwort vergessen?" msgstr "Passwort vergessen?"
@@ -320,6 +328,10 @@ msgstr "Gehen Sie zur API-Dokumentation."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Zur Ansicht Alle wechseln" msgstr "Zur Ansicht Alle wechseln"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Gutes" msgstr "Gutes"
@@ -348,6 +360,7 @@ msgstr "Importieren"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "In der erweiterten Ansicht werden Einträge beim Scrollen als gelesen markiert" msgstr "In der erweiterten Ansicht werden Einträge beim Scrollen als gelesen markiert"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Ungelesen lassen" msgstr "Ungelesen lassen"
@@ -404,6 +417,10 @@ msgstr "Einloggen"
msgid "Logout" msgid "Logout"
msgstr "Abmelden" msgstr "Abmelden"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Alle als gelesen markieren"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Alle Einträge als gelesen markieren" msgstr "Alle Einträge als gelesen markieren"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Als gelesen markieren" msgstr "Als gelesen markieren"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Bis hierhin als gelesen markieren" msgstr "Bis hierhin als gelesen markieren"
@@ -430,6 +449,10 @@ msgstr "Bis hierhin als gelesen markieren"
msgid "Metrics" msgid "Metrics"
msgstr "Metriken" msgstr "Metriken"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Seite nach unten verschieben" msgstr "Seite nach unten verschieben"
@@ -515,6 +538,14 @@ msgstr "Aktuellen Eintrag in neuem Tab im Hintergrund öffnen"
msgid "Open link" msgid "Open link"
msgstr "Link öffnen" msgstr "Link öffnen"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Nächsten Eintrag öffnen" msgstr "Nächsten Eintrag öffnen"
@@ -581,6 +612,10 @@ msgstr "Aktualisieren"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen" msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Seiten teilen"
msgid "Shift" msgid "Shift"
msgstr "Verschiebung" msgstr "Verschiebung"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Feeds und Kategorien ohne ungelesene Einträge anzeigen" msgstr "Feeds und Kategorien ohne ungelesene Einträge anzeigen"
@@ -654,6 +697,7 @@ msgstr "Etwas Schlimmes ist gerade passiert..."
msgid "Space" msgid "Space"
msgstr "Raum" msgstr "Raum"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Stern" msgstr "Stern"
@@ -681,6 +725,10 @@ msgstr "Feed abonnieren"
msgid "Success" msgid "Success"
msgstr "Erfolg" msgstr "Erfolg"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Zum dunklen Design wechseln" msgstr "Zum dunklen Design wechseln"
@@ -713,6 +761,7 @@ msgstr "Testen Sie CommaFeed mit dem Demokonto: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Ungelesen" msgstr "Ungelesen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Stern entfernen" msgstr "Stern entfernen"
@@ -743,6 +792,14 @@ msgstr "Webseite"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Sie haben noch keine Abonnements. " msgstr "Sie haben noch keine Abonnements. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "Datei ist erforderlich" msgstr "Datei ist erforderlich"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Feed URL"
msgid "Feed name" msgid "Feed name"
msgstr "Feed name" msgstr "Feed name"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr "Fetch all my feeds now"
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtering expression" msgstr "Filtering expression"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Forgot password?" msgstr "Forgot password?"
@@ -320,6 +328,10 @@ msgstr "Go to the API documentation."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Go to the All view" msgstr "Go to the All view"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr "Go to {0}"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Goodies" msgstr "Goodies"
@@ -348,6 +360,7 @@ msgstr "Import"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "In expanded view, scrolling through entries mark them as read" msgstr "In expanded view, scrolling through entries mark them as read"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Keep unread" msgstr "Keep unread"
@@ -404,6 +417,10 @@ msgstr "Log in"
msgid "Logout" msgid "Logout"
msgstr "Logout" msgstr "Logout"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr "Long press"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Mark all as read"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Mark all entries as read" msgstr "Mark all entries as read"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Mark as read" msgstr "Mark as read"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Mark as read up to here" msgstr "Mark as read up to here"
@@ -430,6 +449,10 @@ msgstr "Mark as read up to here"
msgid "Metrics" msgid "Metrics"
msgstr "Metrics" msgstr "Metrics"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr "Middle click"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Move the page down" msgstr "Move the page down"
@@ -515,6 +538,14 @@ msgstr "Open current entry in a new tab in the background"
msgid "Open link" msgid "Open link"
msgstr "Open link" msgstr "Open link"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr "Open link in new background tab"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr "Open link in new tab"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Open next entry" msgstr "Open next entry"
@@ -581,6 +612,10 @@ msgstr "Refresh"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registrations are closed on this CommaFeed instance" msgstr "Registrations are closed on this CommaFeed instance"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Right click"
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Sharing sites"
msgid "Shift" msgid "Shift"
msgstr "Shift" msgstr "Shift"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr "Show entry menu (desktop)"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr "Show entry menu (mobile)"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Show feeds and categories with no unread entries" msgstr "Show feeds and categories with no unread entries"
@@ -654,6 +697,7 @@ msgstr "Something bad just happened..."
msgid "Space" msgid "Space"
msgstr "Space" msgstr "Space"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Star" msgstr "Star"
@@ -681,6 +725,10 @@ msgstr "Subscribe to the feed"
msgid "Success" msgid "Success"
msgstr "Success" msgstr "Success"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr "Swipe header to the right"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Switch to dark theme" msgstr "Switch to dark theme"
@@ -713,6 +761,7 @@ msgstr "Try out CommaFeed with the demo account: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Unread" msgstr "Unread"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Unstar" msgstr "Unstar"
@@ -743,6 +792,14 @@ msgstr "Website"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgstr "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr "Your feeds have been queued for refresh."
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "file is required" msgstr "file is required"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr "{0} (in {1})"

View File

@@ -288,10 +288,18 @@ msgstr "URL de fuente"
msgid "Feed name" msgid "Feed name"
msgstr "Nombre de alimentación" msgstr "Nombre de alimentación"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Expresión de filtrado" msgstr "Expresión de filtrado"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "¿Olvidaste la contraseña?" msgstr "¿Olvidaste la contraseña?"
@@ -320,6 +328,10 @@ msgstr "Ir a la documentación de la API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Ir a la vista Todo" msgstr "Ir a la vista Todo"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "golosinas" msgstr "golosinas"
@@ -348,6 +360,7 @@ msgstr "Importar"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "En la vista ampliada, al desplazarse por las entradas, márquelas como leídas" msgstr "En la vista ampliada, al desplazarse por las entradas, márquelas como leídas"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Mantener sin leer" msgstr "Mantener sin leer"
@@ -404,6 +417,10 @@ msgstr "Iniciar sesión"
msgid "Logout" msgid "Logout"
msgstr "Salir" msgstr "Salir"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Marcar todo como leído"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marcar todas las entradas como leídas" msgstr "Marcar todas las entradas como leídas"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Marcar como leído" msgstr "Marcar como leído"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Marcar como leído hasta aquí" msgstr "Marcar como leído hasta aquí"
@@ -430,6 +449,10 @@ msgstr "Marcar como leído hasta aquí"
msgid "Metrics" msgid "Metrics"
msgstr "Métricas" msgstr "Métricas"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Mover la página hacia abajo" msgstr "Mover la página hacia abajo"
@@ -515,6 +538,14 @@ msgstr "Abrir la entrada actual en una nueva pestaña en segundo plano"
msgid "Open link" msgid "Open link"
msgstr "Abrir enlace" msgstr "Abrir enlace"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Abrir siguiente entrada" msgstr "Abrir siguiente entrada"
@@ -581,6 +612,10 @@ msgstr "Actualizar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Los registros están cerrados en esta instancia de CommaFeed" msgstr "Los registros están cerrados en esta instancia de CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Compartir sitios"
msgid "Shift" msgid "Shift"
msgstr "Cambio" msgstr "Cambio"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Mostrar feeds y categorías sin entradas no leídas" msgstr "Mostrar feeds y categorías sin entradas no leídas"
@@ -654,6 +697,7 @@ msgstr "Algo malo acaba de pasar..."
msgid "Space" msgid "Space"
msgstr "Espacio" msgstr "Espacio"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "estrella" msgstr "estrella"
@@ -681,6 +725,10 @@ msgstr "Suscríbete a la fuente"
msgid "Success" msgid "Success"
msgstr "Éxito" msgstr "Éxito"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Cambiar a tema oscuro" msgstr "Cambiar a tema oscuro"
@@ -713,6 +761,7 @@ msgstr "Pruebe CommaFeed con la cuenta demo: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "No leído" msgstr "No leído"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Desmarcar" msgstr "Desmarcar"
@@ -743,6 +792,14 @@ msgstr "Sitio web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Todavía no tienes ninguna suscripción. " msgstr "Todavía no tienes ninguna suscripción. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "archivo requerido" msgstr "archivo requerido"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL فید"
msgid "Feed name" msgid "Feed name"
msgstr "نام فید" msgstr "نام فید"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "بیان فیلتر" msgstr "بیان فیلتر"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "رمز عبور را فراموش کرده اید؟" msgstr "رمز عبور را فراموش کرده اید؟"
@@ -320,6 +328,10 @@ msgstr "به مستندات API بروید."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "به نمای All بروید" msgstr "به نمای All بروید"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "خوبی ها" msgstr "خوبی ها"
@@ -348,6 +360,7 @@ msgstr "واردات"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "در نمای بازشده، پیمایش در ورودی‌ها، آنها را به عنوان خوانده شده علامت‌گذاری می‌کند" msgstr "در نمای بازشده، پیمایش در ورودی‌ها، آنها را به عنوان خوانده شده علامت‌گذاری می‌کند"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "خوانده نشده نگه دارید" msgstr "خوانده نشده نگه دارید"
@@ -404,6 +417,10 @@ msgstr "وارد شوید"
msgid "Logout" msgid "Logout"
msgstr "خروج" msgstr "خروج"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "همه را به عنوان خوانده شده علامت گذاری ک
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "همه ورودی ها را به عنوان خوانده شده علامت گذاری کنید" msgstr "همه ورودی ها را به عنوان خوانده شده علامت گذاری کنید"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "علامت گذاری به عنوان خوانده شده" msgstr "علامت گذاری به عنوان خوانده شده"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "تا اینجا به عنوان خوانده شده علامت بزنید" msgstr "تا اینجا به عنوان خوانده شده علامت بزنید"
@@ -430,6 +449,10 @@ msgstr "تا اینجا به عنوان خوانده شده علامت بزنی
msgid "Metrics" msgid "Metrics"
msgstr "متریک" msgstr "متریک"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "صفحه را به پایین ببرید" msgstr "صفحه را به پایین ببرید"
@@ -515,6 +538,14 @@ msgstr "ورودی فعلی را در یک برگه جدید در پس زمین
msgid "Open link" msgid "Open link"
msgstr "پیوند را باز کنید" msgstr "پیوند را باز کنید"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "ورودی بعدی را باز کنید" msgstr "ورودی بعدی را باز کنید"
@@ -581,6 +612,10 @@ msgstr "تازه کردن"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "ثبت نام در این نمونه CommaFeed بسته شده است" msgstr "ثبت نام در این نمونه CommaFeed بسته شده است"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "اشتراک گذاری سایت ها"
msgid "Shift" msgid "Shift"
msgstr "شیفت" msgstr "شیفت"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "فیدها و دسته ها را بدون ورودی خوانده نشده نشان دهید" msgstr "فیدها و دسته ها را بدون ورودی خوانده نشده نشان دهید"
@@ -654,6 +697,7 @@ msgstr "اتفاق بدی افتاد..."
msgid "Space" msgid "Space"
msgstr "فضا" msgstr "فضا"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "ستاره" msgstr "ستاره"
@@ -681,6 +725,10 @@ msgstr "در فید مشترک شوید"
msgid "Success" msgid "Success"
msgstr "موفقیت" msgstr "موفقیت"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "تغییر به تم تیره" msgstr "تغییر به تم تیره"
@@ -713,6 +761,7 @@ msgstr "CommaFeed را با حساب آزمایشی امتحان کنید: دم
msgid "Unread" msgid "Unread"
msgstr "خوانده نشده" msgstr "خوانده نشده"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "" msgstr ""
@@ -743,6 +792,14 @@ msgstr "وب سایت"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "شما هنوز هیچ اشتراکی ندارید. " msgstr "شما هنوز هیچ اشتراکی ندارید. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "فایل مورد نیاز است" msgstr "فایل مورد نیاز است"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Syötteen URL-osoite"
msgid "Feed name" msgid "Feed name"
msgstr "Syötteen nimi" msgstr "Syötteen nimi"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Suodattava lauseke" msgstr "Suodattava lauseke"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Unohditko salasanan?" msgstr "Unohditko salasanan?"
@@ -320,6 +328,10 @@ msgstr "Siirry API-dokumentaatioon."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Siirry Kaikki-näkymään" msgstr "Siirry Kaikki-näkymään"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Hyvää" msgstr "Hyvää"
@@ -348,6 +360,7 @@ msgstr "Tuo"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Merkitse ne luetuiksi laajennetussa näkymässä vierittämällä merkintöjä" msgstr "Merkitse ne luetuiksi laajennetussa näkymässä vierittämällä merkintöjä"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Pidä lukematta" msgstr "Pidä lukematta"
@@ -404,6 +417,10 @@ msgstr "Kirjaudu sisään"
msgid "Logout" msgid "Logout"
msgstr "Uloskirjautuminen" msgstr "Uloskirjautuminen"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Merkitse kaikki luetuiksi"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Merkitse kaikki merkinnät luetuiksi" msgstr "Merkitse kaikki merkinnät luetuiksi"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Merkitse luetuksi" msgstr "Merkitse luetuksi"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Merkitse luetuksi tähän asti" msgstr "Merkitse luetuksi tähän asti"
@@ -430,6 +449,10 @@ msgstr "Merkitse luetuksi tähän asti"
msgid "Metrics" msgid "Metrics"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Siirrä sivua alaspäin" msgstr "Siirrä sivua alaspäin"
@@ -515,6 +538,14 @@ msgstr "Avaa nykyinen merkintä uudella välilehdellä taustalla"
msgid "Open link" msgid "Open link"
msgstr "Avaa linkki" msgstr "Avaa linkki"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Avaa seuraava merkintä" msgstr "Avaa seuraava merkintä"
@@ -581,6 +612,10 @@ msgstr "Päivitä"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu" msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Sivustojen jakaminen"
msgid "Shift" msgid "Shift"
msgstr "Vaihto" msgstr "Vaihto"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Näytä syötteet ja luokat ilman lukemattomia merkintöjä" msgstr "Näytä syötteet ja luokat ilman lukemattomia merkintöjä"
@@ -654,6 +697,7 @@ msgstr "Jotain pahaa tapahtui juuri..."
msgid "Space" msgid "Space"
msgstr "Avaruus" msgstr "Avaruus"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Tähti" msgstr "Tähti"
@@ -681,6 +725,10 @@ msgstr "Tilaa syöte"
msgid "Success" msgid "Success"
msgstr "Onnistui" msgstr "Onnistui"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Vaihda tummaan teemaan" msgstr "Vaihda tummaan teemaan"
@@ -713,6 +761,7 @@ msgstr "Kokeile CommaFeediä demotilillä: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Lukematon" msgstr "Lukematon"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Poista tähti" msgstr "Poista tähti"
@@ -743,6 +792,14 @@ msgstr "Verkkosivusto"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Sinulla ei ole vielä tilauksia. " msgstr "Sinulla ei ole vielä tilauksia. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "tiedosto vaaditaan" msgstr "tiedosto vaaditaan"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL du flux"
msgid "Feed name" msgid "Feed name"
msgstr "Nom du flux" msgstr "Nom du flux"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Expression de filtrage" msgstr "Expression de filtrage"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Mot de passe oublié ?" msgstr "Mot de passe oublié ?"
@@ -320,6 +328,10 @@ msgstr "Aller à la documentation de l'API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Aller à la catégorie Tout" msgstr "Aller à la catégorie Tout"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Extensions" msgstr "Extensions"
@@ -348,6 +360,7 @@ msgstr "Importer"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "En mode de lecture étendu, marquer les éléments comme lus lorsque la fenêtre descend." msgstr "En mode de lecture étendu, marquer les éléments comme lus lorsque la fenêtre descend."
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Garder non lu" msgstr "Garder non lu"
@@ -404,6 +417,10 @@ msgstr "Connexion"
msgid "Logout" msgid "Logout"
msgstr "Déconnexion" msgstr "Déconnexion"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Tout marquer comme lu"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marquer toutes les entrées comme lues" msgstr "Marquer toutes les entrées comme lues"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Marquer comme lu" msgstr "Marquer comme lu"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Marquer comme lu jusqu'ici" msgstr "Marquer comme lu jusqu'ici"
@@ -430,6 +449,10 @@ msgstr "Marquer comme lu jusqu'ici"
msgid "Metrics" msgid "Metrics"
msgstr "Métriques" msgstr "Métriques"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Faites défiler la page vers le bas" msgstr "Faites défiler la page vers le bas"
@@ -515,6 +538,14 @@ msgstr "Ouvrir l'entrée actuelle dans un nouvel onglet en arrière-plan"
msgid "Open link" msgid "Open link"
msgstr "Ouvrir le lien" msgstr "Ouvrir le lien"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Ouvrir l'entrée suivante" msgstr "Ouvrir l'entrée suivante"
@@ -581,6 +612,10 @@ msgstr "Rafraîchir"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed" msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Sites de partage"
msgid "Shift" msgid "Shift"
msgstr "Maj" msgstr "Maj"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Afficher les flux et les catégories pour lesquels tout est déjà lu" msgstr "Afficher les flux et les catégories pour lesquels tout est déjà lu"
@@ -654,6 +697,7 @@ msgstr "Quelque chose s'est mal passé..."
msgid "Space" msgid "Space"
msgstr "Espace" msgstr "Espace"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Ajouter aux favoris" msgstr "Ajouter aux favoris"
@@ -681,6 +725,10 @@ msgstr "S'abonner au flux"
msgid "Success" msgid "Success"
msgstr "Succès" msgstr "Succès"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr "Faire glisser le titre vers la droite"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Activer le mode sombre" msgstr "Activer le mode sombre"
@@ -713,6 +761,7 @@ msgstr "Essayez CommaFeed avec le compte de démonstration : demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Non lu" msgstr "Non lu"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Retirer des favoris" msgstr "Retirer des favoris"
@@ -743,6 +792,14 @@ msgstr "Site web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Vous n'avez pas encore d'abonnements. Pourquoi ne pas essayer d'en ajouter un en cliquant sur le signe + en haut de la page ?" msgstr "Vous n'avez pas encore d'abonnements. Pourquoi ne pas essayer d'en ajouter un en cliquant sur le signe + en haut de la page ?"
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fichier requis" msgstr "fichier requis"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL da fonte"
msgid "Feed name" msgid "Feed name"
msgstr "Nome do feed" msgstr "Nome do feed"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Expresión de filtrado" msgstr "Expresión de filtrado"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Esqueceches o contrasinal?" msgstr "Esqueceches o contrasinal?"
@@ -320,6 +328,10 @@ msgstr "Ir á documentación da API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Ir á vista Todos" msgstr "Ir á vista Todos"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "agasallos" msgstr "agasallos"
@@ -348,6 +360,7 @@ msgstr "Importación"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Na vista ampliada, ao desprazarse polas entradas márcaas como lidas" msgstr "Na vista ampliada, ao desprazarse polas entradas márcaas como lidas"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Manter sen ler" msgstr "Manter sen ler"
@@ -404,6 +417,10 @@ msgstr "Iniciar sesión"
msgid "Logout" msgid "Logout"
msgstr "Pechar sesión" msgstr "Pechar sesión"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Marcar todo como lido"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marcar todas as entradas como lidas" msgstr "Marcar todas as entradas como lidas"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Marcar como lido" msgstr "Marcar como lido"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Marcar como lido ata aquí" msgstr "Marcar como lido ata aquí"
@@ -430,6 +449,10 @@ msgstr "Marcar como lido ata aquí"
msgid "Metrics" msgid "Metrics"
msgstr "Métricas" msgstr "Métricas"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Move a páxina cara abaixo" msgstr "Move a páxina cara abaixo"
@@ -515,6 +538,14 @@ msgstr "Abre a entrada actual nunha nova pestana en segundo plano"
msgid "Open link" msgid "Open link"
msgstr "ligazón aberta" msgstr "ligazón aberta"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Abrir a seguinte entrada" msgstr "Abrir a seguinte entrada"
@@ -581,6 +612,10 @@ msgstr "Actualizar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Os rexistros están pechados nesta instancia de CommaFeed" msgstr "Os rexistros están pechados nesta instancia de CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Compartir sitios"
msgid "Shift" msgid "Shift"
msgstr "quendas" msgstr "quendas"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Mostrar fontes e categorías sen entradas sen ler" msgstr "Mostrar fontes e categorías sen entradas sen ler"
@@ -654,6 +697,7 @@ msgstr "Algo malo pasou..."
msgid "Space" msgid "Space"
msgstr "Espazo" msgstr "Espazo"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "estrela" msgstr "estrela"
@@ -681,6 +725,10 @@ msgstr "Subscríbete ao feed"
msgid "Success" msgid "Success"
msgstr "Éxito" msgstr "Éxito"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Cambiar ao tema escuro" msgstr "Cambiar ao tema escuro"
@@ -713,6 +761,7 @@ msgstr "Proba CommaFeed coa conta de demostración: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Sen ler" msgstr "Sen ler"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Desestrela" msgstr "Desestrela"
@@ -743,6 +792,14 @@ msgstr "Páxina web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Aínda non tes ningunha subscrición. " msgstr "Aínda non tes ningunha subscrición. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "é necesario o ficheiro" msgstr "é necesario o ficheiro"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr ""
msgid "Feed name" msgid "Feed name"
msgstr "Hírcsatorna neve" msgstr "Hírcsatorna neve"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Szűrő kifejezés" msgstr "Szűrő kifejezés"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Elfelejtette a jelszavát?" msgstr "Elfelejtette a jelszavát?"
@@ -320,6 +328,10 @@ msgstr "Nyissa meg az API dokumentációját."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Lépjen az Összes nézetre" msgstr "Lépjen az Összes nézetre"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Jók" msgstr "Jók"
@@ -348,6 +360,7 @@ msgstr "Importálás"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Kibontott nézetben a bejegyzések görgetése olvasottként jelöli meg őket" msgstr "Kibontott nézetben a bejegyzések görgetése olvasottként jelöli meg őket"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Olvasatlan marad" msgstr "Olvasatlan marad"
@@ -404,6 +417,10 @@ msgstr "Jelentkezzen be"
msgid "Logout" msgid "Logout"
msgstr "Kijelentkezés" msgstr "Kijelentkezés"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Minden megjelölése olvasottként"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Minden bejegyzés megjelölése olvasottként" msgstr "Minden bejegyzés megjelölése olvasottként"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Megjelölés olvasottként" msgstr "Megjelölés olvasottként"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Megjelölés idáig olvasottként" msgstr "Megjelölés idáig olvasottként"
@@ -430,6 +449,10 @@ msgstr "Megjelölés idáig olvasottként"
msgid "Metrics" msgid "Metrics"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Mozgassa le az oldalt" msgstr "Mozgassa le az oldalt"
@@ -515,6 +538,14 @@ msgstr "Az aktuális bejegyzés megnyitása egy új lapon a háttérben"
msgid "Open link" msgid "Open link"
msgstr "Link megnyitása" msgstr "Link megnyitása"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Következő bejegyzés megnyitása" msgstr "Következő bejegyzés megnyitása"
@@ -581,6 +612,10 @@ msgstr "Frissítés"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon" msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Webhelyek megosztása"
msgid "Shift" msgid "Shift"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Hírcsatornák és kategóriák megjelenítése olvasatlan bejegyzések nélkül" msgstr "Hírcsatornák és kategóriák megjelenítése olvasatlan bejegyzések nélkül"
@@ -654,6 +697,7 @@ msgstr "Valami rossz történt..."
msgid "Space" msgid "Space"
msgstr "" msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Csillag" msgstr "Csillag"
@@ -681,6 +725,10 @@ msgstr "Feliratkozás a hírfolyamra"
msgid "Success" msgid "Success"
msgstr "Siker" msgstr "Siker"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Váltás sötét témára" msgstr "Váltás sötét témára"
@@ -713,6 +761,7 @@ msgstr "Próbálja ki a CommaFeed-et a demo fiókkal: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Olvasatlan" msgstr "Olvasatlan"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "" msgstr ""
@@ -743,6 +792,14 @@ msgstr "Webhely"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Még nincs előfizetése. " msgstr "Még nincs előfizetése. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fájl szükséges" msgstr "fájl szükséges"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL Umpan"
msgid "Feed name" msgid "Feed name"
msgstr "Nama umpan" msgstr "Nama umpan"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Memfilter ekspresi" msgstr "Memfilter ekspresi"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Lupa kata sandi?" msgstr "Lupa kata sandi?"
@@ -320,6 +328,10 @@ msgstr "Buka dokumentasi API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Pergi ke tampilan Semua" msgstr "Pergi ke tampilan Semua"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Pernak-pernik" msgstr "Pernak-pernik"
@@ -348,6 +360,7 @@ msgstr "Impor"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Dalam tampilan yang diperluas, menggulir entri menandainya sebagai telah dibaca" msgstr "Dalam tampilan yang diperluas, menggulir entri menandainya sebagai telah dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Tetap belum dibaca" msgstr "Tetap belum dibaca"
@@ -404,6 +417,10 @@ msgstr "Masuk"
msgid "Logout" msgid "Logout"
msgstr "Keluar" msgstr "Keluar"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Tandai semua sebagai telah dibaca"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Tandai semua entri sebagai telah dibaca" msgstr "Tandai semua entri sebagai telah dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Tandai sebagai telah dibaca" msgstr "Tandai sebagai telah dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Tandai sebagai telah dibaca sampai di sini" msgstr "Tandai sebagai telah dibaca sampai di sini"
@@ -430,6 +449,10 @@ msgstr "Tandai sebagai telah dibaca sampai di sini"
msgid "Metrics" msgid "Metrics"
msgstr "Metrik" msgstr "Metrik"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Pindahkan halaman ke bawah" msgstr "Pindahkan halaman ke bawah"
@@ -515,6 +538,14 @@ msgstr "Buka entri saat ini di tab baru di latar belakang"
msgid "Open link" msgid "Open link"
msgstr "Buka tautan" msgstr "Buka tautan"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Buka entri berikutnya" msgstr "Buka entri berikutnya"
@@ -581,6 +612,10 @@ msgstr "Segarkan"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Pendaftaran ditutup pada instans CommaFeed ini" msgstr "Pendaftaran ditutup pada instans CommaFeed ini"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Berbagi situs"
msgid "Shift" msgid "Shift"
msgstr "Pergeseran" msgstr "Pergeseran"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Tampilkan umpan dan kategori tanpa entri yang belum dibaca" msgstr "Tampilkan umpan dan kategori tanpa entri yang belum dibaca"
@@ -654,6 +697,7 @@ msgstr "Sesuatu yang buruk baru saja terjadi..."
msgid "Space" msgid "Space"
msgstr "Luar Angkasa" msgstr "Luar Angkasa"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Bintang" msgstr "Bintang"
@@ -681,6 +725,10 @@ msgstr "Berlangganan umpan"
msgid "Success" msgid "Success"
msgstr "Sukses" msgstr "Sukses"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Beralih ke tema gelap" msgstr "Beralih ke tema gelap"
@@ -713,6 +761,7 @@ msgstr "Cobalah CommaFeed dengan akun demo: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Belum Dibaca" msgstr "Belum Dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Hapus bintang" msgstr "Hapus bintang"
@@ -743,6 +792,14 @@ msgstr "Situs Web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Anda belum memiliki langganan. " msgstr "Anda belum memiliki langganan. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "file diperlukan" msgstr "file diperlukan"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL feed"
msgid "Feed name" msgid "Feed name"
msgstr "Nome del feed" msgstr "Nome del feed"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Espressione filtrante" msgstr "Espressione filtrante"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Password dimenticata?" msgstr "Password dimenticata?"
@@ -320,6 +328,10 @@ msgstr "Vai alla documentazione dell'API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Vai alla vista Tutto" msgstr "Vai alla vista Tutto"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Chicche" msgstr "Chicche"
@@ -348,6 +360,7 @@ msgstr "Importa"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Nella vista espansa, scorrendo le voci contrassegnale come lette" msgstr "Nella vista espansa, scorrendo le voci contrassegnale come lette"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Mantieni non letto" msgstr "Mantieni non letto"
@@ -404,6 +417,10 @@ msgstr "Accedi"
msgid "Logout" msgid "Logout"
msgstr "Disconnessione" msgstr "Disconnessione"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Contrassegna tutto come letto"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Contrassegna tutte le voci come lette" msgstr "Contrassegna tutte le voci come lette"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Contrassegna come letto" msgstr "Contrassegna come letto"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Contrassegna come letto fino a qui" msgstr "Contrassegna come letto fino a qui"
@@ -430,6 +449,10 @@ msgstr "Contrassegna come letto fino a qui"
msgid "Metrics" msgid "Metrics"
msgstr "Metriche" msgstr "Metriche"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Sposta la pagina in basso" msgstr "Sposta la pagina in basso"
@@ -515,6 +538,14 @@ msgstr "Apri la voce corrente in una nuova scheda in background"
msgid "Open link" msgid "Open link"
msgstr "Apri collegamento" msgstr "Apri collegamento"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Apri voce successiva" msgstr "Apri voce successiva"
@@ -581,6 +612,10 @@ msgstr "Aggiorna"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed" msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Condivisione di siti"
msgid "Shift" msgid "Shift"
msgstr "Cambio" msgstr "Cambio"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Mostra feed e categorie senza voci non lette" msgstr "Mostra feed e categorie senza voci non lette"
@@ -654,6 +697,7 @@ msgstr "È appena successo qualcosa di brutto..."
msgid "Space" msgid "Space"
msgstr "Spazio" msgstr "Spazio"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Stella" msgstr "Stella"
@@ -681,6 +725,10 @@ msgstr "Iscriviti al feed"
msgid "Success" msgid "Success"
msgstr "Successo" msgstr "Successo"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Passa al tema scuro" msgstr "Passa al tema scuro"
@@ -713,6 +761,7 @@ msgstr "Prova CommaFeed con il conto demo: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Non letto" msgstr "Non letto"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Elimina le stelle" msgstr "Elimina le stelle"
@@ -743,6 +792,14 @@ msgstr "Sito web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Non hai ancora abbonamenti. " msgstr "Non hai ancora abbonamenti. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "è richiesto il file" msgstr "è richiesto il file"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "フィード URL"
msgid "Feed name" msgid "Feed name"
msgstr "フィード名" msgstr "フィード名"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "フィルタリング式" msgstr "フィルタリング式"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "パスワードをお忘れですか?" msgstr "パスワードをお忘れですか?"
@@ -320,6 +328,10 @@ msgstr "API ドキュメントに移動します。"
msgid "Go to the All view" msgid "Go to the All view"
msgstr "すべてのビューに移動" msgstr "すべてのビューに移動"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "グッディーズ" msgstr "グッディーズ"
@@ -348,6 +360,7 @@ msgstr "インポート"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "展開ビューでエントリをスクロールすると、それらが既読としてマークされます" msgstr "展開ビューでエントリをスクロールすると、それらが既読としてマークされます"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "未読のままにする" msgstr "未読のままにする"
@@ -404,6 +417,10 @@ msgstr "ログイン"
msgid "Logout" msgid "Logout"
msgstr "ログアウト" msgstr "ログアウト"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "すべて既読にする"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "すべてのエントリを既読にする" msgstr "すべてのエントリを既読にする"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "既読にする" msgstr "既読にする"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "ここまで既読にする" msgstr "ここまで既読にする"
@@ -430,6 +449,10 @@ msgstr "ここまで既読にする"
msgid "Metrics" msgid "Metrics"
msgstr "メトリクス" msgstr "メトリクス"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "ページを下に移動" msgstr "ページを下に移動"
@@ -515,6 +538,14 @@ msgstr "現在のエントリをバックグラウンドで新しいタブで開
msgid "Open link" msgid "Open link"
msgstr "リンクを開く" msgstr "リンクを開く"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "次のエントリを開く" msgstr "次のエントリを開く"
@@ -581,6 +612,10 @@ msgstr "リフレッシュ"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "この CommaFeed インスタンスの登録は終了しています" msgstr "この CommaFeed インスタンスの登録は終了しています"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "共有サイト"
msgid "Shift" msgid "Shift"
msgstr "シフト" msgstr "シフト"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "未読エントリのないフィードとカテゴリを表示する" msgstr "未読エントリのないフィードとカテゴリを表示する"
@@ -654,6 +697,7 @@ msgstr "何か悪いことが起こった..."
msgid "Space" msgid "Space"
msgstr "スペース" msgstr "スペース"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "スター" msgstr "スター"
@@ -681,6 +725,10 @@ msgstr "フィードを購読する"
msgid "Success" msgid "Success"
msgstr "成功" msgstr "成功"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "ダークテーマに切り替え" msgstr "ダークテーマに切り替え"
@@ -713,6 +761,7 @@ msgstr "デモアカウントで CommaFeed を試す: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "未読" msgstr "未読"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "スターを外す" msgstr "スターを外す"
@@ -743,6 +792,14 @@ msgstr "ウェブサイト"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "まだサブスクリプションがありません。" msgstr "まだサブスクリプションがありません。"
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "ファイルが必要です" msgstr "ファイルが必要です"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "피드 URL"
msgid "Feed name" msgid "Feed name"
msgstr "피드 이름" msgstr "피드 이름"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "필터링 표현식" msgstr "필터링 표현식"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "비밀번호를 잊으셨나요?" msgstr "비밀번호를 잊으셨나요?"
@@ -320,6 +328,10 @@ msgstr "API 문서로 이동합니다."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "전체 보기로 이동" msgstr "전체 보기로 이동"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "굿즈" msgstr "굿즈"
@@ -348,6 +360,7 @@ msgstr "가져오기"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "확장 보기에서 항목을 스크롤하면 읽은 것으로 표시됩니다." msgstr "확장 보기에서 항목을 스크롤하면 읽은 것으로 표시됩니다."
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "읽지 않은 상태로 유지" msgstr "읽지 않은 상태로 유지"
@@ -404,6 +417,10 @@ msgstr "로그인"
msgid "Logout" msgid "Logout"
msgstr "로그아웃" msgstr "로그아웃"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "모두 읽은 상태로 표시"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "모든 항목을 읽은 상태로 표시" msgstr "모든 항목을 읽은 상태로 표시"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "읽은 상태로 표시" msgstr "읽은 상태로 표시"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "여기까지 읽은 것으로 표시" msgstr "여기까지 읽은 것으로 표시"
@@ -430,6 +449,10 @@ msgstr "여기까지 읽은 것으로 표시"
msgid "Metrics" msgid "Metrics"
msgstr "메트릭스" msgstr "메트릭스"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "페이지를 아래로 이동" msgstr "페이지를 아래로 이동"
@@ -515,6 +538,14 @@ msgstr "배경의 새 탭에서 현재 항목 열기"
msgid "Open link" msgid "Open link"
msgstr "링크 열기" msgstr "링크 열기"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "다음 항목 열기" msgstr "다음 항목 열기"
@@ -581,6 +612,10 @@ msgstr "새로 고침"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다." msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다."
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "사이트 공유"
msgid "Shift" msgid "Shift"
msgstr "시프트" msgstr "시프트"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "읽지 않은 항목이 없는 피드 및 카테고리 표시" msgstr "읽지 않은 항목이 없는 피드 및 카테고리 표시"
@@ -654,6 +697,7 @@ msgstr "뭔가 안 좋은 일이 일어났어..."
msgid "Space" msgid "Space"
msgstr "우주" msgstr "우주"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "스타" msgstr "스타"
@@ -681,6 +725,10 @@ msgstr "피드 구독"
msgid "Success" msgid "Success"
msgstr "성공" msgstr "성공"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "어두운 테마로 전환" msgstr "어두운 테마로 전환"
@@ -713,6 +761,7 @@ msgstr "데모 계정으로 CommaFeed를 사용해 보세요: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "읽지 않음" msgstr "읽지 않음"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "별표 제거" msgstr "별표 제거"
@@ -743,6 +792,14 @@ msgstr "웹사이트"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "아직 구독이 없습니다. " msgstr "아직 구독이 없습니다. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "파일이 필요합니다" msgstr "파일이 필요합니다"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL Suapan"
msgid "Feed name" msgid "Feed name"
msgstr "Nama suapan" msgstr "Nama suapan"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Ungkapan penapisan" msgstr "Ungkapan penapisan"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Lupa kata laluan?" msgstr "Lupa kata laluan?"
@@ -320,6 +328,10 @@ msgstr "Pergi ke dokumentasi API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Pergi ke paparan Semua" msgstr "Pergi ke paparan Semua"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "" msgstr ""
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Dalam paparan yang diperluas, menatal melalui entri menandakannya sebagai dibaca" msgstr "Dalam paparan yang diperluas, menatal melalui entri menandakannya sebagai dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Teruskan tidak dibaca" msgstr "Teruskan tidak dibaca"
@@ -404,6 +417,10 @@ msgstr "Log masuk"
msgid "Logout" msgid "Logout"
msgstr "Log Keluar" msgstr "Log Keluar"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Tandai semua sebagai dibaca"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Tandai semua entri sebagai dibaca" msgstr "Tandai semua entri sebagai dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Tandakan sebagai dibaca" msgstr "Tandakan sebagai dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Tandai sebagai dibaca sehingga di sini" msgstr "Tandai sebagai dibaca sehingga di sini"
@@ -430,6 +449,10 @@ msgstr "Tandai sebagai dibaca sehingga di sini"
msgid "Metrics" msgid "Metrics"
msgstr "Metrik" msgstr "Metrik"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Gerakkan halaman ke bawah" msgstr "Gerakkan halaman ke bawah"
@@ -515,6 +538,14 @@ msgstr "Buka entri semasa dalam tab baharu di latar belakang"
msgid "Open link" msgid "Open link"
msgstr "Buka pautan" msgstr "Buka pautan"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Buka entri seterusnya" msgstr "Buka entri seterusnya"
@@ -581,6 +612,10 @@ msgstr "Muat semula"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Pendaftaran ditutup pada contoh CommaFeed ini" msgstr "Pendaftaran ditutup pada contoh CommaFeed ini"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Berkongsi tapak"
msgid "Shift" msgid "Shift"
msgstr "Anjakan" msgstr "Anjakan"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Tunjukkan suapan dan kategori tanpa entri yang belum dibaca" msgstr "Tunjukkan suapan dan kategori tanpa entri yang belum dibaca"
@@ -654,6 +697,7 @@ msgstr "Sesuatu yang buruk baru saja berlaku..."
msgid "Space" msgid "Space"
msgstr "Angkasa" msgstr "Angkasa"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Bintang" msgstr "Bintang"
@@ -681,6 +725,10 @@ msgstr "Langgan suapan"
msgid "Success" msgid "Success"
msgstr "Kejayaan" msgstr "Kejayaan"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Tukar kepada tema gelap" msgstr "Tukar kepada tema gelap"
@@ -713,6 +761,7 @@ msgstr "Cuba CommaFeed dengan akaun demo: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Belum dibaca" msgstr "Belum dibaca"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Nyahbintang" msgstr "Nyahbintang"
@@ -743,6 +792,14 @@ msgstr "Laman web"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Anda belum mempunyai sebarang langganan lagi. " msgstr "Anda belum mempunyai sebarang langganan lagi. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fail diperlukan" msgstr "fail diperlukan"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Feed-URL"
msgid "Feed name" msgid "Feed name"
msgstr "Feednavn" msgstr "Feednavn"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrerende uttrykk" msgstr "Filtrerende uttrykk"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Glemt passord?" msgstr "Glemt passord?"
@@ -320,6 +328,10 @@ msgstr "Gå til API-dokumentasjonen."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Gå til visningen Alle" msgstr "Gå til visningen Alle"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Godbiter" msgstr "Godbiter"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "I utvidet visning merker du dem som lest ved å rulle gjennom oppføringer" msgstr "I utvidet visning merker du dem som lest ved å rulle gjennom oppføringer"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Behold ulest" msgstr "Behold ulest"
@@ -404,6 +417,10 @@ msgstr "Logg inn"
msgid "Logout" msgid "Logout"
msgstr "Logg ut" msgstr "Logg ut"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Merk alle som lest"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Merk alle oppføringer som lest" msgstr "Merk alle oppføringer som lest"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Merk som lest" msgstr "Merk som lest"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Merk som lest frem til her" msgstr "Merk som lest frem til her"
@@ -430,6 +449,10 @@ msgstr "Merk som lest frem til her"
msgid "Metrics" msgid "Metrics"
msgstr "Beregninger" msgstr "Beregninger"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Flytt siden ned" msgstr "Flytt siden ned"
@@ -515,6 +538,14 @@ msgstr "Åpne gjeldende oppføring i en ny fane i bakgrunnen"
msgid "Open link" msgid "Open link"
msgstr "Åpen lenke" msgstr "Åpen lenke"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Åpne neste oppføring" msgstr "Åpne neste oppføring"
@@ -581,6 +612,10 @@ msgstr "Oppdater"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten" msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Delingssider"
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Vis feeder og kategorier uten uleste oppføringer" msgstr "Vis feeder og kategorier uten uleste oppføringer"
@@ -654,6 +697,7 @@ msgstr "Noe ille skjedde akkurat..."
msgid "Space" msgid "Space"
msgstr "" msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Stjerne" msgstr "Stjerne"
@@ -681,6 +725,10 @@ msgstr "Abonner på feeden"
msgid "Success" msgid "Success"
msgstr "Suksess" msgstr "Suksess"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Bytt til mørkt tema" msgstr "Bytt til mørkt tema"
@@ -713,6 +761,7 @@ msgstr "Prøv CommaFeed med demokontoen: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Ulest" msgstr "Ulest"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Fjern stjerne" msgstr "Fjern stjerne"
@@ -743,6 +792,14 @@ msgstr "Nettsted"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Du har ingen abonnementer ennå. " msgstr "Du har ingen abonnementer ennå. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fil kreves" msgstr "fil kreves"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Feed-URL"
msgid "Feed name" msgid "Feed name"
msgstr "Feednaam" msgstr "Feednaam"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Uitdrukking filteren" msgstr "Uitdrukking filteren"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Wachtwoord vergeten?" msgstr "Wachtwoord vergeten?"
@@ -320,6 +328,10 @@ msgstr "Ga naar de API-documentatie."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Ga naar de weergave Alles" msgstr "Ga naar de weergave Alles"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Goederen" msgstr "Goederen"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "In de uitgevouwen weergave markeert het scrollen door items ze als gelezen" msgstr "In de uitgevouwen weergave markeert het scrollen door items ze als gelezen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Ongelezen houden" msgstr "Ongelezen houden"
@@ -404,6 +417,10 @@ msgstr "Inloggen"
msgid "Logout" msgid "Logout"
msgstr "Uitloggen" msgstr "Uitloggen"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Alles markeren als gelezen"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Markeer alle vermeldingen als gelezen" msgstr "Markeer alle vermeldingen als gelezen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Markeren als gelezen" msgstr "Markeren als gelezen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Markeer als gelezen tot hier" msgstr "Markeer als gelezen tot hier"
@@ -430,6 +449,10 @@ msgstr "Markeer als gelezen tot hier"
msgid "Metrics" msgid "Metrics"
msgstr "Metrieken" msgstr "Metrieken"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Verplaats de pagina naar beneden" msgstr "Verplaats de pagina naar beneden"
@@ -515,6 +538,14 @@ msgstr "Open huidig item in een nieuw tabblad op de achtergrond"
msgid "Open link" msgid "Open link"
msgstr "Link openen" msgstr "Link openen"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Volgende invoer openen" msgstr "Volgende invoer openen"
@@ -581,6 +612,10 @@ msgstr "Vernieuwen"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registraties zijn gesloten op deze CommaFeed-instantie" msgstr "Registraties zijn gesloten op deze CommaFeed-instantie"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Sites delen"
msgid "Shift" msgid "Shift"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Toon feeds en categorieën zonder ongelezen items" msgstr "Toon feeds en categorieën zonder ongelezen items"
@@ -654,6 +697,7 @@ msgstr "Er is net iets ergs gebeurd..."
msgid "Space" msgid "Space"
msgstr "Ruimte" msgstr "Ruimte"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Ster" msgstr "Ster"
@@ -681,6 +725,10 @@ msgstr "Abonneer je op de feed"
msgid "Success" msgid "Success"
msgstr "Succes" msgstr "Succes"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Overschakelen naar donker thema" msgstr "Overschakelen naar donker thema"
@@ -713,6 +761,7 @@ msgstr "Probeer CommaFeed uit met het demo-account: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Ongelezen" msgstr "Ongelezen"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Sterren uit" msgstr "Sterren uit"
@@ -743,6 +792,14 @@ msgstr ""
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Je hebt nog geen abonnementen. " msgstr "Je hebt nog geen abonnementen. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "bestand is vereist" msgstr "bestand is vereist"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Feed-URL"
msgid "Feed name" msgid "Feed name"
msgstr "Feednavn" msgstr "Feednavn"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrerende uttrykk" msgstr "Filtrerende uttrykk"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Glemt passord?" msgstr "Glemt passord?"
@@ -320,6 +328,10 @@ msgstr "Gå til API-dokumentasjonen."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Gå til visningen Alle" msgstr "Gå til visningen Alle"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Godbiter" msgstr "Godbiter"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "I utvidet visning merker du dem som lest ved å rulle gjennom oppføringer" msgstr "I utvidet visning merker du dem som lest ved å rulle gjennom oppføringer"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Behold ulest" msgstr "Behold ulest"
@@ -404,6 +417,10 @@ msgstr "Logg inn"
msgid "Logout" msgid "Logout"
msgstr "Logg ut" msgstr "Logg ut"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Merk alle som lest"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Merk alle oppføringer som lest" msgstr "Merk alle oppføringer som lest"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Merk som lest" msgstr "Merk som lest"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Merk som lest frem til her" msgstr "Merk som lest frem til her"
@@ -430,6 +449,10 @@ msgstr "Merk som lest frem til her"
msgid "Metrics" msgid "Metrics"
msgstr "Beregninger" msgstr "Beregninger"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Flytt siden ned" msgstr "Flytt siden ned"
@@ -515,6 +538,14 @@ msgstr "Åpne gjeldende oppføring i en ny fane i bakgrunnen"
msgid "Open link" msgid "Open link"
msgstr "Åpen lenke" msgstr "Åpen lenke"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Åpne neste oppføring" msgstr "Åpne neste oppføring"
@@ -581,6 +612,10 @@ msgstr "Oppdater"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten" msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Delingssider"
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Vis feeder og kategorier uten uleste oppføringer" msgstr "Vis feeder og kategorier uten uleste oppføringer"
@@ -654,6 +697,7 @@ msgstr "Noe ille skjedde akkurat..."
msgid "Space" msgid "Space"
msgstr "" msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Stjerne" msgstr "Stjerne"
@@ -681,6 +725,10 @@ msgstr "Abonner på feeden"
msgid "Success" msgid "Success"
msgstr "Suksess" msgstr "Suksess"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Bytt til mørkt tema" msgstr "Bytt til mørkt tema"
@@ -713,6 +761,7 @@ msgstr "Prøv CommaFeed med demokontoen: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Ulest" msgstr "Ulest"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Fjern stjerne" msgstr "Fjern stjerne"
@@ -743,6 +792,14 @@ msgstr "Nettsted"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Du har ingen abonnementer ennå. " msgstr "Du har ingen abonnementer ennå. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fil kreves" msgstr "fil kreves"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL kanału"
msgid "Feed name" msgid "Feed name"
msgstr "nazwa kanału" msgstr "nazwa kanału"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Wyrażenie filtrujące" msgstr "Wyrażenie filtrujące"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Zapomniałeś hasła?" msgstr "Zapomniałeś hasła?"
@@ -320,6 +328,10 @@ msgstr "Przejdź do dokumentacji API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Przejdź do widoku Wszystkie" msgstr "Przejdź do widoku Wszystkie"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Gadżety" msgstr "Gadżety"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "W widoku rozszerzonym przewijanie wpisów oznacza je jako przeczytane" msgstr "W widoku rozszerzonym przewijanie wpisów oznacza je jako przeczytane"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Nie przeczytaj" msgstr "Nie przeczytaj"
@@ -404,6 +417,10 @@ msgstr "Zaloguj się"
msgid "Logout" msgid "Logout"
msgstr "Wyloguj" msgstr "Wyloguj"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Oznacz wszystko jako przeczytane"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Oznacz wszystkie wpisy jako przeczytane" msgstr "Oznacz wszystkie wpisy jako przeczytane"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Oznacz jako przeczytane" msgstr "Oznacz jako przeczytane"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Oznacz jako przeczytane do tej pory" msgstr "Oznacz jako przeczytane do tej pory"
@@ -430,6 +449,10 @@ msgstr "Oznacz jako przeczytane do tej pory"
msgid "Metrics" msgid "Metrics"
msgstr "Metryki" msgstr "Metryki"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Przesuń stronę w dół" msgstr "Przesuń stronę w dół"
@@ -515,6 +538,14 @@ msgstr "Otwórz bieżący wpis w nowej karcie w tle"
msgid "Open link" msgid "Open link"
msgstr "Otwórz link" msgstr "Otwórz link"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Otwórz następny wpis" msgstr "Otwórz następny wpis"
@@ -581,6 +612,10 @@ msgstr "Odśwież"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Rejestracje są zamknięte w tej instancji CommaFeed" msgstr "Rejestracje są zamknięte w tej instancji CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Udostępnianie witryn"
msgid "Shift" msgid "Shift"
msgstr "zmiana" msgstr "zmiana"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Pokaż kanały i kategorie bez nieprzeczytanych wpisów" msgstr "Pokaż kanały i kategorie bez nieprzeczytanych wpisów"
@@ -654,6 +697,7 @@ msgstr "Coś złego właśnie się stało..."
msgid "Space" msgid "Space"
msgstr "Przestrzeń" msgstr "Przestrzeń"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Gwiazda" msgstr "Gwiazda"
@@ -681,6 +725,10 @@ msgstr "Subskrybuj kanał"
msgid "Success" msgid "Success"
msgstr "Sukces" msgstr "Sukces"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Przełącz na ciemny motyw" msgstr "Przełącz na ciemny motyw"
@@ -713,6 +761,7 @@ msgstr "Wypróbuj CommaFeed z kontem demo: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Nieprzeczytane" msgstr "Nieprzeczytane"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "" msgstr ""
@@ -743,6 +792,14 @@ msgstr "Strona internetowa"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Nie masz jeszcze żadnych subskrypcji. " msgstr "Nie masz jeszcze żadnych subskrypcji. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "plik jest wymagany" msgstr "plik jest wymagany"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL do feed"
msgid "Feed name" msgid "Feed name"
msgstr "Nome do feed" msgstr "Nome do feed"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrando expressão" msgstr "Filtrando expressão"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Esqueceu a senha?" msgstr "Esqueceu a senha?"
@@ -320,6 +328,10 @@ msgstr "Vá para a documentação da API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Ir para a visualização Tudo" msgstr "Ir para a visualização Tudo"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Brindes" msgstr "Brindes"
@@ -348,6 +360,7 @@ msgstr "Importar"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Na visualização expandida, rolar pelas entradas marca-as como lidas" msgstr "Na visualização expandida, rolar pelas entradas marca-as como lidas"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Manter não lido" msgstr "Manter não lido"
@@ -404,6 +417,10 @@ msgstr "Entrar"
msgid "Logout" msgid "Logout"
msgstr "Sair" msgstr "Sair"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Marcar todos como lidos"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Marcar todas as entradas como lidas" msgstr "Marcar todas as entradas como lidas"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Marcar como lido" msgstr "Marcar como lido"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Marcar como lido até aqui" msgstr "Marcar como lido até aqui"
@@ -430,6 +449,10 @@ msgstr "Marcar como lido até aqui"
msgid "Metrics" msgid "Metrics"
msgstr "Métricas" msgstr "Métricas"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Mova a página para baixo" msgstr "Mova a página para baixo"
@@ -515,6 +538,14 @@ msgstr "Abrir a entrada atual em uma nova aba em segundo plano"
msgid "Open link" msgid "Open link"
msgstr "Abrir link" msgstr "Abrir link"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Abrir próxima entrada" msgstr "Abrir próxima entrada"
@@ -581,6 +612,10 @@ msgstr "Atualizar"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Os registros estão fechados nesta instância do CommaFeed" msgstr "Os registros estão fechados nesta instância do CommaFeed"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Compartilhando sites"
msgid "Shift" msgid "Shift"
msgstr "Mudar" msgstr "Mudar"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Mostrar feeds e categorias sem entradas não lidas" msgstr "Mostrar feeds e categorias sem entradas não lidas"
@@ -654,6 +697,7 @@ msgstr "Algo ruim acabou de acontecer..."
msgid "Space" msgid "Space"
msgstr "Espaço" msgstr "Espaço"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Estrela" msgstr "Estrela"
@@ -681,6 +725,10 @@ msgstr "Inscrever-se no feed"
msgid "Success" msgid "Success"
msgstr "Sucesso" msgstr "Sucesso"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Mudar para tema escuro" msgstr "Mudar para tema escuro"
@@ -713,6 +761,7 @@ msgstr "Experimente o CommaFeed com a conta demo: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Não lido" msgstr "Não lido"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Desestrelar" msgstr "Desestrelar"
@@ -743,6 +792,14 @@ msgstr "Site"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Você ainda não tem nenhuma assinatura. " msgstr "Você ainda não tem nenhuma assinatura. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "o arquivo é obrigatório" msgstr "o arquivo é obrigatório"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL-адрес фида"
msgid "Feed name" msgid "Feed name"
msgstr "Имя фида" msgstr "Имя фида"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Выражение фильтрации" msgstr "Выражение фильтрации"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Забыли пароль?" msgstr "Забыли пароль?"
@@ -320,6 +328,10 @@ msgstr "Перейдите к документации по API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Перейти к представлению «Все»" msgstr "Перейти к представлению «Все»"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Сладости" msgstr "Сладости"
@@ -348,6 +360,7 @@ msgstr "Импорт"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "В развернутом виде прокрутка записей помечает их как прочитанные." msgstr "В развернутом виде прокрутка записей помечает их как прочитанные."
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Не читать" msgstr "Не читать"
@@ -404,6 +417,10 @@ msgstr "Войти"
msgid "Logout" msgid "Logout"
msgstr "Выйти" msgstr "Выйти"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Отметить все как прочитанное"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Отметить все записи как прочитанные" msgstr "Отметить все записи как прочитанные"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Отметить как прочитанное" msgstr "Отметить как прочитанное"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Отметить как прочитанное до этого места" msgstr "Отметить как прочитанное до этого места"
@@ -430,6 +449,10 @@ msgstr "Отметить как прочитанное до этого мест
msgid "Metrics" msgid "Metrics"
msgstr "Метрики" msgstr "Метрики"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Переместить страницу вниз" msgstr "Переместить страницу вниз"
@@ -515,6 +538,14 @@ msgstr "Открыть текущую запись в новой вкладке
msgid "Open link" msgid "Open link"
msgstr "Открыть ссылку" msgstr "Открыть ссылку"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Открыть следующую запись" msgstr "Открыть следующую запись"
@@ -581,6 +612,10 @@ msgstr "Обновить"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Регистрация закрыта для этого экземпляра CommaFeed." msgstr "Регистрация закрыта для этого экземпляра CommaFeed."
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Обмен сайтами"
msgid "Shift" msgid "Shift"
msgstr "Сдвиг" msgstr "Сдвиг"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Показать каналы и категории без непрочитанных записей" msgstr "Показать каналы и категории без непрочитанных записей"
@@ -654,6 +697,7 @@ msgstr "Только что случилось что-то плохое..."
msgid "Space" msgid "Space"
msgstr "Пробел" msgstr "Пробел"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Звезда" msgstr "Звезда"
@@ -681,6 +725,10 @@ msgstr "Подписаться на ленту"
msgid "Success" msgid "Success"
msgstr "Успех" msgstr "Успех"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Переключиться на темную тему" msgstr "Переключиться на темную тему"
@@ -713,6 +761,7 @@ msgstr "Попробуйте CommaFeed на демо-счете: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "непрочитано" msgstr "непрочитано"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Снять пометку" msgstr "Снять пометку"
@@ -743,6 +792,14 @@ msgstr "Веб-сайт"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "У вас пока нет подписок. " msgstr "У вас пока нет подписок. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "требуется файл" msgstr "требуется файл"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "URL informačného kanála"
msgid "Feed name" msgid "Feed name"
msgstr "Názov informačného kanála" msgstr "Názov informačného kanála"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrovanie výrazu" msgstr "Filtrovanie výrazu"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Zabudli ste heslo?" msgstr "Zabudli ste heslo?"
@@ -320,6 +328,10 @@ msgstr "Prejdite na dokumentáciu API."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Prejdite na zobrazenie Všetky" msgstr "Prejdite na zobrazenie Všetky"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Dobrôtky" msgstr "Dobrôtky"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "V rozšírenom zobrazení ich rolovanie cez položky označí ako prečítané" msgstr "V rozšírenom zobrazení ich rolovanie cez položky označí ako prečítané"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Ponechať neprečítané" msgstr "Ponechať neprečítané"
@@ -404,6 +417,10 @@ msgstr "Prihláste sa"
msgid "Logout" msgid "Logout"
msgstr "Odhlásenie" msgstr "Odhlásenie"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Označiť všetko ako prečítané"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Označte všetky položky ako prečítané" msgstr "Označte všetky položky ako prečítané"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Označiť ako prečítané" msgstr "Označiť ako prečítané"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Označiť ako prečítané až sem" msgstr "Označiť ako prečítané až sem"
@@ -430,6 +449,10 @@ msgstr "Označiť ako prečítané až sem"
msgid "Metrics" msgid "Metrics"
msgstr "Metriky" msgstr "Metriky"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Posuňte stránku nadol" msgstr "Posuňte stránku nadol"
@@ -515,6 +538,14 @@ msgstr "Otvorte aktuálny záznam na novej karte na pozadí"
msgid "Open link" msgid "Open link"
msgstr "Otvoriť odkaz" msgstr "Otvoriť odkaz"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Otvor ďalší záznam" msgstr "Otvor ďalší záznam"
@@ -581,6 +612,10 @@ msgstr "Obnoviť"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté" msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Zdieľanie stránok"
msgid "Shift" msgid "Shift"
msgstr "Smena" msgstr "Smena"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Zobraziť kanály a kategórie bez neprečítaných záznamov" msgstr "Zobraziť kanály a kategórie bez neprečítaných záznamov"
@@ -654,6 +697,7 @@ msgstr "Práve sa stalo niečo zlé..."
msgid "Space" msgid "Space"
msgstr "Vesmír" msgstr "Vesmír"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Hviezda" msgstr "Hviezda"
@@ -681,6 +725,10 @@ msgstr "Prihláste sa na odber kanála"
msgid "Success" msgid "Success"
msgstr "Úspech" msgstr "Úspech"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Prepnúť na tmavú tému" msgstr "Prepnúť na tmavú tému"
@@ -713,6 +761,7 @@ msgstr "Vyskúšajte CommaFeed s demo účtom: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Neprečítané" msgstr "Neprečítané"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Odobrať hviezdičku" msgstr "Odobrať hviezdičku"
@@ -743,6 +792,14 @@ msgstr "Webová stránka"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Zatiaľ nemáte žiadne odbery. " msgstr "Zatiaľ nemáte žiadne odbery. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "" msgstr ""
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Flödes-URL"
msgid "Feed name" msgid "Feed name"
msgstr "Flödesnamn" msgstr "Flödesnamn"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtrerande uttryck" msgstr "Filtrerande uttryck"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Glömt lösenord?" msgstr "Glömt lösenord?"
@@ -320,6 +328,10 @@ msgstr "Gå till API-dokumentationen."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Gå till vyn Alla" msgstr "Gå till vyn Alla"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "Godsaker" msgstr "Godsaker"
@@ -348,6 +360,7 @@ msgstr ""
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "I utökad vy, rullning genom poster markerar dem som lästa" msgstr "I utökad vy, rullning genom poster markerar dem som lästa"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Behåll oläst" msgstr "Behåll oläst"
@@ -404,6 +417,10 @@ msgstr "Logga in"
msgid "Logout" msgid "Logout"
msgstr "Logga ut" msgstr "Logga ut"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Markera alla som lästa"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Markera alla poster som lästa" msgstr "Markera alla poster som lästa"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Markera som läst" msgstr "Markera som läst"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Markera som läst hit" msgstr "Markera som läst hit"
@@ -430,6 +449,10 @@ msgstr "Markera som läst hit"
msgid "Metrics" msgid "Metrics"
msgstr "Mätverk" msgstr "Mätverk"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Flytta sidan nedåt" msgstr "Flytta sidan nedåt"
@@ -515,6 +538,14 @@ msgstr "Öppna aktuell post i en ny flik i bakgrunden"
msgid "Open link" msgid "Open link"
msgstr "Öppen länk" msgstr "Öppen länk"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Öppna nästa post" msgstr "Öppna nästa post"
@@ -581,6 +612,10 @@ msgstr "Uppdatera"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringar är stängda på denna CommaFeed-instans" msgstr "Registreringar är stängda på denna CommaFeed-instans"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Delningssajter"
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Visa flöden och kategorier utan olästa poster" msgstr "Visa flöden och kategorier utan olästa poster"
@@ -654,6 +697,7 @@ msgstr "Något dåligt hände precis..."
msgid "Space" msgid "Space"
msgstr "Rymden" msgstr "Rymden"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Stjärna" msgstr "Stjärna"
@@ -681,6 +725,10 @@ msgstr "Prenumerera på flödet"
msgid "Success" msgid "Success"
msgstr "Framgång" msgstr "Framgång"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Byt till mörkt tema" msgstr "Byt till mörkt tema"
@@ -713,6 +761,7 @@ msgstr "Prova CommaFeed med demokontot: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Oläst" msgstr "Oläst"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "" msgstr ""
@@ -743,6 +792,14 @@ msgstr "Webbplats"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Du har inga prenumerationer än. " msgstr "Du har inga prenumerationer än. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "fil krävs" msgstr "fil krävs"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "Feed URL'si"
msgid "Feed name" msgid "Feed name"
msgstr "Yayın adı" msgstr "Yayın adı"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "Filtreleme ifadesi" msgstr "Filtreleme ifadesi"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "Parolanızı mı unuttunuz?" msgstr "Parolanızı mı unuttunuz?"
@@ -320,6 +328,10 @@ msgstr "API belgelerine gidin."
msgid "Go to the All view" msgid "Go to the All view"
msgstr "Tümü görünümüne git" msgstr "Tümü görünümüne git"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "İyilikler" msgstr "İyilikler"
@@ -348,6 +360,7 @@ msgstr "İçe Aktar"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "Genişletilmiş görünümde, girişler arasında gezinmek onları okundu olarak işaretler" msgstr "Genişletilmiş görünümde, girişler arasında gezinmek onları okundu olarak işaretler"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "Okunmadan sakla" msgstr "Okunmadan sakla"
@@ -404,6 +417,10 @@ msgstr "Giriş"
msgid "Logout" msgid "Logout"
msgstr "Çıkış" msgstr "Çıkış"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "Tümünü okundu olarak işaretle"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "Tüm girişleri okundu olarak işaretle" msgstr "Tüm girişleri okundu olarak işaretle"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "Okundu olarak işaretle" msgstr "Okundu olarak işaretle"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "Buraya kadar okundu olarak işaretle" msgstr "Buraya kadar okundu olarak işaretle"
@@ -430,6 +449,10 @@ msgstr "Buraya kadar okundu olarak işaretle"
msgid "Metrics" msgid "Metrics"
msgstr "Metrikler" msgstr "Metrikler"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "Sayfayı aşağı taşı" msgstr "Sayfayı aşağı taşı"
@@ -515,6 +538,14 @@ msgstr "Geçerli girişi arka planda yeni bir sekmede aç"
msgid "Open link" msgid "Open link"
msgstr "Bağlantıyı aç" msgstr "Bağlantıyı aç"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "Sonraki girişi aç" msgstr "Sonraki girişi aç"
@@ -581,6 +612,10 @@ msgstr "Yenile"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "Bu CommaFeed örneğinde kayıtlar kapalı" msgstr "Bu CommaFeed örneğinde kayıtlar kapalı"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "Siteleri paylaşma"
msgid "Shift" msgid "Shift"
msgstr "Vardiya" msgstr "Vardiya"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "Okunmamış girişi olmayan beslemeleri ve kategorileri göster" msgstr "Okunmamış girişi olmayan beslemeleri ve kategorileri göster"
@@ -654,6 +697,7 @@ msgstr "Az önce kötü bir şey oldu..."
msgid "Space" msgid "Space"
msgstr "Uzay" msgstr "Uzay"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "Yıldız" msgstr "Yıldız"
@@ -681,6 +725,10 @@ msgstr "beslemeye abone olun"
msgid "Success" msgid "Success"
msgstr "Başarı" msgstr "Başarı"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "Karanlık temaya geç" msgstr "Karanlık temaya geç"
@@ -713,6 +761,7 @@ msgstr "CommaFeed'i demo hesabıyla deneyin: demo/demo"
msgid "Unread" msgid "Unread"
msgstr "Okunmadı" msgstr "Okunmadı"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "Yıldızı kaldır" msgstr "Yıldızı kaldır"
@@ -743,6 +792,14 @@ msgstr "Web sitesi"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Henüz aboneliğiniz yok. " msgstr "Henüz aboneliğiniz yok. "
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "dosya gerekli" msgstr "dosya gerekli"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -288,10 +288,18 @@ msgstr "供稿网址"
msgid "Feed name" msgid "Feed name"
msgstr "提要名称" msgstr "提要名称"
#: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now"
msgstr ""
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "Filtering expression" msgid "Filtering expression"
msgstr "过滤表达式" msgstr "过滤表达式"
#: src/pages/app/AboutPage.tsx
msgid "For those of you who prefer bitcoin, here is the address: {bitcoinAddress}"
msgstr ""
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Forgot password?" msgid "Forgot password?"
msgstr "忘记密码?" msgstr "忘记密码?"
@@ -320,6 +328,10 @@ msgstr "转到 API 文档。"
msgid "Go to the All view" msgid "Go to the All view"
msgstr "转到全部视图" msgstr "转到全部视图"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}"
msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Goodies" msgid "Goodies"
msgstr "好东西" msgstr "好东西"
@@ -348,6 +360,7 @@ msgstr "进口"
msgid "In expanded view, scrolling through entries mark them as read" msgid "In expanded view, scrolling through entries mark them as read"
msgstr "在展开视图中,滚动条目将它们标记为已读" msgstr "在展开视图中,滚动条目将它们标记为已读"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread" msgid "Keep unread"
msgstr "保持未读状态" msgstr "保持未读状态"
@@ -404,6 +417,10 @@ msgstr "登录"
msgid "Logout" msgid "Logout"
msgstr "注销" msgstr "注销"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Manage users" msgid "Manage users"
@@ -418,10 +435,12 @@ msgstr "全部标记为已读"
msgid "Mark all entries as read" msgid "Mark all entries as read"
msgstr "将所有条目标记为已读" msgstr "将所有条目标记为已读"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read" msgid "Mark as read"
msgstr "标记为已读" msgstr "标记为已读"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Mark as read up to here" msgid "Mark as read up to here"
msgstr "标记为已读到这里" msgstr "标记为已读到这里"
@@ -430,6 +449,10 @@ msgstr "标记为已读到这里"
msgid "Metrics" msgid "Metrics"
msgstr "指标" msgstr "指标"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
msgstr "页面下移" msgstr "页面下移"
@@ -515,6 +538,14 @@ msgstr "在后台的新选项卡中打开当前条目"
msgid "Open link" msgid "Open link"
msgstr "打开链接" msgstr "打开链接"
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
msgstr "打开下一个条目" msgstr "打开下一个条目"
@@ -581,6 +612,10 @@ msgstr "刷新"
msgid "Registrations are closed on this CommaFeed instance" msgid "Registrations are closed on this CommaFeed instance"
msgstr "此 CommaFeed 实例上的注册已关闭" msgstr "此 CommaFeed 实例上的注册已关闭"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr ""
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
#: src/pages/app/CategoryDetailsPage.tsx #: src/pages/app/CategoryDetailsPage.tsx
@@ -632,6 +667,14 @@ msgstr "共享站点"
msgid "Shift" msgid "Shift"
msgstr "换档" msgstr "换档"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)"
msgstr ""
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
msgstr "显示没有未读条目的提要和类别" msgstr "显示没有未读条目的提要和类别"
@@ -654,6 +697,7 @@ msgstr "刚刚发生了不好的事情……"
msgid "Space" msgid "Space"
msgstr "空间" msgstr "空间"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Star" msgid "Star"
msgstr "星星" msgstr "星星"
@@ -681,6 +725,10 @@ msgstr "订阅订阅源"
msgid "Success" msgid "Success"
msgstr "成功" msgstr "成功"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right"
msgstr ""
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Switch to dark theme" msgid "Switch to dark theme"
msgstr "切换到深色主题" msgstr "切换到深色主题"
@@ -713,6 +761,7 @@ msgstr "使用演示帐户试用 CommaFeeddemo/demo"
msgid "Unread" msgid "Unread"
msgstr "未读" msgstr "未读"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Unstar" msgid "Unstar"
msgstr "解星" msgstr "解星"
@@ -743,6 +792,14 @@ msgstr "网站"
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "您还没有任何订阅。" msgstr "您还没有任何订阅。"
#: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh."
msgstr ""
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
msgstr "文件是必需的" msgstr "文件是必需的"
#: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})"
msgstr ""

View File

@@ -2,6 +2,7 @@ import "@fontsource/open-sans"
import { store } from "app/store" import { store } from "app/store"
import dayjs from "dayjs" import dayjs from "dayjs"
import relativeTime from "dayjs/plugin/relativeTime" import relativeTime from "dayjs/plugin/relativeTime"
import "react-contexify/ReactContexify.css"
import ReactDOM from "react-dom/client" import ReactDOM from "react-dom/client"
import { Provider } from "react-redux" import { Provider } from "react-redux"
import { App } from "./App" import { App } from "./App"

View File

@@ -21,9 +21,7 @@ const shownGauges: { [key: string]: string } = {
"com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.pending": "Feed Updater queued", "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.pending": "Feed Updater queued",
"com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.active": "Feed Worker active", "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.active": "Feed Worker active",
"com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.pending": "Feed Worker queued", "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.pending": "Feed Worker queued",
"com.commafeed.backend.feed.FeedQueues.addQueue": "Task Giver Add Queue", "com.commafeed.backend.feed.FeedQueues.queue": "Feed Refresh queue size",
"com.commafeed.backend.feed.FeedQueues.takeQueue": "Task Giver Take Queue",
"com.commafeed.backend.feed.FeedQueues.giveBackQueue": "Task Giver Give Back Queue",
} }
export function MetricsPage() { export function MetricsPage() {

View File

@@ -1,5 +1,5 @@
import { t, Trans } from "@lingui/macro" import { t, Trans } from "@lingui/macro"
import { Anchor, Box, Center, Container, createStyles, List, NativeSelect, SimpleGrid, Title } from "@mantine/core" import { Anchor, Box, Center, Code, Container, createStyles, List, NativeSelect, SimpleGrid, Title } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { redirectToApiDocumentation } from "app/slices/redirect" import { redirectToApiDocumentation } from "app/slices/redirect"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
@@ -57,6 +57,7 @@ function NextUnreadBookmarklet() {
) )
} }
const bitcoinAddress = <Code>{Constants.bitcoinWalletAddress}</Code>
export function AboutPage() { export function AboutPage() {
const version = useAppSelector(state => state.server.serverInfos?.version) const version = useAppSelector(state => state.server.serverInfos?.version)
const revision = useAppSelector(state => state.server.serverInfos?.gitCommit) const revision = useAppSelector(state => state.server.serverInfos?.gitCommit)
@@ -70,7 +71,7 @@ export function AboutPage() {
CommaFeed version {version} ({revision}) CommaFeed version {version} ({revision})
</Trans> </Trans>
</Box> </Box>
<Box> <Box mt="md">
<Trans> <Trans>
CommaFeed is an open-source project. Sources are hosted on&nbsp; CommaFeed is an open-source project. Sources are hosted on&nbsp;
<Anchor href="https://github.com/Athou/commafeed" target="_blank" rel="noreferrer"> <Anchor href="https://github.com/Athou/commafeed" target="_blank" rel="noreferrer">
@@ -114,6 +115,9 @@ export function AboutPage() {
</Center> </Center>
</form> </form>
</Box> </Box>
<Box mt="xs">
<Trans>For those of you who prefer bitcoin, here is the address: {bitcoinAddress}</Trans>
</Box>
</Section> </Section>
<Section title={t`Goodies`} icon={<TbPuzzle size={24} />}> <Section title={t`Goodies`} icon={<TbPuzzle size={24} />}>
<List> <List>

View File

@@ -109,7 +109,12 @@ export function CategoryDetailsPage() {
{editable && ( {editable && (
<> <>
<TextInput label={t`Name`} {...form.getInputProps("name")} required /> <TextInput label={t`Name`} {...form.getInputProps("name")} required />
<CategorySelect label={t`Parent Category`} {...form.getInputProps("parentId")} clearable /> <CategorySelect
label={t`Parent Category`}
{...form.getInputProps("parentId")}
clearable
withoutCategoryIds={[id]}
/>
<NumberInput label={t`Position`} {...form.getInputProps("position")} required min={0} /> <NumberInput label={t`Position`} {...form.getInputProps("position")} required min={0} />
</> </>
)} )}

View File

@@ -17,7 +17,7 @@ import { TbDeviceFloppy, TbTrash } from "react-icons/tb"
import { useParams } from "react-router-dom" import { useParams } from "react-router-dom"
function FilteringExpressionDescription() { function FilteringExpressionDescription() {
const example = <Code>url.contains('youtube') or (author eq 'athou' and title.contains('github')</Code> const example = <Code>url.contains('youtube') or (author eq 'athou' and title.contains('github'))</Code>
return ( return (
<div> <div>
<div> <div>

View File

@@ -25,6 +25,7 @@ import { Logo } from "components/Logo"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "components/responsive/OnDesktop"
import { OnMobile } from "components/responsive/OnMobile" import { OnMobile } from "components/responsive/OnMobile"
import { useAppLoading } from "hooks/useAppLoading" import { useAppLoading } from "hooks/useAppLoading"
import { useWebSocket } from "hooks/useWebSocket"
import { LoadingPage } from "pages/LoadingPage" import { LoadingPage } from "pages/LoadingPage"
import { ReactNode, Suspense, useEffect } from "react" import { ReactNode, Suspense, useEffect } from "react"
import { TbPlus } from "react-icons/tb" import { TbPlus } from "react-icons/tb"
@@ -85,6 +86,7 @@ export default function Layout({ sidebar, header }: LayoutProps) {
const { loading } = useAppLoading() const { loading } = useAppLoading()
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen) const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
useWebSocket()
useEffect(() => { useEffect(() => {
dispatch(reloadSettings()) dispatch(reloadSettings())

View File

@@ -22,6 +22,7 @@ export default defineConfig({
port: 8082, port: 8082,
proxy: { proxy: {
"/rest": "http://localhost:8083", "/rest": "http://localhost:8083",
"/ws": "ws://localhost:8083",
"/swagger": "http://localhost:8083", "/swagger": "http://localhost:8083",
}, },
}, },

View File

@@ -87,7 +87,7 @@ app:
database: database:
driverClass: org.h2.Driver driverClass: org.h2.Driver
url: jdbc:h2:/home/commafeed/db url: jdbc:h2:/commafeed/data/db
user: sa user: sa
password: sa password: sa
properties: properties:

View File

@@ -41,7 +41,9 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version> <configuration>
<parameters>true</parameters>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@@ -234,7 +236,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.22</version> <version>1.18.26</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -261,8 +263,8 @@
<artifactId>dropwizard-hibernate</artifactId> <artifactId>dropwizard-hibernate</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.liquibase</groupId> <groupId>io.dropwizard</groupId>
<artifactId>liquibase-core</artifactId> <artifactId>dropwizard-migrations</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.dropwizard</groupId> <groupId>io.dropwizard</groupId>
@@ -285,6 +287,16 @@
<artifactId>dropwizard-web</artifactId> <artifactId>dropwizard-web</artifactId>
<version>1.5.0</version> <version>1.5.0</version>
</dependency> </dependency>
<dependency>
<groupId>be.tomcools</groupId>
<artifactId>dropwizard-websocket-jee7-bundle</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>io.whitfin</groupId>
<artifactId>dropwizard-environment-substitutor</artifactId>
<version>1.1.1</version>
</dependency>
<dependency> <dependency>
<groupId>javax.xml.bind</groupId> <groupId>javax.xml.bind</groupId>
@@ -474,6 +486,11 @@
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.mock-server</groupId> <groupId>org.mock-server</groupId>
<artifactId>mockserver-junit-jupiter</artifactId> <artifactId>mockserver-junit-jupiter</artifactId>

View File

@@ -13,6 +13,7 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.websocket.server.ServerEndpointConfig;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
@@ -47,22 +48,31 @@ import com.commafeed.frontend.servlet.CustomCssServlet;
import com.commafeed.frontend.servlet.LogoutServlet; import com.commafeed.frontend.servlet.LogoutServlet;
import com.commafeed.frontend.servlet.NextUnreadServlet; import com.commafeed.frontend.servlet.NextUnreadServlet;
import com.commafeed.frontend.session.SessionHelperFactoryProvider; import com.commafeed.frontend.session.SessionHelperFactoryProvider;
import com.commafeed.frontend.ws.WebSocketConfigurator;
import com.commafeed.frontend.ws.WebSocketEndpoint;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import be.tomcools.dropwizard.websocket.WebsocketBundle;
import io.dropwizard.Application; import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle; import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.configuration.DefaultConfigurationFactoryFactory;
import io.dropwizard.configuration.EnvironmentVariableSubstitutor;
import io.dropwizard.configuration.SubstitutingSourceProvider;
import io.dropwizard.db.DataSourceFactory; import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.forms.MultiPartBundle; import io.dropwizard.forms.MultiPartBundle;
import io.dropwizard.hibernate.HibernateBundle; import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.server.DefaultServerFactory; import io.dropwizard.migrations.MigrationsBundle;
import io.dropwizard.servlets.CacheBustingFilter; import io.dropwizard.servlets.CacheBustingFilter;
import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment; import io.dropwizard.setup.Environment;
import io.dropwizard.web.WebBundle; import io.dropwizard.web.WebBundle;
import io.dropwizard.web.conf.WebConfiguration; import io.dropwizard.web.conf.WebConfiguration;
import io.whitfin.dropwizard.configuration.EnvironmentSubstitutor;
public class CommaFeedApplication extends Application<CommaFeedConfiguration> { public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
@@ -72,6 +82,7 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
public static final Date STARTUP_TIME = new Date(); public static final Date STARTUP_TIME = new Date();
private HibernateBundle<CommaFeedConfiguration> hibernateBundle; private HibernateBundle<CommaFeedConfiguration> hibernateBundle;
private WebsocketBundle<CommaFeedConfiguration> websocketBundle;
@Override @Override
public String getName() { public String getName() {
@@ -80,8 +91,26 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
@Override @Override
public void initialize(Bootstrap<CommaFeedConfiguration> bootstrap) { public void initialize(Bootstrap<CommaFeedConfiguration> bootstrap) {
bootstrap.setConfigurationFactoryFactory(new DefaultConfigurationFactoryFactory<CommaFeedConfiguration>() {
@Override
protected ObjectMapper configureObjectMapper(ObjectMapper objectMapper) {
// disable case sensitivity because EnvironmentSubstitutor maps MYPROPERTY to myproperty and not to myProperty
return objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
}
});
// enable config.yml string substitution
// e.g. having a custom config.yml file with app.session.path=${SOME_ENV_VAR} will substitute SOME_ENV_VAR
SubstitutingSourceProvider substitutingSourceProvider = new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor(false));
// enable config.yml properties override with env variables prefixed with CF_
// e.g. setting CF_APP_ALLOWREGISTRATIONS=true will set app.allowRegistrations to true
EnvironmentSubstitutor environmentSubstitutor = new EnvironmentSubstitutor("CF", substitutingSourceProvider);
bootstrap.setConfigurationSourceProvider(environmentSubstitutor);
bootstrap.getObjectMapper().registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.SECONDS, false)); bootstrap.getObjectMapper().registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.SECONDS, false));
bootstrap.addBundle(websocketBundle = new WebsocketBundle<>());
bootstrap.addBundle(hibernateBundle = new HibernateBundle<CommaFeedConfiguration>(AbstractModel.class, Feed.class, bootstrap.addBundle(hibernateBundle = new HibernateBundle<CommaFeedConfiguration>(AbstractModel.class, Feed.class,
FeedCategory.class, FeedEntry.class, FeedEntryContent.class, FeedEntryStatus.class, FeedEntryTag.class, FeedCategory.class, FeedEntry.class, FeedEntryContent.class, FeedEntryStatus.class, FeedEntryTag.class,
FeedSubscription.class, User.class, UserRole.class, UserSettings.class) { FeedSubscription.class, User.class, UserRole.class, UserSettings.class) {
@@ -107,6 +136,13 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
} }
}); });
bootstrap.addBundle(new MigrationsBundle<CommaFeedConfiguration>() {
@Override
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
return configuration.getDataSourceFactory();
}
});
bootstrap.addBundle(new AssetsBundle("/assets/", "/", "index.html")); bootstrap.addBundle(new AssetsBundle("/assets/", "/", "index.html"));
bootstrap.addBundle(new MultiPartBundle()); bootstrap.addBundle(new MultiPartBundle());
} }
@@ -126,7 +162,6 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
// REST resources // REST resources
environment.jersey().setUrlPattern("/rest/*"); environment.jersey().setUrlPattern("/rest/*");
((DefaultServerFactory) config.getServerFactory()).setJerseyRootPath("/rest/*");
environment.jersey().register(injector.getInstance(AdminREST.class)); environment.jersey().register(injector.getInstance(AdminREST.class));
environment.jersey().register(injector.getInstance(CategoryREST.class)); environment.jersey().register(injector.getInstance(CategoryREST.class));
environment.jersey().register(injector.getInstance(EntryREST.class)); environment.jersey().register(injector.getInstance(EntryREST.class));
@@ -141,6 +176,12 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
environment.servlets().addServlet("customCss", injector.getInstance(CustomCssServlet.class)).addMapping("/custom_css.css"); environment.servlets().addServlet("customCss", injector.getInstance(CustomCssServlet.class)).addMapping("/custom_css.css");
environment.servlets().addServlet("analytics.js", injector.getInstance(AnalyticsServlet.class)).addMapping("/analytics.js"); environment.servlets().addServlet("analytics.js", injector.getInstance(AnalyticsServlet.class)).addMapping("/analytics.js");
// WebSocket endpoint
ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(WebSocketEndpoint.class, "/ws")
.configurator(injector.getInstance(WebSocketConfigurator.class))
.build();
websocketBundle.addEndpoint(serverEndpointConfig);
// Scheduled tasks // Scheduled tasks
Set<ScheduledTask> tasks = injector.getInstance(Key.get(new TypeLiteral<Set<ScheduledTask>>() { Set<ScheduledTask> tasks = injector.getInstance(Key.get(new TypeLiteral<Set<ScheduledTask>>() {
})); }));

View File

@@ -45,18 +45,14 @@ public class CommaFeedConfiguration extends Configuration {
@JsonProperty("app") @JsonProperty("app")
private ApplicationSettings applicationSettings; private ApplicationSettings applicationSettings;
private final ResourceBundle bundle; private final String version;
private final String gitCommit;
public CommaFeedConfiguration() { public CommaFeedConfiguration() {
bundle = ResourceBundle.getBundle("application"); ResourceBundle bundle = ResourceBundle.getBundle("application");
}
public String getVersion() { this.version = bundle.getString("version");
return bundle.getString("version"); this.gitCommit = bundle.getString("git.commit");
}
public String getGitCommit() {
return bundle.getString("git.commit");
} }
@Getter @Getter

View File

@@ -182,11 +182,28 @@ public class HttpGetter {
System.out.println(new String(result.content)); System.out.println(new String(result.content));
} }
@Getter
public static class NotModifiedException extends Exception { public static class NotModifiedException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* if the value of this header changed, this is its new value
*/
private String newLastModifiedHeader;
/**
* if the value of this header changed, this is its new value
*/
private String newEtagHeader;
public NotModifiedException(String message) { public NotModifiedException(String message) {
this(message, null, null);
}
public NotModifiedException(String message, String newLastModifiedHeader, String newEtagHeader) {
super(message); super(message);
this.newLastModifiedHeader = newLastModifiedHeader;
this.newEtagHeader = newEtagHeader;
} }
} }

View File

@@ -62,16 +62,23 @@ public class FeedFetcher {
throw new IOException("Feed content is empty."); throw new IOException("Feed content is empty.");
} }
boolean lastModifiedHeaderValueChanged = !StringUtils.equals(lastModified, result.getLastModifiedSince());
boolean etagHeaderValueChanged = !StringUtils.equals(eTag, result.getETag());
String hash = DigestUtils.sha1Hex(content); String hash = DigestUtils.sha1Hex(content);
if (lastContentHash != null && hash != null && lastContentHash.equals(hash)) { if (lastContentHash != null && hash != null && lastContentHash.equals(hash)) {
log.debug("content hash not modified: {}", feedUrl); log.debug("content hash not modified: {}", feedUrl);
throw new NotModifiedException("content hash not modified"); throw new NotModifiedException("content hash not modified",
lastModifiedHeaderValueChanged ? result.getLastModifiedSince() : null,
etagHeaderValueChanged ? result.getETag() : null);
} }
if (lastPublishedDate != null && fetchedFeed.getFeed().getLastPublishedDate() != null if (lastPublishedDate != null && fetchedFeed.getFeed().getLastPublishedDate() != null
&& lastPublishedDate.getTime() == fetchedFeed.getFeed().getLastPublishedDate().getTime()) { && lastPublishedDate.getTime() == fetchedFeed.getFeed().getLastPublishedDate().getTime()) {
log.debug("publishedDate not modified: {}", feedUrl); log.debug("publishedDate not modified: {}", feedUrl);
throw new NotModifiedException("publishedDate not modified"); throw new NotModifiedException("publishedDate not modified",
lastModifiedHeaderValueChanged ? result.getLastModifiedSince() : null,
etagHeaderValueChanged ? result.getETag() : null);
} }
Feed feed = fetchedFeed.getFeed(); Feed feed = fetchedFeed.getFeed();

View File

@@ -1,13 +1,10 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.concurrent.BlockingDeque;
import java.util.Queue; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -27,126 +24,90 @@ import com.commafeed.backend.model.Feed;
@Singleton @Singleton
public class FeedQueues { public class FeedQueues {
private SessionFactory sessionFactory; private final SessionFactory sessionFactory;
private final FeedDAO feedDAO; private final FeedDAO feedDAO;
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
private final BlockingDeque<FeedRefreshContext> queue = new LinkedBlockingDeque<>();
private Queue<FeedRefreshContext> addQueue = new ConcurrentLinkedQueue<>(); private final Meter refill;
private Queue<FeedRefreshContext> takeQueue = new ConcurrentLinkedQueue<>();
private Queue<Feed> giveBackQueue = new ConcurrentLinkedQueue<>();
private Meter refill;
@Inject @Inject
public FeedQueues(SessionFactory sessionFactory, FeedDAO feedDAO, CommaFeedConfiguration config, MetricRegistry metrics) { public FeedQueues(SessionFactory sessionFactory, FeedDAO feedDAO, CommaFeedConfiguration config, MetricRegistry metrics) {
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
this.config = config; this.config = config;
this.feedDAO = feedDAO; this.feedDAO = feedDAO;
this.refill = metrics.meter(MetricRegistry.name(getClass(), "refill"));
refill = metrics.meter(MetricRegistry.name(getClass(), "refill")); metrics.register(MetricRegistry.name(getClass(), "queue"), (Gauge<Integer>) queue::size);
metrics.register(MetricRegistry.name(getClass(), "addQueue"), new Gauge<Integer>() {
@Override
public Integer getValue() {
return addQueue.size();
}
});
metrics.register(MetricRegistry.name(getClass(), "takeQueue"), new Gauge<Integer>() {
@Override
public Integer getValue() {
return takeQueue.size();
}
});
metrics.register(MetricRegistry.name(getClass(), "giveBackQueue"), new Gauge<Integer>() {
@Override
public Integer getValue() {
return giveBackQueue.size();
}
});
} }
/** /**
* take a feed from the refresh queue * take a feed from the refresh queue
*/ */
public synchronized FeedRefreshContext take() { public synchronized FeedRefreshContext take() {
FeedRefreshContext context = takeQueue.poll(); FeedRefreshContext context = queue.poll();
if (context != null) {
if (context == null) { return context;
refill(); }
context = takeQueue.poll();
refill();
try {
// try to get something from the queue
// if the queue is empty, wait a bit
// polling the queue instead of sleeping gives us the opportunity to process a feed immediately if it was added manually with
// add()
return queue.poll(15, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException("interrupted while waiting for a feed in the queue", e);
} }
return context;
} }
/** /**
* add a feed to the refresh queue * add a feed to the refresh queue
*/ */
public void add(Feed feed, boolean urgent) { public void add(Feed feed, boolean urgent) {
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes(); if (isFeedAlreadyQueued(feed)) {
if (feed.getLastUpdated() == null || feed.getLastUpdated().before(DateUtils.addMinutes(new Date(), -1 * refreshInterval))) { return;
boolean alreadyQueued = addQueue.stream().anyMatch(c -> c.getFeed().getId().equals(feed.getId())); }
if (!alreadyQueued) {
addQueue.add(new FeedRefreshContext(feed, urgent)); FeedRefreshContext context = new FeedRefreshContext(feed, urgent);
} if (urgent) {
queue.addFirst(context);
} else {
queue.addLast(context);
} }
} }
/** /**
* refills the refresh queue and empties the giveBack queue while at it * refills the refresh queue
*/ */
private void refill() { private void refill() {
refill.mark(); refill.mark();
List<FeedRefreshContext> contexts = new ArrayList<>();
int batchSize = Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
// add feeds we got from the add() method
int addQueueSize = addQueue.size();
for (int i = 0; i < Math.min(batchSize, addQueueSize); i++) {
contexts.add(addQueue.poll());
}
// add feeds that are up to refresh from the database // add feeds that are up to refresh from the database
int count = batchSize - contexts.size(); int batchSize = Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
if (count > 0) { List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> {
List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> feedDAO.findNextUpdatable(count, getLastLoginThreshold())); List<Feed> list = feedDAO.findNextUpdatable(batchSize, getLastLoginThreshold());
for (Feed feed : feeds) {
contexts.add(new FeedRefreshContext(feed, false));
}
}
// set the disabledDate as we use it in feedDAO to decide what to refresh next. We also use a map to remove // set the disabledDate as we use it in feedDAO.findNextUpdatable() to decide what to refresh next
// duplicates. Date nextRefreshDate = DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes());
Map<Long, FeedRefreshContext> map = new LinkedHashMap<>(); list.forEach(f -> f.setDisabledUntil(nextRefreshDate));
for (FeedRefreshContext context : contexts) { feedDAO.saveOrUpdate(list);
Feed feed = context.getFeed();
feed.setDisabledUntil(DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes()));
map.put(feed.getId(), context);
}
// refill the queue return list;
takeQueue.addAll(map.values()); });
// add feeds from the giveBack queue to the map, overriding duplicates feeds.forEach(f -> add(f, false));
int giveBackQueueSize = giveBackQueue.size();
for (int i = 0; i < giveBackQueueSize; i++) {
Feed feed = giveBackQueue.poll();
map.put(feed.getId(), new FeedRefreshContext(feed, false));
}
// update all feeds in the database
List<Feed> feeds = map.values().stream().map(c -> c.getFeed()).collect(Collectors.toList());
UnitOfWork.run(sessionFactory, () -> feedDAO.saveOrUpdate(feeds));
} }
/**
* give a feed back, updating it to the database during the next refill()
*/
public void giveBack(Feed feed) { public void giveBack(Feed feed) {
String normalized = FeedUtils.normalizeURL(feed.getUrl()); String normalized = FeedUtils.normalizeURL(feed.getUrl());
feed.setNormalizedUrl(normalized); feed.setNormalizedUrl(normalized);
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized)); feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized));
feed.setLastUpdated(new Date()); feed.setLastUpdated(new Date());
giveBackQueue.add(feed); UnitOfWork.run(sessionFactory, () -> feedDAO.saveOrUpdate(feed));
// we just finished updating the feed, remove it from the queue
queue.removeIf(c -> isFeedAlreadyQueued(c.getFeed()));
} }
private Date getLastLoginThreshold() { private Date getLastLoginThreshold() {
@@ -157,4 +118,7 @@ public class FeedQueues {
} }
} }
private boolean isFeedAlreadyQueued(Feed feed) {
return queue.stream().anyMatch(c -> c.getFeed().getId().equals(feed.getId()));
}
} }

View File

@@ -0,0 +1,84 @@
package com.commafeed.backend.feed;
import java.util.Date;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang3.time.DateUtils;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.model.Feed;
@Singleton
public class FeedRefreshIntervalCalculator {
private boolean heavyLoad;
private int refreshIntervalMinutes;
@Inject
public FeedRefreshIntervalCalculator(CommaFeedConfiguration config) {
this.heavyLoad = config.getApplicationSettings().getHeavyLoad();
this.refreshIntervalMinutes = config.getApplicationSettings().getRefreshIntervalMinutes();
}
public Date onFetchSuccess(FetchedFeed fetchedFeed) {
Date defaultRefreshInterval = getDefaultRefreshInterval();
return heavyLoad ? computeRefreshIntervalForHeavyLoad(fetchedFeed.getFeed(), defaultRefreshInterval) : defaultRefreshInterval;
}
public Date onFeedNotModified(Feed feed) {
Date defaultRefreshInterval = getDefaultRefreshInterval();
return heavyLoad ? computeRefreshIntervalForHeavyLoad(feed, defaultRefreshInterval) : defaultRefreshInterval;
}
public Date onFetchError(Feed feed) {
int errorCount = feed.getErrorCount();
int retriesBeforeDisable = 3;
if (errorCount < retriesBeforeDisable || !heavyLoad) {
return getDefaultRefreshInterval();
}
int disabledHours = Math.min(24 * 7, errorCount - retriesBeforeDisable + 1);
return DateUtils.addHours(new Date(), disabledHours);
}
private Date getDefaultRefreshInterval() {
return DateUtils.addMinutes(new Date(), refreshIntervalMinutes);
}
private Date computeRefreshIntervalForHeavyLoad(Feed feed, Date defaultRefreshInterval) {
Date now = new Date();
Date publishedDate = feed.getLastEntryDate();
Long averageEntryInterval = feed.getAverageEntryInterval();
if (publishedDate == null) {
// feed with no entries, recheck in 24 hours
return DateUtils.addHours(now, 24);
} else if (publishedDate.before(DateUtils.addMonths(now, -1))) {
// older than a month, recheck in 24 hours
return DateUtils.addHours(now, 24);
} else if (publishedDate.before(DateUtils.addDays(now, -14))) {
// older than two weeks, recheck in 12 hours
return DateUtils.addHours(now, 12);
} else if (publishedDate.before(DateUtils.addDays(now, -7))) {
// older than a week, recheck in 6 hours
return DateUtils.addHours(now, 6);
} else if (averageEntryInterval != null) {
// use average time between entries to decide when to refresh next, divided by factor
int factor = 2;
// not more than 6 hours
long date = Math.min(DateUtils.addHours(now, 6).getTime(), now.getTime() + averageEntryInterval / factor);
// not less than default refresh interval
date = Math.max(defaultRefreshInterval.getTime(), date);
return new Date(date);
} else {
// unknown case, recheck in 24 hours
return DateUtils.addHours(now, 24);
}
}
}

View File

@@ -28,7 +28,6 @@ public class FeedRefreshTaskGiver implements Managed {
private final ExecutorService executor; private final ExecutorService executor;
private final Meter feedRefreshed; private final Meter feedRefreshed;
private final Meter threadWaited;
@Inject @Inject
public FeedRefreshTaskGiver(FeedQueues queues, FeedDAO feedDAO, FeedRefreshWorker worker, CommaFeedConfiguration config, public FeedRefreshTaskGiver(FeedQueues queues, FeedDAO feedDAO, FeedRefreshWorker worker, CommaFeedConfiguration config,
@@ -38,7 +37,6 @@ public class FeedRefreshTaskGiver implements Managed {
executor = Executors.newFixedThreadPool(1); executor = Executors.newFixedThreadPool(1);
feedRefreshed = metrics.meter(MetricRegistry.name(getClass(), "feedRefreshed")); feedRefreshed = metrics.meter(MetricRegistry.name(getClass(), "feedRefreshed"));
threadWaited = metrics.meter(MetricRegistry.name(getClass(), "threadWaited"));
} }
@Override @Override
@@ -66,14 +64,6 @@ public class FeedRefreshTaskGiver implements Managed {
if (context != null) { if (context != null) {
feedRefreshed.mark(); feedRefreshed.mark();
worker.updateFeed(context); worker.updateFeed(context);
} else {
log.debug("nothing to do, sleeping for 15s");
threadWaited.mark();
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
log.debug("interrupted while sleeping");
}
} }
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);

View File

@@ -32,9 +32,12 @@ import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.backend.service.FeedUpdateService; import com.commafeed.backend.service.FeedUpdateService;
import com.commafeed.backend.service.PubSubService; import com.commafeed.backend.service.PubSubService;
import com.commafeed.frontend.ws.WebSocketMessageBuilder;
import com.commafeed.frontend.ws.WebSocketSessions;
import com.google.common.util.concurrent.Striped; import com.google.common.util.concurrent.Striped;
import io.dropwizard.lifecycle.Managed; import io.dropwizard.lifecycle.Managed;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@@ -48,6 +51,7 @@ public class FeedRefreshUpdater implements Managed {
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
private final FeedSubscriptionDAO feedSubscriptionDAO; private final FeedSubscriptionDAO feedSubscriptionDAO;
private final CacheService cache; private final CacheService cache;
private final WebSocketSessions webSocketSessions;
private final FeedRefreshExecutor pool; private final FeedRefreshExecutor pool;
private final Striped<Lock> locks; private final Striped<Lock> locks;
@@ -60,7 +64,7 @@ public class FeedRefreshUpdater implements Managed {
@Inject @Inject
public FeedRefreshUpdater(SessionFactory sessionFactory, FeedUpdateService feedUpdateService, PubSubService pubSubService, public FeedRefreshUpdater(SessionFactory sessionFactory, FeedUpdateService feedUpdateService, PubSubService pubSubService,
FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO, FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO,
CacheService cache) { CacheService cache, WebSocketSessions webSocketSessions) {
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
this.feedUpdateService = feedUpdateService; this.feedUpdateService = feedUpdateService;
this.pubSubService = pubSubService; this.pubSubService = pubSubService;
@@ -68,6 +72,7 @@ public class FeedRefreshUpdater implements Managed {
this.config = config; this.config = config;
this.feedSubscriptionDAO = feedSubscriptionDAO; this.feedSubscriptionDAO = feedSubscriptionDAO;
this.cache = cache; this.cache = cache;
this.webSocketSessions = webSocketSessions;
ApplicationSettings settings = config.getApplicationSettings(); ApplicationSettings settings = config.getApplicationSettings();
int threads = Math.max(settings.getDatabaseUpdateThreads(), 1); int threads = Math.max(settings.getDatabaseUpdateThreads(), 1);
@@ -94,8 +99,9 @@ public class FeedRefreshUpdater implements Managed {
pool.execute(new EntryTask(context)); pool.execute(new EntryTask(context));
} }
private boolean addEntry(final Feed feed, final FeedEntry entry, final List<FeedSubscription> subscriptions) { private AddEntryResult addEntry(final Feed feed, final FeedEntry entry, final List<FeedSubscription> subscriptions) {
boolean success = false; boolean processed = false;
boolean inserted = false;
// lock on feed, make sure we are not updating the same feed twice at // lock on feed, make sure we are not updating the same feed twice at
// the same time // the same time
@@ -112,14 +118,15 @@ public class FeedRefreshUpdater implements Managed {
boolean locked1 = false; boolean locked1 = false;
boolean locked2 = false; boolean locked2 = false;
try { try {
// try to lock, give up after 1 minute
locked1 = lock1.tryLock(1, TimeUnit.MINUTES); locked1 = lock1.tryLock(1, TimeUnit.MINUTES);
locked2 = lock2.tryLock(1, TimeUnit.MINUTES); locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
if (locked1 && locked2) { if (locked1 && locked2) {
boolean inserted = UnitOfWork.call(sessionFactory, () -> feedUpdateService.addEntry(feed, entry, subscriptions)); processed = true;
inserted = UnitOfWork.call(sessionFactory, () -> feedUpdateService.addEntry(feed, entry, subscriptions));
if (inserted) { if (inserted) {
entryInserted.mark(); entryInserted.mark();
} }
success = true;
} else { } else {
log.error("lock timeout for " + feed.getUrl() + " - " + key1); log.error("lock timeout for " + feed.getUrl() + " - " + key1);
} }
@@ -133,7 +140,7 @@ public class FeedRefreshUpdater implements Managed {
lock2.unlock(); lock2.unlock();
} }
} }
return success; return new AddEntryResult(processed, inserted);
} }
private void handlePubSub(final Feed feed) { private void handlePubSub(final Feed feed) {
@@ -169,7 +176,9 @@ public class FeedRefreshUpdater implements Managed {
@Override @Override
public void run() { public void run() {
boolean ok = true; boolean processed = true;
boolean insertedAtLeastOneEntry = false;
final Feed feed = context.getFeed(); final Feed feed = context.getFeed();
List<FeedEntry> entries = context.getEntries(); List<FeedEntry> entries = context.getEntries();
if (entries.isEmpty()) { if (entries.isEmpty()) {
@@ -186,7 +195,10 @@ public class FeedRefreshUpdater implements Managed {
if (subscriptions == null) { if (subscriptions == null) {
subscriptions = UnitOfWork.call(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed)); subscriptions = UnitOfWork.call(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed));
} }
ok &= addEntry(feed, entry, subscriptions); AddEntryResult addEntryResult = addEntry(feed, entry, subscriptions);
processed &= addEntryResult.processed;
insertedAtLeastOneEntry |= addEntryResult.inserted;
entryCacheMiss.mark(); entryCacheMiss.mark();
} else { } else {
log.debug("cache hit for {}", entry.getUrl()); log.debug("cache hit for {}", entry.getUrl());
@@ -199,17 +211,20 @@ public class FeedRefreshUpdater implements Managed {
if (subscriptions == null) { if (subscriptions == null) {
feed.setMessage("No new entries found"); feed.setMessage("No new entries found");
} else if (!subscriptions.isEmpty()) { } else if (insertedAtLeastOneEntry) {
List<User> users = subscriptions.stream().map(FeedSubscription::getUser).collect(Collectors.toList()); List<User> users = subscriptions.stream().map(FeedSubscription::getUser).collect(Collectors.toList());
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0])); cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
cache.invalidateUserRootCategory(users.toArray(new User[0])); cache.invalidateUserRootCategory(users.toArray(new User[0]));
// notify over websocket
subscriptions.forEach(sub -> webSocketSessions.sendMessage(sub.getUser(), WebSocketMessageBuilder.newFeedEntries(sub)));
} }
} }
if (config.getApplicationSettings().getPubsubhubbub()) { if (config.getApplicationSettings().getPubsubhubbub()) {
handlePubSub(feed); handlePubSub(feed);
} }
if (!ok) { if (!processed) {
// requeue asap // requeue asap
feed.setDisabledUntil(new Date(0)); feed.setDisabledUntil(new Date(0));
} }
@@ -223,4 +238,10 @@ public class FeedRefreshUpdater implements Managed {
} }
} }
@AllArgsConstructor
private static class AddEntryResult {
private final boolean processed;
private final boolean inserted;
}
} }

View File

@@ -1,6 +1,5 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -10,7 +9,6 @@ import javax.inject.Singleton;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
@@ -31,15 +29,17 @@ import lombok.extern.slf4j.Slf4j;
public class FeedRefreshWorker implements Managed { public class FeedRefreshWorker implements Managed {
private final FeedRefreshUpdater feedRefreshUpdater; private final FeedRefreshUpdater feedRefreshUpdater;
private final FeedRefreshIntervalCalculator refreshIntervalCalculator;
private final FeedFetcher fetcher; private final FeedFetcher fetcher;
private final FeedQueues queues; private final FeedQueues queues;
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
private final FeedRefreshExecutor pool; private final FeedRefreshExecutor pool;
@Inject @Inject
public FeedRefreshWorker(FeedRefreshUpdater feedRefreshUpdater, FeedFetcher fetcher, FeedQueues queues, CommaFeedConfiguration config, public FeedRefreshWorker(FeedRefreshUpdater feedRefreshUpdater, FeedRefreshIntervalCalculator refreshIntervalCalculator,
MetricRegistry metrics) { FeedFetcher fetcher, FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics) {
this.feedRefreshUpdater = feedRefreshUpdater; this.feedRefreshUpdater = feedRefreshUpdater;
this.refreshIntervalCalculator = refreshIntervalCalculator;
this.fetcher = fetcher; this.fetcher = fetcher;
this.config = config; this.config = config;
this.queues = queues; this.queues = queues;
@@ -62,8 +62,6 @@ public class FeedRefreshWorker implements Managed {
private void update(FeedRefreshContext context) { private void update(FeedRefreshContext context) {
Feed feed = context.getFeed(); Feed feed = context.getFeed();
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
Date disabledUntil = DateUtils.addMinutes(new Date(), refreshInterval);
try { try {
String url = Optional.ofNullable(feed.getUrlAfterRedirect()).orElse(feed.getUrl()); String url = Optional.ofNullable(feed.getUrlAfterRedirect()).orElse(feed.getUrl());
FetchedFeed fetchedFeed = fetcher.fetch(url, false, feed.getLastModifiedHeader(), feed.getEtagHeader(), FetchedFeed fetchedFeed = fetcher.fetch(url, false, feed.getLastModifiedHeader(), feed.getEtagHeader(),
@@ -76,10 +74,6 @@ public class FeedRefreshWorker implements Managed {
entries = entries.stream().limit(maxFeedCapacity).collect(Collectors.toList()); entries = entries.stream().limit(maxFeedCapacity).collect(Collectors.toList());
} }
if (config.getApplicationSettings().getHeavyLoad()) {
disabledUntil = FeedUtils.buildDisabledUntil(fetchedFeed.getFeed().getLastEntryDate(),
fetchedFeed.getFeed().getAverageEntryInterval(), disabledUntil);
}
String urlAfterRedirect = fetchedFeed.getUrlAfterRedirect(); String urlAfterRedirect = fetchedFeed.getUrlAfterRedirect();
if (StringUtils.equals(url, urlAfterRedirect)) { if (StringUtils.equals(url, urlAfterRedirect)) {
urlAfterRedirect = null; urlAfterRedirect = null;
@@ -95,7 +89,7 @@ public class FeedRefreshWorker implements Managed {
feed.setErrorCount(0); feed.setErrorCount(0);
feed.setMessage(null); feed.setMessage(null);
feed.setDisabledUntil(disabledUntil); feed.setDisabledUntil(refreshIntervalCalculator.onFetchSuccess(fetchedFeed));
handlePubSub(feed, fetchedFeed.getFeed()); handlePubSub(feed, fetchedFeed.getFeed());
context.setEntries(entries); context.setEntries(entries);
@@ -104,12 +98,17 @@ public class FeedRefreshWorker implements Managed {
} catch (NotModifiedException e) { } catch (NotModifiedException e) {
log.debug("Feed not modified : {} - {}", feed.getUrl(), e.getMessage()); log.debug("Feed not modified : {} - {}", feed.getUrl(), e.getMessage());
if (config.getApplicationSettings().getHeavyLoad()) {
disabledUntil = FeedUtils.buildDisabledUntil(feed.getLastEntryDate(), feed.getAverageEntryInterval(), disabledUntil);
}
feed.setErrorCount(0); feed.setErrorCount(0);
feed.setMessage(e.getMessage()); feed.setMessage(e.getMessage());
feed.setDisabledUntil(disabledUntil); feed.setDisabledUntil(refreshIntervalCalculator.onFeedNotModified(feed));
if (e.getNewLastModifiedHeader() != null) {
feed.setLastModifiedHeader(e.getNewLastModifiedHeader());
}
if (e.getNewEtagHeader() != null) {
feed.setEtagHeader(e.getNewEtagHeader());
}
queues.giveBack(feed); queues.giveBack(feed);
} catch (Exception e) { } catch (Exception e) {
@@ -118,7 +117,7 @@ public class FeedRefreshWorker implements Managed {
feed.setErrorCount(feed.getErrorCount() + 1); feed.setErrorCount(feed.getErrorCount() + 1);
feed.setMessage(message); feed.setMessage(message);
feed.setDisabledUntil(FeedUtils.buildDisabledUntil(feed.getErrorCount())); feed.setDisabledUntil(refreshIntervalCalculator.onFetchError(feed));
queues.giveBack(feed); queues.giveBack(feed);
} }

View File

@@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -20,7 +19,6 @@ import org.ahocorasick.trie.Trie.TrieBuilder;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
@@ -359,57 +357,6 @@ public class FeedUtils {
return sb.toString(); return sb.toString();
} }
/**
* When there was an error fetching the feed
*
*/
public static Date buildDisabledUntil(int errorCount) {
Date now = new Date();
int retriesBeforeDisable = 3;
if (errorCount >= retriesBeforeDisable) {
int disabledHours = errorCount - retriesBeforeDisable + 1;
disabledHours = Math.min(24 * 7, disabledHours);
return DateUtils.addHours(now, disabledHours);
}
return now;
}
/**
* When the feed was refreshed successfully
*/
public static Date buildDisabledUntil(Date publishedDate, Long averageEntryInterval, Date defaultRefreshInterval) {
Date now = new Date();
if (publishedDate == null) {
// feed with no entries, recheck in 24 hours
return DateUtils.addHours(now, 24);
} else if (publishedDate.before(DateUtils.addMonths(now, -1))) {
// older than a month, recheck in 24 hours
return DateUtils.addHours(now, 24);
} else if (publishedDate.before(DateUtils.addDays(now, -14))) {
// older than two weeks, recheck in 12 hours
return DateUtils.addHours(now, 12);
} else if (publishedDate.before(DateUtils.addDays(now, -7))) {
// older than a week, recheck in 6 hours
return DateUtils.addHours(now, 6);
} else if (averageEntryInterval != null) {
// use average time between entries to decide when to refresh next, divided by factor
int factor = 2;
// not more than 6 hours
long date = Math.min(DateUtils.addHours(now, 6).getTime(), now.getTime() + averageEntryInterval / factor);
// not less than default refresh interval
date = Math.max(defaultRefreshInterval.getTime(), date);
return new Date(date);
} else {
// unknown case, recheck in 24 hours
return DateUtils.addHours(now, 24);
}
}
public static Long averageTimeBetweenEntries(List<FeedEntry> entries) { public static Long averageTimeBetweenEntries(List<FeedEntry> entries) {
if (entries.isEmpty() || entries.size() == 1) { if (entries.isEmpty() || entries.size() == 1) {
return null; return null;

View File

@@ -76,7 +76,7 @@ public class FeedSubscriptionService {
sub.setTitle(FeedUtils.truncate(title, 128)); sub.setTitle(FeedUtils.truncate(title, 128));
feedSubscriptionDAO.saveOrUpdate(sub); feedSubscriptionDAO.saveOrUpdate(sub);
queues.add(feed, false); queues.add(feed, true);
cache.invalidateUserRootCategory(user); cache.invalidateUserRootCategory(user);
return sub.getId(); return sub.getId();
} }

View File

@@ -19,6 +19,9 @@ public class Category implements Serializable {
@ApiModelProperty(value = "parent category id") @ApiModelProperty(value = "parent category id")
private String parentId; private String parentId;
@ApiModelProperty(value = "parent category name")
private String parentName;
@ApiModelProperty(value = "category id", required = true) @ApiModelProperty(value = "category id", required = true)
private String name; private String name;

View File

@@ -95,7 +95,7 @@ public class CategoryREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get category entries", notes = "Get a list of category entries", response = Entries.class) @ApiOperation(value = "Get category entries", notes = "Get a list of category entries", response = Entries.class)
@Timed @Timed
public Response getCategoryEntries(@ApiParam(hidden = true) @SecurityCheck User user, public Response getCategoryEntries(@ApiParam(hidden = true) @SecurityCheck(apiKeyAllowed = true) User user,
@ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id, @ApiParam(value = "id of the category, 'all' or 'starred'", required = true) @QueryParam("id") String id,
@ApiParam( @ApiParam(
value = "all entries or only unread ones", value = "all entries or only unread ones",
@@ -413,7 +413,7 @@ public class CategoryREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get unread count for feed subscriptions", response = UnreadCount.class, responseContainer = "List") @ApiOperation(value = "Get unread count for feed subscriptions", response = UnreadCount.class, responseContainer = "List")
@Timed @Timed
public Response getUnreadCount(@ApiParam(hidden = true) @SecurityCheck User user) { public Response getUnreadCount(@ApiParam(hidden = true) @SecurityCheck(apiKeyAllowed = true) User user) {
Map<Long, UnreadCount> unreadCount = feedSubscriptionService.getUnreadCount(user); Map<Long, UnreadCount> unreadCount = feedSubscriptionService.getUnreadCount(user);
return Response.ok(Lists.newArrayList(unreadCount.values())).build(); return Response.ok(Lists.newArrayList(unreadCount.values())).build();
} }
@@ -454,17 +454,13 @@ public class CategoryREST {
child.setPosition(c.getPosition()); child.setPosition(c.getPosition());
if (c.getParent() != null && c.getParent().getId() != null) { if (c.getParent() != null && c.getParent().getId() != null) {
child.setParentId(String.valueOf(c.getParent().getId())); child.setParentId(String.valueOf(c.getParent().getId()));
child.setParentName(c.getParent().getName());
} }
child.setExpanded(!c.isCollapsed()); child.setExpanded(!c.isCollapsed());
category.getChildren().add(child); category.getChildren().add(child);
} }
} }
Collections.sort(category.getChildren(), new Comparator<Category>() { Collections.sort(category.getChildren(), (o1, o2) -> ObjectUtils.compare(o1.getPosition(), o2.getPosition()));
@Override
public int compare(Category o1, Category o2) {
return ObjectUtils.compare(o1.getPosition(), o2.getPosition());
}
});
for (FeedSubscription subscription : subscriptions) { for (FeedSubscription subscription : subscriptions) {
if (id == null && subscription.getCategory() == null if (id == null && subscription.getCategory() == null
@@ -474,12 +470,8 @@ public class CategoryREST {
category.getFeeds().add(sub); category.getFeeds().add(sub);
} }
} }
Collections.sort(category.getFeeds(), new Comparator<Subscription>() { Collections.sort(category.getFeeds(), (o1, o2) -> ObjectUtils.compare(o1.getPosition(), o2.getPosition()));
@Override
public int compare(Subscription o1, Subscription o2) {
return ObjectUtils.compare(o1.getPosition(), o2.getPosition());
}
});
return category; return category;
} }

View File

@@ -132,7 +132,7 @@ public class FeedREST {
@UnitOfWork @UnitOfWork
@ApiOperation(value = "Get feed entries", notes = "Get a list of feed entries", response = Entries.class) @ApiOperation(value = "Get feed entries", notes = "Get a list of feed entries", response = Entries.class)
@Timed @Timed
public Response getFeedEntries(@ApiParam(hidden = true) @SecurityCheck User user, public Response getFeedEntries(@ApiParam(hidden = true) @SecurityCheck(apiKeyAllowed = true) User user,
@ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id, @ApiParam(value = "id of the feed", required = true) @QueryParam("id") String id,
@ApiParam( @ApiParam(
value = "all entries or only unread ones", value = "all entries or only unread ones",

View File

@@ -9,16 +9,26 @@ import org.eclipse.jetty.server.session.FileSessionDataStore;
import org.eclipse.jetty.server.session.SessionCache; import org.eclipse.jetty.server.session.SessionCache;
import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.server.session.SessionHandler;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.dropwizard.util.Duration; import io.dropwizard.util.Duration;
public class SessionHandlerFactory { public class SessionHandlerFactory {
@JsonProperty
private String path = "sessions"; private String path = "sessions";
@JsonProperty
private Duration cookieMaxAge = Duration.days(30); private Duration cookieMaxAge = Duration.days(30);
@JsonProperty
private Duration cookieRefreshAge = Duration.days(1); private Duration cookieRefreshAge = Duration.days(1);
@JsonProperty
private Duration maxInactiveInterval = Duration.days(30); private Duration maxInactiveInterval = Duration.days(30);
@JsonProperty
private Duration savePeriod = Duration.minutes(5); private Duration savePeriod = Duration.minutes(5);
public SessionHandler build() { public SessionHandler build() {

View File

@@ -9,7 +9,7 @@ import com.commafeed.backend.model.User;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor() @RequiredArgsConstructor
public class SessionHelper { public class SessionHelper {
private static final String SESSION_KEY_USER = "user"; private static final String SESSION_KEY_USER = "user";
@@ -18,15 +18,18 @@ public class SessionHelper {
public Optional<User> getLoggedInUser() { public Optional<User> getLoggedInUser() {
Optional<HttpSession> session = getSession(false); Optional<HttpSession> session = getSession(false);
if (session.isPresent()) { if (session.isPresent()) {
User user = (User) session.get().getAttribute(SESSION_KEY_USER); return getLoggedInUser(session.get());
return Optional.ofNullable(user);
} }
return Optional.empty(); return Optional.empty();
} }
public static Optional<User> getLoggedInUser(HttpSession session) {
User user = (User) session.getAttribute(SESSION_KEY_USER);
return Optional.ofNullable(user);
}
public void setLoggedInUser(User user) { public void setLoggedInUser(User user) {
Optional<HttpSession> session = getSession(true); Optional<HttpSession> session = getSession(true);
session.get().setAttribute(SESSION_KEY_USER, user); session.get().setAttribute(SESSION_KEY_USER, user);

View File

@@ -0,0 +1,42 @@
package com.commafeed.frontend.ws;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
import com.commafeed.backend.model.User;
import com.commafeed.frontend.session.SessionHelper;
import lombok.RequiredArgsConstructor;
@Singleton
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
public class WebSocketConfigurator extends Configurator {
public static final String SESSIONKEY_USERID = "userId";
private final WebSocketSessions webSocketSessions;
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
if (httpSession != null) {
Optional<User> user = SessionHelper.getLoggedInUser(httpSession);
if (user.isPresent()) {
config.getUserProperties().put(SESSIONKEY_USERID, user.get().getId());
}
}
}
@SuppressWarnings("unchecked")
@Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
return (T) new WebSocketEndpoint(webSocketSessions);
}
}

View File

@@ -0,0 +1,61 @@
package com.commafeed.frontend.ws;
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Singleton
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
public class WebSocketEndpoint extends Endpoint {
private final WebSocketSessions sessions;
@Override
public void onOpen(Session session, EndpointConfig config) {
Long userId = (Long) config.getUserProperties().get(WebSocketConfigurator.SESSIONKEY_USERID);
if (userId == null) {
reject(session);
} else {
log.debug("created websocket session for user {}", userId);
sessions.add(userId, session);
}
// converting this anonymous inner class to a lambda causes the following error when a message is sent from the client
// Unable to find decoder for type <javax.websocket.MessageHandler$Whole>
// this error is only visible when registering a listener to ws.onclose on the client
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
if ("ping".equals(message)) {
session.getAsyncRemote().sendText("pong");
}
}
});
}
private void reject(Session session) {
try {
session.close(new CloseReason(CloseCodes.VIOLATED_POLICY, "unauthorized"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void onClose(Session session, CloseReason reason) {
sessions.remove(session);
}
}

View File

@@ -0,0 +1,14 @@
package com.commafeed.frontend.ws;
import com.commafeed.backend.model.FeedSubscription;
import lombok.experimental.UtilityClass;
@UtilityClass
public class WebSocketMessageBuilder {
public static final String newFeedEntries(FeedSubscription subscription) {
return String.format("%s:%s", "new-feed-entries", subscription.getId());
}
}

View File

@@ -0,0 +1,44 @@
package com.commafeed.frontend.ws;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javax.inject.Singleton;
import javax.websocket.Session;
import com.commafeed.backend.model.User;
import lombok.extern.slf4j.Slf4j;
@Singleton
@Slf4j
public class WebSocketSessions {
// a user may have multiple sessions (two tabs, on mobile, ...)
private final Map<Long, Set<Session>> sessions = new ConcurrentHashMap<>();
public void add(Long userId, Session session) {
sessions.computeIfAbsent(userId, v -> ConcurrentHashMap.newKeySet()).add(session);
}
public void remove(Session session) {
sessions.values().forEach(v -> v.removeIf(e -> e.equals(session)));
}
public void sendMessage(User user, String text) {
Set<Session> userSessions = sessions.entrySet()
.stream()
.filter(e -> e.getKey().equals(user.getId()))
.flatMap(e -> e.getValue().stream())
.collect(Collectors.toSet());
log.debug("sending '{}' to {} users via websocket", text, userSessions.size());
for (Session userSession : userSessions) {
if (userSession.isOpen()) {
userSession.getAsyncRemote().sendText(text);
}
}
}
}

View File

@@ -0,0 +1,60 @@
package com.commafeed.backend.feed;
import java.io.IOException;
import java.util.Date;
import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.HttpGetter.NotModifiedException;
import com.commafeed.backend.urlprovider.FeedURLProvider;
import com.rometools.rome.io.FeedException;
@ExtendWith(MockitoExtension.class)
class FeedFetcherTest {
@Mock
private FeedParser parser;
@Mock
private HttpGetter getter;
@Mock
private Set<FeedURLProvider> urlProviders;
private FeedFetcher fetcher;
@BeforeEach
void init() {
fetcher = new FeedFetcher(parser, getter, urlProviders);
}
@Test
void updatesHeaderWhenContentDitNotChange() throws FeedException, IOException, NotModifiedException {
String url = "https://aaa.com";
String lastModified = "last-modified-1";
String etag = "etag-1";
byte[] content = "content".getBytes();
String lastContentHash = DigestUtils.sha1Hex(content);
Mockito.when(getter.getBinary(url, lastModified, etag, 20000))
.thenReturn(new HttpResult(content, "content-type", "last-modified-2", "etag-2", 20, null));
NotModifiedException e = Assertions.assertThrows(NotModifiedException.class,
() -> fetcher.fetch(url, false, lastModified, etag, new Date(), lastContentHash));
Assertions.assertEquals("last-modified-2", e.getNewLastModifiedHeader());
Assertions.assertEquals("etag-2", e.getNewEtagHeader());
}
}

10
pom.xml
View File

@@ -28,6 +28,16 @@
</profile> </profile>
</profiles> </profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
</plugins>
</build>
<modules> <modules>
<module>commafeed-client</module> <module>commafeed-client</module>
<module>commafeed-server</module> <module>commafeed-server</module>