mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f421ec3b0 | ||
|
|
69fb11eee0 | ||
|
|
ffbb85df43 | ||
|
|
a4e78c4e0d | ||
|
|
6d4b0cbdef | ||
|
|
ea4b120a85 | ||
|
|
5c2454c331 | ||
|
|
4ff46965c4 | ||
|
|
33e3f7ea3c | ||
|
|
347fc4f2c8 | ||
|
|
2b4ff4a8a5 | ||
|
|
f7d34983e0 | ||
|
|
3271d69fcb | ||
|
|
7ea24b21f8 | ||
|
|
b2b608e8c3 | ||
|
|
e44ea5bc96 | ||
|
|
fa58b1e53f | ||
|
|
9466bc544c | ||
|
|
9e65f5726c | ||
|
|
fc2c3740a0 | ||
|
|
2095a6512b | ||
|
|
a461a72224 | ||
|
|
f9e7653901 |
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -18,6 +18,7 @@ jobs:
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
distribution: "temurin"
|
||||
cache: "maven"
|
||||
- name: Build with Maven
|
||||
run: mvn --batch-mode --update-snapshots verify
|
||||
- uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -70,8 +70,6 @@
|
||||
"groups": [
|
||||
"useLocation",
|
||||
"useParams",
|
||||
"useStyles",
|
||||
"useMantineTheme",
|
||||
"useState",
|
||||
"useAppSelector",
|
||||
"useAppDispatch",
|
||||
|
||||
4481
commafeed-client/package-lock.json
generated
4481
commafeed-client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "npm run i18n:compile && tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
@@ -17,60 +17,63 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@fontsource/open-sans": "^4.5.13",
|
||||
"@lingui/core": "^3.14.0",
|
||||
"@lingui/macro": "^3.14.0",
|
||||
"@lingui/react": "^3.14.0",
|
||||
"@mantine/core": "^5.6.3",
|
||||
"@mantine/form": "^5.6.3",
|
||||
"@mantine/hooks": "^5.6.3",
|
||||
"@mantine/modals": "^5.6.3",
|
||||
"@mantine/notifications": "^5.6.3",
|
||||
"@mantine/spotlight": "^5.6.3",
|
||||
"@reduxjs/toolkit": "^1.8.6",
|
||||
"axios": "^1.1.3",
|
||||
"dayjs": "^1.11.6",
|
||||
"@fontsource/open-sans": "^4.5.14",
|
||||
"@lingui/core": "^3.17.0",
|
||||
"@lingui/macro": "^3.17.0",
|
||||
"@lingui/react": "^3.17.0",
|
||||
"@mantine/core": "^5.10.3",
|
||||
"@mantine/form": "^5.10.3",
|
||||
"@mantine/hooks": "^5.10.3",
|
||||
"@mantine/modals": "^5.10.3",
|
||||
"@mantine/notifications": "^5.10.3",
|
||||
"@mantine/spotlight": "^5.10.3",
|
||||
"@reduxjs/toolkit": "^1.9.2",
|
||||
"axios": "^1.3.2",
|
||||
"dayjs": "^1.11.7",
|
||||
"interweave": "^13.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"make-plural": "^7.1.0",
|
||||
"make-plural": "^7.2.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"react": "^18.2.0",
|
||||
"react-async-hook": "^4.0.0",
|
||||
"react-contexify": "^6.0.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.6.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-infinite-scroller": "^1.2.6",
|
||||
"react-redux": "^8.0.4",
|
||||
"react-router-dom": "^6.4.2",
|
||||
"swagger-ui-react": "^4.15.2",
|
||||
"tinycon": "^0.6.8"
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-swipeable": "^7.0.0",
|
||||
"swagger-ui-react": "^4.15.5",
|
||||
"tinycon": "^0.6.8",
|
||||
"websocket-heartbeat-js": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lingui/cli": "^3.14.0",
|
||||
"@types/eslint": "^8.4.8",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/mousetrap": "^1.6.10",
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/react-dom": "^18.0.8",
|
||||
"@lingui/cli": "^3.17.0",
|
||||
"@types/eslint": "^8.21.0",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/mousetrap": "^1.6.11",
|
||||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/react-infinite-scroller": "^1.2.3",
|
||||
"@types/swagger-ui-react": "^4.11.0",
|
||||
"@types/tinycon": "^0.6.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||
"@typescript-eslint/parser": "^5.41.0",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"eslint": "^8.26.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"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-plugin-hooks": "^0.4.3",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.7.1",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"typescript": "^4.8.4",
|
||||
"vite": "^3.2.0",
|
||||
"prettier": "^2.8.3",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.1",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-tsconfig-paths": "^3.5.2",
|
||||
"vitest": "^0.24.3",
|
||||
"vitest-mock-extended": "^1.0.3"
|
||||
"vite-tsconfig-paths": "^4.0.5",
|
||||
"vitest": "^0.28.4",
|
||||
"vitest-mock-extended": "^1.0.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ export const client = {
|
||||
getEntries: (req: GetEntriesPaginatedRequest) => axiosInstance.get<Entries>("feed/entries", { params: req }),
|
||||
markEntries: (req: MarkRequest) => axiosInstance.post("feed/mark", req),
|
||||
fetchFeed: (req: FeedInfoRequest) => axiosInstance.post<FeedInfo>("feed/fetch", req),
|
||||
refreshAll: () => axiosInstance.get("feed/refreshAll"),
|
||||
subscribe: (req: SubscribeRequest) => axiosInstance.post<number>("feed/subscribe", req),
|
||||
unsubscribe: (req: IDRequest) => axiosInstance.post("feed/unsubscribe", req),
|
||||
importOpml: (req: File) => {
|
||||
|
||||
@@ -128,6 +128,7 @@ export const markAllEntries = createAsyncThunk<void, { sourceType: EntrySourceTy
|
||||
async (arg, thunkApi) => {
|
||||
const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries
|
||||
await endpoint(arg.req)
|
||||
thunkApi.dispatch(reloadEntries())
|
||||
thunkApi.dispatch(reloadTree())
|
||||
}
|
||||
)
|
||||
|
||||
@@ -51,3 +51,16 @@ export const scrollToWithCallback = ({
|
||||
|
||||
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,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ import { useState } from "react"
|
||||
import { TbPhoto } from "react-icons/tb"
|
||||
|
||||
interface ImageWithPlaceholderWhileLoadingProps {
|
||||
src?: string
|
||||
alt?: string
|
||||
src: string
|
||||
alt: string
|
||||
title?: string
|
||||
width?: number
|
||||
height?: number | "auto"
|
||||
placeholderWidth?: number
|
||||
placeholderHeight?: number
|
||||
placeholderBackgroundColor?: string
|
||||
placeholderIconSize?: number
|
||||
placeholderIconColor?: string
|
||||
}
|
||||
|
||||
const useStyles = createStyles((theme, props: ImageWithPlaceholderWhileLoadingProps) => ({
|
||||
@@ -17,8 +20,8 @@ const useStyles = createStyles((theme, props: ImageWithPlaceholderWhileLoadingPr
|
||||
width: props.placeholderWidth ?? 400,
|
||||
height: props.placeholderHeight ?? 600,
|
||||
maxWidth: "100%",
|
||||
color: theme.fn.variant({ color: theme.primaryColor, variant: "subtle" }).color,
|
||||
backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[1],
|
||||
color: props.placeholderIconColor ?? theme.fn.variant({ color: theme.primaryColor, variant: "subtle" }).color,
|
||||
backgroundColor: props.placeholderBackgroundColor ?? (theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[1]),
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -32,7 +35,7 @@ export function ImageWithPlaceholderWhileLoading(props: ImageWithPlaceholderWhil
|
||||
<Box>
|
||||
<Center className={classes.placeholder}>
|
||||
<div>
|
||||
<TbPhoto size={48} />
|
||||
<TbPhoto size={props.placeholderIconSize ?? 48} />
|
||||
</div>
|
||||
</Center>
|
||||
</Box>
|
||||
|
||||
@@ -95,6 +95,10 @@ export function KeyboardShortcutsHelp() {
|
||||
</td>
|
||||
<td>
|
||||
<Kbd>B</Kbd>
|
||||
<span>, </span>
|
||||
<Kbd>
|
||||
<Trans>Middle click</Trans>
|
||||
</Kbd>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -103,6 +107,8 @@ export function KeyboardShortcutsHelp() {
|
||||
</td>
|
||||
<td>
|
||||
<Kbd>M</Kbd>
|
||||
<span>, </span>
|
||||
<Trans>Swipe header to the right</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -143,6 +149,26 @@ export function KeyboardShortcutsHelp() {
|
||||
<Kbd>U</Kbd>
|
||||
</td>
|
||||
</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>
|
||||
<td>
|
||||
<Trans>Show keyboard shortcut help</Trans>
|
||||
|
||||
@@ -29,7 +29,9 @@ const transform: TransformCallback = node => {
|
||||
if (node.tagName === "IMG") {
|
||||
// show placeholders for loading img tags, this allows the entry to have its final height immediately
|
||||
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 nodeWidth = node.getAttribute("width")
|
||||
const nodeHeight = node.getAttribute("height")
|
||||
@@ -40,6 +42,7 @@ const transform: TransformCallback = node => {
|
||||
height,
|
||||
maxWidth: Constants.layout.entryMaxWidth,
|
||||
})
|
||||
|
||||
return (
|
||||
<ImageWithPlaceholderWhileLoading
|
||||
src={src}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TypographyStylesProvider } from "@mantine/core"
|
||||
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 hasAudio = props.enclosureType && props.enclosureType.indexOf("audio") === 0
|
||||
const hasImage = props.enclosureType && props.enclosureType.indexOf("image") === 0
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "app/slices/entries"
|
||||
import { redirectToRootCategory } from "app/slices/redirect"
|
||||
import { useAppDispatch, useAppSelector } from "app/store"
|
||||
import { openLinkInBackgroundTab } from "app/utils"
|
||||
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
|
||||
import { Loader } from "components/Loader"
|
||||
import { useMousetrap } from "hooks/useMousetrap"
|
||||
@@ -211,15 +212,7 @@ export function FeedEntries() {
|
||||
useMousetrap("b", () => {
|
||||
// simulate ctrl+click to open tab in background
|
||||
if (!selectedEntry) return
|
||||
const a = document.createElement("a")
|
||||
a.href = selectedEntry.url
|
||||
a.rel = "noreferrer"
|
||||
a.dispatchEvent(
|
||||
new MouseEvent("click", {
|
||||
ctrlKey: true,
|
||||
metaKey: true,
|
||||
})
|
||||
)
|
||||
openLinkInBackgroundTab(selectedEntry.url)
|
||||
})
|
||||
useMousetrap("m", () => {
|
||||
// toggle read status
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Anchor, Box, createStyles, Divider, Paper } from "@mantine/core"
|
||||
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 React from "react"
|
||||
import { useSwipeable } from "react-swipeable"
|
||||
import { FeedEntryBody } from "./FeedEntryBody"
|
||||
import { FeedEntryCompactHeader } from "./FeedEntryCompactHeader"
|
||||
import { FeedEntryContextMenu, useFeedEntryContextMenu } from "./FeedEntryContextMenu"
|
||||
import { FeedEntryFooter } from "./FeedEntryFooter"
|
||||
import { FeedEntryHeader } from "./FeedEntryHeader"
|
||||
|
||||
@@ -15,19 +18,25 @@ interface FeedEntryProps {
|
||||
onHeaderClick: (e: React.MouseEvent) => void
|
||||
}
|
||||
|
||||
const useStyles = createStyles((theme, props: FeedEntryProps) => {
|
||||
const useStyles = createStyles((theme, props: FeedEntryProps & { compact: boolean }) => {
|
||||
let backgroundColor
|
||||
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"
|
||||
|
||||
let marginY = theme.spacing.xs
|
||||
if (props.compact) marginY = 6
|
||||
|
||||
let mobileMarginY = 6
|
||||
if (props.compact) mobileMarginY = 4
|
||||
|
||||
const styles = {
|
||||
paper: {
|
||||
backgroundColor,
|
||||
marginTop: theme.spacing.xs,
|
||||
marginBottom: theme.spacing.xs,
|
||||
marginTop: marginY,
|
||||
marginBottom: marginY,
|
||||
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
||||
marginTop: "6px",
|
||||
marginBottom: "6px",
|
||||
marginTop: mobileMarginY,
|
||||
marginBottom: mobileMarginY,
|
||||
},
|
||||
},
|
||||
body: {
|
||||
@@ -43,12 +52,25 @@ const useStyles = createStyles((theme, props: FeedEntryProps) => {
|
||||
})
|
||||
|
||||
export function FeedEntry(props: FeedEntryProps) {
|
||||
const { classes } = useStyles(props)
|
||||
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 (
|
||||
<Paper withBorder className={classes.paper}>
|
||||
<Paper withBorder radius={borderRadius} className={classes.paper}>
|
||||
<Anchor
|
||||
variant="text"
|
||||
href={props.entry.url}
|
||||
@@ -56,21 +78,24 @@ export function FeedEntry(props: FeedEntryProps) {
|
||||
rel="noreferrer"
|
||||
onClick={props.onHeaderClick}
|
||||
onAuxClick={props.onHeaderClick}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<Box p="xs">
|
||||
<Box p={spacing} {...swipeHandlers}>
|
||||
{compactHeader && <FeedEntryCompactHeader entry={props.entry} />}
|
||||
{!compactHeader && <FeedEntryHeader entry={props.entry} expanded={props.expanded} />}
|
||||
</Box>
|
||||
</Anchor>
|
||||
{props.expanded && (
|
||||
<Box px="xs" pb="xs">
|
||||
<Box px={spacing} pb={spacing}>
|
||||
<Box className={classes.body} sx={{ direction: props.entry.rtl ? "rtl" : "ltr" }}>
|
||||
<FeedEntryBody entry={props.entry} />
|
||||
</Box>
|
||||
<Divider variant="dashed" my="xs" />
|
||||
<Divider variant="dashed" my={spacing} />
|
||||
<FeedEntryFooter entry={props.entry} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<FeedEntryContextMenu entry={props.entry} />
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function FeedEntryBody(props: FeedEntryBodyProps) {
|
||||
<Box>
|
||||
<Content content={props.entry.content} />
|
||||
</Box>
|
||||
{props.entry.enclosureUrl && (
|
||||
{props.entry.enclosureType && props.entry.enclosureUrl && (
|
||||
<Box pt="md">
|
||||
<Enclosure enclosureType={props.entry.enclosureType} enclosureUrl={props.entry.enclosureUrl} />
|
||||
</Box>
|
||||
|
||||
@@ -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 { RelativeDate } from "components/RelativeDate"
|
||||
import { OnDesktop } from "components/responsive/OnDesktop"
|
||||
import { FeedEntryTitle } from "./FeedEntryTitle"
|
||||
import { FeedFavicon } from "./FeedFavicon"
|
||||
|
||||
export interface FeedEntryHeaderProps {
|
||||
entry: Entry
|
||||
@@ -37,7 +38,7 @@ export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
|
||||
return (
|
||||
<Box className={classes.wrapper}>
|
||||
<Box>
|
||||
<Image withPlaceholder src={props.entry.iconUrl} alt="feed icon" width={18} height={18} />
|
||||
<FeedFavicon url={props.entry.iconUrl} />
|
||||
</Box>
|
||||
<OnDesktop>
|
||||
<Text color="dimmed" className={classes.feedName}>
|
||||
|
||||
127
commafeed-client/src/components/content/FeedEntryContextMenu.tsx
Normal file
127
commafeed-client/src/components/content/FeedEntryContextMenu.tsx
Normal 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 }
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { useAppDispatch, useAppSelector } from "app/store"
|
||||
import { Entry } from "app/types"
|
||||
import { ActionButton } from "components/ActionButtton"
|
||||
import { ButtonToolbar } from "components/ButtonToolbar"
|
||||
import { throttle } from "lodash"
|
||||
import { useEffect, useState } from "react"
|
||||
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbShare, TbStar, TbStarOff, TbTag } from "react-icons/tb"
|
||||
import { ShareButtons } from "./ShareButtons"
|
||||
@@ -22,8 +23,7 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
||||
const mobile = !useMediaQuery(`(min-width: ${Constants.layout.mobileBreakpoint}px)`)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const showSharingButtons =
|
||||
sharingSettings && (Object.values(sharingSettings) as Array<typeof sharingSettings[keyof typeof sharingSettings]>).some(v => v)
|
||||
const showSharingButtons = sharingSettings && Object.values(sharingSettings).some(v => v)
|
||||
|
||||
const readStatusButtonClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))
|
||||
const onTagsChange = (values: string[]) =>
|
||||
@@ -38,8 +38,10 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
||||
const scrollArea = document.getElementById(Constants.dom.mainScrollAreaId)
|
||||
|
||||
const listener = () => setScrollPosition(scrollArea ? scrollArea.scrollTop : 0)
|
||||
scrollArea?.addEventListener("scroll", listener)
|
||||
return () => scrollArea?.removeEventListener("scroll", listener)
|
||||
const throttledListener = throttle(listener, 100)
|
||||
|
||||
scrollArea?.addEventListener("scroll", throttledListener)
|
||||
return () => scrollArea?.removeEventListener("scroll", throttledListener)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
@@ -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 { RelativeDate } from "components/RelativeDate"
|
||||
import { FeedEntryTitle } from "./FeedEntryTitle"
|
||||
import { FeedFavicon } from "./FeedFavicon"
|
||||
|
||||
export interface FeedEntryHeaderProps {
|
||||
entry: Entry
|
||||
@@ -33,7 +34,7 @@ export function FeedEntryHeader(props: FeedEntryHeaderProps) {
|
||||
</Box>
|
||||
<Box className={classes.headerSubtext}>
|
||||
<Box mr={6}>
|
||||
<Image withPlaceholder src={props.entry.iconUrl} alt="feed icon" width={18} height={18} />
|
||||
<FeedFavicon url={props.entry.iconUrl} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text color="dimmed">{props.entry.feedName}</Text>
|
||||
|
||||
22
commafeed-client/src/components/content/FeedFavicon.tsx
Normal file
22
commafeed-client/src/components/content/FeedFavicon.tsx
Normal 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"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { t } from "@lingui/macro"
|
||||
import { ActionIcon, Center, Divider, Indicator, Popover, TextInput } from "@mantine/core"
|
||||
import { useForm } from "@mantine/form"
|
||||
import { reloadEntries, search } from "app/slices/entries"
|
||||
import { search } from "app/slices/entries"
|
||||
import { changeReadingMode, changeReadingOrder } from "app/slices/user"
|
||||
import { useAppDispatch, useAppSelector } from "app/store"
|
||||
import { ActionButton } from "components/ActionButtton"
|
||||
@@ -11,6 +11,7 @@ import { useEffect } from "react"
|
||||
import { TbArrowDown, TbArrowUp, TbEye, TbEyeOff, TbRefresh, TbSearch, TbUser, TbX } from "react-icons/tb"
|
||||
import { MarkAllAsReadButton } from "./MarkAllAsReadButton"
|
||||
import { ProfileMenu } from "./ProfileMenu"
|
||||
import { RefreshMenu } from "./RefreshMenu"
|
||||
|
||||
function HeaderDivider() {
|
||||
return <Divider orientation="vertical" />
|
||||
@@ -40,7 +41,7 @@ export function Header() {
|
||||
return (
|
||||
<Center>
|
||||
<ButtonToolbar>
|
||||
<ActionButton icon={<TbRefresh size={iconSize} />} label={t`Refresh`} onClick={() => dispatch(reloadEntries())} />
|
||||
<RefreshMenu control={<ActionButton icon={<TbRefresh size={iconSize} />} label={t`Refresh`} />} />
|
||||
<MarkAllAsReadButton iconSize={iconSize} />
|
||||
|
||||
<HeaderDivider />
|
||||
|
||||
41
commafeed-client/src/components/header/RefreshMenu.tsx
Normal file
41
commafeed-client/src/components/header/RefreshMenu.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { t, Trans } from "@lingui/macro"
|
||||
import { Menu } from "@mantine/core"
|
||||
import { showNotification } from "@mantine/notifications"
|
||||
import { client } from "app/client"
|
||||
import { reloadEntries } from "app/slices/entries"
|
||||
import { useAppDispatch } from "app/store"
|
||||
import { TbRotateClockwise, TbWorldDownload } from "react-icons/tb"
|
||||
|
||||
interface RefreshMenuProps {
|
||||
control: React.ReactElement
|
||||
}
|
||||
|
||||
const iconSize = 16
|
||||
|
||||
export function RefreshMenu(props: RefreshMenuProps) {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Target>{props.control}</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item icon={<TbRotateClockwise size={iconSize} />} onClick={() => dispatch(reloadEntries())}>
|
||||
<Trans>Reload</Trans>
|
||||
</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",
|
||||
})
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trans>Fetch all my feeds now</Trans>
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export function ProfileSettings() {
|
||||
|
||||
const form = useForm<FormData>({
|
||||
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
|
||||
|
||||
@@ -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 { UnreadCount } from "./UnreadCount"
|
||||
|
||||
@@ -49,11 +50,7 @@ export function TreeNode(props: TreeNodeProps) {
|
||||
return (
|
||||
<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)}>
|
||||
{typeof props.icon === "string" ? (
|
||||
<Image withPlaceholder src={props.icon} alt="favicon" width={18} height={18} />
|
||||
) : (
|
||||
props.icon
|
||||
)}
|
||||
{typeof props.icon === "string" ? <FeedFavicon url={props.icon} /> : props.icon}
|
||||
</Box>
|
||||
<Box className={classes.nodeText}>{props.name}</Box>
|
||||
{!props.expanded && (
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
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 { redirectToFeed } from "app/slices/redirect"
|
||||
import { useAppDispatch } from "app/store"
|
||||
import { Subscription } from "app/types"
|
||||
import { FeedFavicon } from "components/content/FeedFavicon"
|
||||
import { useMousetrap } from "hooks/useMousetrap"
|
||||
import { TbSearch } from "react-icons/tb"
|
||||
|
||||
@@ -17,7 +18,7 @@ export function TreeSearch(props: TreeSearchProps) {
|
||||
.sort((f1, f2) => f1.name.localeCompare(f2.name))
|
||||
.map(f => ({
|
||||
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)),
|
||||
}))
|
||||
|
||||
|
||||
24
commafeed-client/src/hooks/useWebSocket.ts
Normal file
24
commafeed-client/src/hooks/useWebSocket.ts
Normal 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])
|
||||
}
|
||||
@@ -29,7 +29,37 @@ import "dayjs/locale/sk"
|
||||
import "dayjs/locale/sv"
|
||||
import "dayjs/locale/tr"
|
||||
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 { messages as arMessages } from "./locales/ar/messages"
|
||||
import { messages as caMessages } from "./locales/ca/messages"
|
||||
@@ -64,7 +94,7 @@ interface Locale {
|
||||
key: string
|
||||
label: string
|
||||
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
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "موجز URL"
|
||||
msgid "Feed name"
|
||||
msgstr "اسم الخلاصة"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "تصفية التعبير"
|
||||
@@ -320,6 +324,10 @@ msgstr "انتقل إلى وثائق API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "اذهب إلى طريقة العرض \"الكل\""
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "الأشياء الجيدة"
|
||||
@@ -348,6 +356,7 @@ msgstr "استيراد"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "في العرض الموسع ، التمرير عبر الإدخالات وضع علامة عليها كمقروءة"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "إبقاء غير مقروءة"
|
||||
@@ -404,6 +413,10 @@ msgstr "تسجيل الدخول"
|
||||
msgid "Logout"
|
||||
msgstr "تسجيل الخروج"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "تعليم الكل كمقروء"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "تعليم كافة الإدخالات كمقروءة"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "وضع علامة كمقروء"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "وضع علامة كمقروءة حتى هنا"
|
||||
@@ -430,6 +445,10 @@ msgstr "وضع علامة كمقروءة حتى هنا"
|
||||
msgid "Metrics"
|
||||
msgstr "المقاييس"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "تحريك الصفحة لأسفل"
|
||||
@@ -515,6 +534,14 @@ msgstr "فتح الإدخال الحالي في علامة تبويب جديدة
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "فتح الإدخال التالي"
|
||||
@@ -581,6 +608,14 @@ msgstr "تحديث"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "مشاركة المواقع"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "إظهار موجز ويب والفئات التي لا تحتوي على إدخالات غير مقروءة"
|
||||
@@ -654,6 +697,7 @@ msgstr "شيء سيء حدث للتو ..."
|
||||
msgid "Space"
|
||||
msgstr "فضاء"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "النجم"
|
||||
@@ -681,6 +725,10 @@ msgstr "الاشتراك في موجز ويب"
|
||||
msgid "Success"
|
||||
msgstr "النجاح"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "التبديل إلى النسق الداكن"
|
||||
@@ -713,6 +761,7 @@ msgstr "جرب CommaFeed باستخدام الحساب التجريبي: تجر
|
||||
msgid "Unread"
|
||||
msgstr "غير مقروءة"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "إلغاء النجم"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "ليس لديك أي اشتراكات حتى الآن. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "الملف مطلوب"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL del canal"
|
||||
msgid "Feed name"
|
||||
msgstr "Nom del canal"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expressió de filtratge"
|
||||
@@ -320,6 +324,10 @@ msgstr "Vés a la documentació de l'API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Vés a la vista Tot"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Bones"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importació"
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Mantenir sense llegir"
|
||||
@@ -404,6 +413,10 @@ msgstr "Inicia sessió"
|
||||
msgid "Logout"
|
||||
msgstr "Tanca sessió"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Marca-ho tot com a llegit"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marqueu totes les entrades com a llegides"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Marca com a llegit"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Marca com a llegit fins aquí"
|
||||
@@ -430,6 +445,10 @@ msgstr "Marca com a llegit fins aquí"
|
||||
msgid "Metrics"
|
||||
msgstr "mètriques"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Mou la pàgina cap avall"
|
||||
@@ -515,6 +534,14 @@ msgstr "Obre l'entrada actual en una pestanya nova al fons"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Obre la següent entrada"
|
||||
@@ -581,6 +608,14 @@ msgstr "Actualitzar"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Els registres estan tancats en aquesta instància de CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Compartir llocs"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Mostra feeds i categories sense entrades no llegides"
|
||||
@@ -654,6 +697,7 @@ msgstr "Acaba de passar una cosa dolenta..."
|
||||
msgid "Space"
|
||||
msgstr "Espai"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Estrella"
|
||||
@@ -681,6 +725,10 @@ msgstr "Subscriu-te al canal"
|
||||
msgid "Success"
|
||||
msgstr "Éxit"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Canvia al tema fosc"
|
||||
@@ -713,6 +761,7 @@ msgstr "Proveu CommaFeed amb el compte de demostració: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Sense llegir"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Desestrellar"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Encara no teniu cap subscripció. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "el fitxer és necessari"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL zdroje"
|
||||
msgid "Feed name"
|
||||
msgstr "Název zdroje"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrování výrazu"
|
||||
@@ -320,6 +324,10 @@ msgstr "Přejděte na dokumentaci API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Přejděte do zobrazení Vše"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Dobroty"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
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é"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Ponechat nepřečtené"
|
||||
@@ -404,6 +413,10 @@ msgstr "Přihlaste se"
|
||||
msgid "Logout"
|
||||
msgstr "Odhlášení"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Označit vše jako přečtené"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Označte všechny položky jako přečtené"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Označit jako přečtené"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Označit jako přečtené až sem"
|
||||
@@ -430,6 +445,10 @@ msgstr "Označit jako přečtené až sem"
|
||||
msgid "Metrics"
|
||||
msgstr "Metriky"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Přesuňte stránku dolů"
|
||||
@@ -515,6 +534,14 @@ msgstr "Otevřít aktuální položku na nové kartě na pozadí"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Otevřete další položku"
|
||||
@@ -581,6 +608,14 @@ msgstr "Obnovit"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "V této instanci CommaFeed jsou registrace uzavřeny"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Stránky pro sdílení"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
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"
|
||||
msgstr "Vesmír"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Hvězda"
|
||||
@@ -681,6 +725,10 @@ msgstr "Přihlaste se k odběru kanálu"
|
||||
msgid "Success"
|
||||
msgstr "Úspěch"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Přepněte na tmavý motiv"
|
||||
@@ -713,6 +761,7 @@ msgstr "Vyzkoušejte CommaFeed s demo účtem: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Nepřečteno"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Odstranit hvězdu"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Zatím nemáte žádné předplatné. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr ""
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL porthiant"
|
||||
msgid "Feed name"
|
||||
msgstr "Enw porthiant"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Hidlo mynegiant"
|
||||
@@ -320,6 +324,10 @@ msgstr "Ewch i'r ddogfennaeth API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Ewch i'r golwg Pawb"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "nwyddau"
|
||||
@@ -348,6 +356,7 @@ msgstr "Mewnforio"
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Cadwch heb ei ddarllen"
|
||||
@@ -404,6 +413,10 @@ msgstr "Mewngofnodi"
|
||||
msgid "Logout"
|
||||
msgstr "Allgofnodi"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Marciwch y cyfan wedi'i ddarllen"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marciwch bob cofnod wedi'i ddarllen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Marciwch ei fod wedi'i ddarllen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Marciwch fel y darllenwyd hyd yma"
|
||||
@@ -430,6 +445,10 @@ msgstr "Marciwch fel y darllenwyd hyd yma"
|
||||
msgid "Metrics"
|
||||
msgstr "metrigau"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Symudwch y dudalen i lawr"
|
||||
@@ -515,6 +534,14 @@ msgstr "Agorwch y cofnod cyfredol mewn tab newydd yn y cefndir"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Agor y cofnod nesaf"
|
||||
@@ -581,6 +608,14 @@ msgstr "Adnewyddu"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Rhannu gwefannau"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Dangos ffrydiau a chategorïau heb unrhyw gofnodion heb eu darllen"
|
||||
@@ -654,6 +697,7 @@ msgstr "Mae rhywbeth drwg newydd ddigwydd ..."
|
||||
msgid "Space"
|
||||
msgstr "Gofod"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "seren"
|
||||
@@ -681,6 +725,10 @@ msgstr "Tanysgrifio i'r porthiant"
|
||||
msgid "Success"
|
||||
msgstr "Llwyddiant"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Newid i thema dywyll"
|
||||
@@ -713,6 +761,7 @@ msgstr "Rhowch gynnig ar CommaFeed gyda'r cyfrif demo: demo / demo"
|
||||
msgid "Unread"
|
||||
msgstr "Heb ei ddarllen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "dad-seren"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Nid oes gennych unrhyw danysgrifiadau eto. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "mae angen y ffeil"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr ""
|
||||
msgid "Feed name"
|
||||
msgstr "Feednavn"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerende udtryk"
|
||||
@@ -320,6 +324,10 @@ msgstr "Gå til API-dokumentationen."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Gå til visningen Alle"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Godbidder"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Forbehold ulæst"
|
||||
@@ -404,6 +413,10 @@ msgstr "Log ind"
|
||||
msgid "Logout"
|
||||
msgstr "Log ud"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Marker alle som læst"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marker alle poster som læst"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Markér som læst"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Markér som læst indtil her"
|
||||
@@ -430,6 +445,10 @@ msgstr "Markér som læst indtil her"
|
||||
msgid "Metrics"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Flyt siden ned"
|
||||
@@ -515,6 +534,14 @@ msgstr "Åbn den aktuelle post i en ny fane i baggrunden"
|
||||
msgid "Open 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
|
||||
msgid "Open next entry"
|
||||
msgstr "Åbn næste post"
|
||||
@@ -581,6 +608,14 @@ msgstr "Opdater"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registreringer er lukket på denne CommaFeed-instans"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Delingssider"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Vis feeds og kategorier uden ulæste poster"
|
||||
@@ -654,6 +697,7 @@ msgstr "Der er lige sket noget slemt..."
|
||||
msgid "Space"
|
||||
msgstr "Rum"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Stjerne"
|
||||
@@ -681,6 +725,10 @@ msgstr "Abonner på feedet"
|
||||
msgid "Success"
|
||||
msgstr "Succes"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Skift til mørkt tema"
|
||||
@@ -713,6 +761,7 @@ msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Ulæst"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr ""
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Du har ingen abonnementer endnu. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fil er påkrævet"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Feed-URL"
|
||||
msgid "Feed name"
|
||||
msgstr "Feedname"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filterausdruck"
|
||||
@@ -320,6 +324,10 @@ msgstr "Gehen Sie zur API-Dokumentation."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Zur Ansicht Alle wechseln"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Gutes"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importieren"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "In der erweiterten Ansicht werden Einträge beim Scrollen als gelesen markiert"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Ungelesen lassen"
|
||||
@@ -404,6 +413,10 @@ msgstr "Einloggen"
|
||||
msgid "Logout"
|
||||
msgstr "Abmelden"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Alle als gelesen markieren"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Alle Einträge als gelesen markieren"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Als gelesen markieren"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Bis hierhin als gelesen markieren"
|
||||
@@ -430,6 +445,10 @@ msgstr "Bis hierhin als gelesen markieren"
|
||||
msgid "Metrics"
|
||||
msgstr "Metriken"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Seite nach unten verschieben"
|
||||
@@ -515,6 +534,14 @@ msgstr "Aktuellen Eintrag in neuem Tab im Hintergrund öffnen"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Nächsten Eintrag öffnen"
|
||||
@@ -581,6 +608,14 @@ msgstr "Aktualisieren"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Seiten teilen"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Feeds und Kategorien ohne ungelesene Einträge anzeigen"
|
||||
@@ -654,6 +697,7 @@ msgstr "Etwas Schlimmes ist gerade passiert..."
|
||||
msgid "Space"
|
||||
msgstr "Raum"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Stern"
|
||||
@@ -681,6 +725,10 @@ msgstr "Feed abonnieren"
|
||||
msgid "Success"
|
||||
msgstr "Erfolg"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Zum dunklen Design wechseln"
|
||||
@@ -713,6 +761,7 @@ msgstr "Testen Sie CommaFeed mit dem Demokonto: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Ungelesen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Stern entfernen"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Sie haben noch keine Abonnements. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "Datei ist erforderlich"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Feed URL"
|
||||
msgid "Feed name"
|
||||
msgstr "Feed name"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr "Fetch all my feeds now"
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtering expression"
|
||||
@@ -320,6 +324,10 @@ msgstr "Go to the API documentation."
|
||||
msgid "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
|
||||
msgid "Goodies"
|
||||
msgstr "Goodies"
|
||||
@@ -348,6 +356,7 @@ msgstr "Import"
|
||||
msgid "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
|
||||
msgid "Keep unread"
|
||||
msgstr "Keep unread"
|
||||
@@ -404,6 +413,10 @@ msgstr "Log in"
|
||||
msgid "Logout"
|
||||
msgstr "Logout"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr "Long press"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Mark all as read"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Mark all entries as read"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Mark as read"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Mark as read up to here"
|
||||
@@ -430,6 +445,10 @@ msgstr "Mark as read up to here"
|
||||
msgid "Metrics"
|
||||
msgstr "Metrics"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr "Middle click"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Move the page down"
|
||||
@@ -515,6 +534,14 @@ msgstr "Open current entry in a new tab in the background"
|
||||
msgid "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
|
||||
msgid "Open next entry"
|
||||
msgstr "Open next entry"
|
||||
@@ -581,6 +608,14 @@ msgstr "Refresh"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registrations are closed on this CommaFeed instance"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr "Reload"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr "Right click"
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Sharing sites"
|
||||
msgid "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
|
||||
msgid "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"
|
||||
msgstr "Space"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Star"
|
||||
@@ -681,6 +725,10 @@ msgstr "Subscribe to the feed"
|
||||
msgid "Success"
|
||||
msgstr "Success"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr "Swipe header to the right"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "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"
|
||||
msgstr "Unread"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Unstar"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
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/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr "Your feeds have been queued for refresh."
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "file is required"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL de fuente"
|
||||
msgid "Feed name"
|
||||
msgstr "Nombre de alimentación"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expresión de filtrado"
|
||||
@@ -320,6 +324,10 @@ msgstr "Ir a la documentación de la API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Ir a la vista Todo"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "golosinas"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importar"
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Mantener sin leer"
|
||||
@@ -404,6 +413,10 @@ msgstr "Iniciar sesión"
|
||||
msgid "Logout"
|
||||
msgstr "Salir"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Marcar todo como leído"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marcar todas las entradas como leídas"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Marcar como leído"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Marcar como leído hasta aquí"
|
||||
@@ -430,6 +445,10 @@ msgstr "Marcar como leído hasta aquí"
|
||||
msgid "Metrics"
|
||||
msgstr "Métricas"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Mover la página hacia abajo"
|
||||
@@ -515,6 +534,14 @@ msgstr "Abrir la entrada actual en una nueva pestaña en segundo plano"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Abrir siguiente entrada"
|
||||
@@ -581,6 +608,14 @@ msgstr "Actualizar"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Los registros están cerrados en esta instancia de CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Compartir sitios"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Mostrar feeds y categorías sin entradas no leídas"
|
||||
@@ -654,6 +697,7 @@ msgstr "Algo malo acaba de pasar..."
|
||||
msgid "Space"
|
||||
msgstr "Espacio"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "estrella"
|
||||
@@ -681,6 +725,10 @@ msgstr "Suscríbete a la fuente"
|
||||
msgid "Success"
|
||||
msgstr "Éxito"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Cambiar a tema oscuro"
|
||||
@@ -713,6 +761,7 @@ msgstr "Pruebe CommaFeed con la cuenta demo: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "No leído"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Desmarcar"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Todavía no tienes ninguna suscripción. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "archivo requerido"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL فید"
|
||||
msgid "Feed name"
|
||||
msgstr "نام فید"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "بیان فیلتر"
|
||||
@@ -320,6 +324,10 @@ msgstr "به مستندات API بروید."
|
||||
msgid "Go to the All view"
|
||||
msgstr "به نمای All بروید"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "خوبی ها"
|
||||
@@ -348,6 +356,7 @@ msgstr "واردات"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "در نمای بازشده، پیمایش در ورودیها، آنها را به عنوان خوانده شده علامتگذاری میکند"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "خوانده نشده نگه دارید"
|
||||
@@ -404,6 +413,10 @@ msgstr "وارد شوید"
|
||||
msgid "Logout"
|
||||
msgstr "خروج"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "همه را به عنوان خوانده شده علامت گذاری ک
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "همه ورودی ها را به عنوان خوانده شده علامت گذاری کنید"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "علامت گذاری به عنوان خوانده شده"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "تا اینجا به عنوان خوانده شده علامت بزنید"
|
||||
@@ -430,6 +445,10 @@ msgstr "تا اینجا به عنوان خوانده شده علامت بزنی
|
||||
msgid "Metrics"
|
||||
msgstr "متریک"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "صفحه را به پایین ببرید"
|
||||
@@ -515,6 +534,14 @@ msgstr "ورودی فعلی را در یک برگه جدید در پس زمین
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "ورودی بعدی را باز کنید"
|
||||
@@ -581,6 +608,14 @@ msgstr "تازه کردن"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "ثبت نام در این نمونه CommaFeed بسته شده است"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "اشتراک گذاری سایت ها"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "فیدها و دسته ها را بدون ورودی خوانده نشده نشان دهید"
|
||||
@@ -654,6 +697,7 @@ msgstr "اتفاق بدی افتاد..."
|
||||
msgid "Space"
|
||||
msgstr "فضا"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "ستاره"
|
||||
@@ -681,6 +725,10 @@ msgstr "در فید مشترک شوید"
|
||||
msgid "Success"
|
||||
msgstr "موفقیت"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "تغییر به تم تیره"
|
||||
@@ -713,6 +761,7 @@ msgstr "CommaFeed را با حساب آزمایشی امتحان کنید: دم
|
||||
msgid "Unread"
|
||||
msgstr "خوانده نشده"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr ""
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "شما هنوز هیچ اشتراکی ندارید. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "فایل مورد نیاز است"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Syötteen URL-osoite"
|
||||
msgid "Feed name"
|
||||
msgstr "Syötteen nimi"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Suodattava lauseke"
|
||||
@@ -320,6 +324,10 @@ msgstr "Siirry API-dokumentaatioon."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Siirry Kaikki-näkymään"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Hyvää"
|
||||
@@ -348,6 +356,7 @@ msgstr "Tuo"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "Merkitse ne luetuiksi laajennetussa näkymässä vierittämällä merkintöjä"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Pidä lukematta"
|
||||
@@ -404,6 +413,10 @@ msgstr "Kirjaudu sisään"
|
||||
msgid "Logout"
|
||||
msgstr "Uloskirjautuminen"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Merkitse kaikki luetuiksi"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Merkitse kaikki merkinnät luetuiksi"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Merkitse luetuksi"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Merkitse luetuksi tähän asti"
|
||||
@@ -430,6 +445,10 @@ msgstr "Merkitse luetuksi tähän asti"
|
||||
msgid "Metrics"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Siirrä sivua alaspäin"
|
||||
@@ -515,6 +534,14 @@ msgstr "Avaa nykyinen merkintä uudella välilehdellä taustalla"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Avaa seuraava merkintä"
|
||||
@@ -581,6 +608,14 @@ msgstr "Päivitä"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Sivustojen jakaminen"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Näytä syötteet ja luokat ilman lukemattomia merkintöjä"
|
||||
@@ -654,6 +697,7 @@ msgstr "Jotain pahaa tapahtui juuri..."
|
||||
msgid "Space"
|
||||
msgstr "Avaruus"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Tähti"
|
||||
@@ -681,6 +725,10 @@ msgstr "Tilaa syöte"
|
||||
msgid "Success"
|
||||
msgstr "Onnistui"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Vaihda tummaan teemaan"
|
||||
@@ -713,6 +761,7 @@ msgstr "Kokeile CommaFeediä demotilillä: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Lukematon"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Poista tähti"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Sinulla ei ole vielä tilauksia. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "tiedosto vaaditaan"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL du flux"
|
||||
msgid "Feed name"
|
||||
msgstr "Nom du flux"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expression de filtrage"
|
||||
@@ -320,6 +324,10 @@ msgstr "Aller à la documentation de l'API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Aller à la catégorie Tout"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Extensions"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importer"
|
||||
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."
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Garder non lu"
|
||||
@@ -404,6 +413,10 @@ msgstr "Connexion"
|
||||
msgid "Logout"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Tout marquer comme lu"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marquer toutes les entrées comme lues"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Marquer comme lu"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Marquer comme lu jusqu'ici"
|
||||
@@ -430,6 +445,10 @@ msgstr "Marquer comme lu jusqu'ici"
|
||||
msgid "Metrics"
|
||||
msgstr "Métriques"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Faites défiler la page vers le bas"
|
||||
@@ -515,6 +534,14 @@ msgstr "Ouvrir l'entrée actuelle dans un nouvel onglet en arrière-plan"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Ouvrir l'entrée suivante"
|
||||
@@ -581,6 +608,14 @@ msgstr "Rafraîchir"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Sites de partage"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
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"
|
||||
msgstr "Espace"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Ajouter aux favoris"
|
||||
@@ -681,6 +725,10 @@ msgstr "S'abonner au flux"
|
||||
msgid "Success"
|
||||
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
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Activer le mode sombre"
|
||||
@@ -713,6 +761,7 @@ msgstr "Essayez CommaFeed avec le compte de démonstration : demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Non lu"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Retirer des favoris"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
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/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fichier requis"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL da fonte"
|
||||
msgid "Feed name"
|
||||
msgstr "Nome do feed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expresión de filtrado"
|
||||
@@ -320,6 +324,10 @@ msgstr "Ir á documentación da API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Ir á vista Todos"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "agasallos"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importación"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "Na vista ampliada, ao desprazarse polas entradas márcaas como lidas"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Manter sen ler"
|
||||
@@ -404,6 +413,10 @@ msgstr "Iniciar sesión"
|
||||
msgid "Logout"
|
||||
msgstr "Pechar sesión"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Marcar todo como lido"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marcar todas as entradas como lidas"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Marcar como lido"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Marcar como lido ata aquí"
|
||||
@@ -430,6 +445,10 @@ msgstr "Marcar como lido ata aquí"
|
||||
msgid "Metrics"
|
||||
msgstr "Métricas"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Move a páxina cara abaixo"
|
||||
@@ -515,6 +534,14 @@ msgstr "Abre a entrada actual nunha nova pestana en segundo plano"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Abrir a seguinte entrada"
|
||||
@@ -581,6 +608,14 @@ msgstr "Actualizar"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Os rexistros están pechados nesta instancia de CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Compartir sitios"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Mostrar fontes e categorías sen entradas sen ler"
|
||||
@@ -654,6 +697,7 @@ msgstr "Algo malo pasou..."
|
||||
msgid "Space"
|
||||
msgstr "Espazo"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "estrela"
|
||||
@@ -681,6 +725,10 @@ msgstr "Subscríbete ao feed"
|
||||
msgid "Success"
|
||||
msgstr "Éxito"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Cambiar ao tema escuro"
|
||||
@@ -713,6 +761,7 @@ msgstr "Proba CommaFeed coa conta de demostración: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Sen ler"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Desestrela"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Aínda non tes ningunha subscrición. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "é necesario o ficheiro"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr ""
|
||||
msgid "Feed name"
|
||||
msgstr "Hírcsatorna neve"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Szűrő kifejezés"
|
||||
@@ -320,6 +324,10 @@ msgstr "Nyissa meg az API dokumentációját."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Lépjen az Összes nézetre"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Jók"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importálás"
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Olvasatlan marad"
|
||||
@@ -404,6 +413,10 @@ msgstr "Jelentkezzen be"
|
||||
msgid "Logout"
|
||||
msgstr "Kijelentkezés"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Minden megjelölése olvasottként"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Minden bejegyzés megjelölése olvasottként"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Megjelölés olvasottként"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Megjelölés idáig olvasottként"
|
||||
@@ -430,6 +445,10 @@ msgstr "Megjelölés idáig olvasottként"
|
||||
msgid "Metrics"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Mozgassa le az oldalt"
|
||||
@@ -515,6 +534,14 @@ msgstr "Az aktuális bejegyzés megnyitása egy új lapon a háttérben"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Következő bejegyzés megnyitása"
|
||||
@@ -581,6 +608,14 @@ msgstr "Frissítés"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Webhelyek megosztása"
|
||||
msgid "Shift"
|
||||
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
|
||||
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"
|
||||
@@ -654,6 +697,7 @@ msgstr "Valami rossz történt..."
|
||||
msgid "Space"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Csillag"
|
||||
@@ -681,6 +725,10 @@ msgstr "Feliratkozás a hírfolyamra"
|
||||
msgid "Success"
|
||||
msgstr "Siker"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
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"
|
||||
msgstr "Olvasatlan"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr ""
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Még nincs előfizetése. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fájl szükséges"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL Umpan"
|
||||
msgid "Feed name"
|
||||
msgstr "Nama umpan"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Memfilter ekspresi"
|
||||
@@ -320,6 +324,10 @@ msgstr "Buka dokumentasi API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Pergi ke tampilan Semua"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Pernak-pernik"
|
||||
@@ -348,6 +356,7 @@ msgstr "Impor"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "Dalam tampilan yang diperluas, menggulir entri menandainya sebagai telah dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Tetap belum dibaca"
|
||||
@@ -404,6 +413,10 @@ msgstr "Masuk"
|
||||
msgid "Logout"
|
||||
msgstr "Keluar"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Tandai semua sebagai telah dibaca"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Tandai semua entri sebagai telah dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Tandai sebagai telah dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Tandai sebagai telah dibaca sampai di sini"
|
||||
@@ -430,6 +445,10 @@ msgstr "Tandai sebagai telah dibaca sampai di sini"
|
||||
msgid "Metrics"
|
||||
msgstr "Metrik"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Pindahkan halaman ke bawah"
|
||||
@@ -515,6 +534,14 @@ msgstr "Buka entri saat ini di tab baru di latar belakang"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Buka entri berikutnya"
|
||||
@@ -581,6 +608,14 @@ msgstr "Segarkan"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Pendaftaran ditutup pada instans CommaFeed ini"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Berbagi situs"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Tampilkan umpan dan kategori tanpa entri yang belum dibaca"
|
||||
@@ -654,6 +697,7 @@ msgstr "Sesuatu yang buruk baru saja terjadi..."
|
||||
msgid "Space"
|
||||
msgstr "Luar Angkasa"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Bintang"
|
||||
@@ -681,6 +725,10 @@ msgstr "Berlangganan umpan"
|
||||
msgid "Success"
|
||||
msgstr "Sukses"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Beralih ke tema gelap"
|
||||
@@ -713,6 +761,7 @@ msgstr "Cobalah CommaFeed dengan akun demo: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Belum Dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Hapus bintang"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Anda belum memiliki langganan. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "file diperlukan"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL feed"
|
||||
msgid "Feed name"
|
||||
msgstr "Nome del feed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Espressione filtrante"
|
||||
@@ -320,6 +324,10 @@ msgstr "Vai alla documentazione dell'API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Vai alla vista Tutto"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Chicche"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importa"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "Nella vista espansa, scorrendo le voci contrassegnale come lette"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Mantieni non letto"
|
||||
@@ -404,6 +413,10 @@ msgstr "Accedi"
|
||||
msgid "Logout"
|
||||
msgstr "Disconnessione"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Contrassegna tutto come letto"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Contrassegna tutte le voci come lette"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Contrassegna come letto"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Contrassegna come letto fino a qui"
|
||||
@@ -430,6 +445,10 @@ msgstr "Contrassegna come letto fino a qui"
|
||||
msgid "Metrics"
|
||||
msgstr "Metriche"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Sposta la pagina in basso"
|
||||
@@ -515,6 +534,14 @@ msgstr "Apri la voce corrente in una nuova scheda in background"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Apri voce successiva"
|
||||
@@ -581,6 +608,14 @@ msgstr "Aggiorna"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Condivisione di siti"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Mostra feed e categorie senza voci non lette"
|
||||
@@ -654,6 +697,7 @@ msgstr "È appena successo qualcosa di brutto..."
|
||||
msgid "Space"
|
||||
msgstr "Spazio"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Stella"
|
||||
@@ -681,6 +725,10 @@ msgstr "Iscriviti al feed"
|
||||
msgid "Success"
|
||||
msgstr "Successo"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Passa al tema scuro"
|
||||
@@ -713,6 +761,7 @@ msgstr "Prova CommaFeed con il conto demo: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Non letto"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Elimina le stelle"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Non hai ancora abbonamenti. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "è richiesto il file"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "フィード URL"
|
||||
msgid "Feed name"
|
||||
msgstr "フィード名"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "フィルタリング式"
|
||||
@@ -320,6 +324,10 @@ msgstr "API ドキュメントに移動します。"
|
||||
msgid "Go to the All view"
|
||||
msgstr "すべてのビューに移動"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "グッディーズ"
|
||||
@@ -348,6 +356,7 @@ msgstr "インポート"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "展開ビューでエントリをスクロールすると、それらが既読としてマークされます"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "未読のままにする"
|
||||
@@ -404,6 +413,10 @@ msgstr "ログイン"
|
||||
msgid "Logout"
|
||||
msgstr "ログアウト"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "すべて既読にする"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "すべてのエントリを既読にする"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "既読にする"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "ここまで既読にする"
|
||||
@@ -430,6 +445,10 @@ msgstr "ここまで既読にする"
|
||||
msgid "Metrics"
|
||||
msgstr "メトリクス"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "ページを下に移動"
|
||||
@@ -515,6 +534,14 @@ msgstr "現在のエントリをバックグラウンドで新しいタブで開
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "次のエントリを開く"
|
||||
@@ -581,6 +608,14 @@ msgstr "リフレッシュ"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "この CommaFeed インスタンスの登録は終了しています"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "共有サイト"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "未読エントリのないフィードとカテゴリを表示する"
|
||||
@@ -654,6 +697,7 @@ msgstr "何か悪いことが起こった..."
|
||||
msgid "Space"
|
||||
msgstr "スペース"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "スター"
|
||||
@@ -681,6 +725,10 @@ msgstr "フィードを購読する"
|
||||
msgid "Success"
|
||||
msgstr "成功"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "ダークテーマに切り替え"
|
||||
@@ -713,6 +761,7 @@ msgstr "デモアカウントで CommaFeed を試す: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "未読"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "スターを外す"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "まだサブスクリプションがありません。"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "ファイルが必要です"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "피드 URL"
|
||||
msgid "Feed name"
|
||||
msgstr "피드 이름"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "필터링 표현식"
|
||||
@@ -320,6 +324,10 @@ msgstr "API 문서로 이동합니다."
|
||||
msgid "Go to the All view"
|
||||
msgstr "전체 보기로 이동"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "굿즈"
|
||||
@@ -348,6 +356,7 @@ msgstr "가져오기"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "확장 보기에서 항목을 스크롤하면 읽은 것으로 표시됩니다."
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "읽지 않은 상태로 유지"
|
||||
@@ -404,6 +413,10 @@ msgstr "로그인"
|
||||
msgid "Logout"
|
||||
msgstr "로그아웃"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "모두 읽은 상태로 표시"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "모든 항목을 읽은 상태로 표시"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "읽은 상태로 표시"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "여기까지 읽은 것으로 표시"
|
||||
@@ -430,6 +445,10 @@ msgstr "여기까지 읽은 것으로 표시"
|
||||
msgid "Metrics"
|
||||
msgstr "메트릭스"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "페이지를 아래로 이동"
|
||||
@@ -515,6 +534,14 @@ msgstr "배경의 새 탭에서 현재 항목 열기"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "다음 항목 열기"
|
||||
@@ -581,6 +608,14 @@ msgstr "새로 고침"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다."
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "사이트 공유"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "읽지 않은 항목이 없는 피드 및 카테고리 표시"
|
||||
@@ -654,6 +697,7 @@ msgstr "뭔가 안 좋은 일이 일어났어..."
|
||||
msgid "Space"
|
||||
msgstr "우주"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "스타"
|
||||
@@ -681,6 +725,10 @@ msgstr "피드 구독"
|
||||
msgid "Success"
|
||||
msgstr "성공"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "어두운 테마로 전환"
|
||||
@@ -713,6 +761,7 @@ msgstr "데모 계정으로 CommaFeed를 사용해 보세요: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "읽지 않음"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "별표 제거"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "아직 구독이 없습니다. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "파일이 필요합니다"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL Suapan"
|
||||
msgid "Feed name"
|
||||
msgstr "Nama suapan"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Ungkapan penapisan"
|
||||
@@ -320,6 +324,10 @@ msgstr "Pergi ke dokumentasi API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Pergi ke paparan Semua"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr ""
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "Dalam paparan yang diperluas, menatal melalui entri menandakannya sebagai dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Teruskan tidak dibaca"
|
||||
@@ -404,6 +413,10 @@ msgstr "Log masuk"
|
||||
msgid "Logout"
|
||||
msgstr "Log Keluar"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Tandai semua sebagai dibaca"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Tandai semua entri sebagai dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Tandakan sebagai dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Tandai sebagai dibaca sehingga di sini"
|
||||
@@ -430,6 +445,10 @@ msgstr "Tandai sebagai dibaca sehingga di sini"
|
||||
msgid "Metrics"
|
||||
msgstr "Metrik"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Gerakkan halaman ke bawah"
|
||||
@@ -515,6 +534,14 @@ msgstr "Buka entri semasa dalam tab baharu di latar belakang"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Buka entri seterusnya"
|
||||
@@ -581,6 +608,14 @@ msgstr "Muat semula"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Pendaftaran ditutup pada contoh CommaFeed ini"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Berkongsi tapak"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Tunjukkan suapan dan kategori tanpa entri yang belum dibaca"
|
||||
@@ -654,6 +697,7 @@ msgstr "Sesuatu yang buruk baru saja berlaku..."
|
||||
msgid "Space"
|
||||
msgstr "Angkasa"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Bintang"
|
||||
@@ -681,6 +725,10 @@ msgstr "Langgan suapan"
|
||||
msgid "Success"
|
||||
msgstr "Kejayaan"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Tukar kepada tema gelap"
|
||||
@@ -713,6 +761,7 @@ msgstr "Cuba CommaFeed dengan akaun demo: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Belum dibaca"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Nyahbintang"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Anda belum mempunyai sebarang langganan lagi. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fail diperlukan"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Feed-URL"
|
||||
msgid "Feed name"
|
||||
msgstr "Feednavn"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerende uttrykk"
|
||||
@@ -320,6 +324,10 @@ msgstr "Gå til API-dokumentasjonen."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Gå til visningen Alle"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Godbiter"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Behold ulest"
|
||||
@@ -404,6 +413,10 @@ msgstr "Logg inn"
|
||||
msgid "Logout"
|
||||
msgstr "Logg ut"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Merk alle som lest"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Merk alle oppføringer som lest"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Merk som lest"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Merk som lest frem til her"
|
||||
@@ -430,6 +445,10 @@ msgstr "Merk som lest frem til her"
|
||||
msgid "Metrics"
|
||||
msgstr "Beregninger"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Flytt siden ned"
|
||||
@@ -515,6 +534,14 @@ msgstr "Åpne gjeldende oppføring i en ny fane i bakgrunnen"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Åpne neste oppføring"
|
||||
@@ -581,6 +608,14 @@ msgstr "Oppdater"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Delingssider"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Vis feeder og kategorier uten uleste oppføringer"
|
||||
@@ -654,6 +697,7 @@ msgstr "Noe ille skjedde akkurat..."
|
||||
msgid "Space"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Stjerne"
|
||||
@@ -681,6 +725,10 @@ msgstr "Abonner på feeden"
|
||||
msgid "Success"
|
||||
msgstr "Suksess"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Bytt til mørkt tema"
|
||||
@@ -713,6 +761,7 @@ msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Ulest"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Fjern stjerne"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Du har ingen abonnementer ennå. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fil kreves"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Feed-URL"
|
||||
msgid "Feed name"
|
||||
msgstr "Feednaam"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Uitdrukking filteren"
|
||||
@@ -320,6 +324,10 @@ msgstr "Ga naar de API-documentatie."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Ga naar de weergave Alles"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Goederen"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "In de uitgevouwen weergave markeert het scrollen door items ze als gelezen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Ongelezen houden"
|
||||
@@ -404,6 +413,10 @@ msgstr "Inloggen"
|
||||
msgid "Logout"
|
||||
msgstr "Uitloggen"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Alles markeren als gelezen"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Markeer alle vermeldingen als gelezen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Markeren als gelezen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Markeer als gelezen tot hier"
|
||||
@@ -430,6 +445,10 @@ msgstr "Markeer als gelezen tot hier"
|
||||
msgid "Metrics"
|
||||
msgstr "Metrieken"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Verplaats de pagina naar beneden"
|
||||
@@ -515,6 +534,14 @@ msgstr "Open huidig item in een nieuw tabblad op de achtergrond"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Volgende invoer openen"
|
||||
@@ -581,6 +608,14 @@ msgstr "Vernieuwen"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registraties zijn gesloten op deze CommaFeed-instantie"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Sites delen"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Toon feeds en categorieën zonder ongelezen items"
|
||||
@@ -654,6 +697,7 @@ msgstr "Er is net iets ergs gebeurd..."
|
||||
msgid "Space"
|
||||
msgstr "Ruimte"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Ster"
|
||||
@@ -681,6 +725,10 @@ msgstr "Abonneer je op de feed"
|
||||
msgid "Success"
|
||||
msgstr "Succes"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Overschakelen naar donker thema"
|
||||
@@ -713,6 +761,7 @@ msgstr "Probeer CommaFeed uit met het demo-account: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Ongelezen"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Sterren uit"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Je hebt nog geen abonnementen. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "bestand is vereist"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Feed-URL"
|
||||
msgid "Feed name"
|
||||
msgstr "Feednavn"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerende uttrykk"
|
||||
@@ -320,6 +324,10 @@ msgstr "Gå til API-dokumentasjonen."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Gå til visningen Alle"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Godbiter"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Behold ulest"
|
||||
@@ -404,6 +413,10 @@ msgstr "Logg inn"
|
||||
msgid "Logout"
|
||||
msgstr "Logg ut"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Merk alle som lest"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Merk alle oppføringer som lest"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Merk som lest"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Merk som lest frem til her"
|
||||
@@ -430,6 +445,10 @@ msgstr "Merk som lest frem til her"
|
||||
msgid "Metrics"
|
||||
msgstr "Beregninger"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Flytt siden ned"
|
||||
@@ -515,6 +534,14 @@ msgstr "Åpne gjeldende oppføring i en ny fane i bakgrunnen"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Åpne neste oppføring"
|
||||
@@ -581,6 +608,14 @@ msgstr "Oppdater"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Delingssider"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Vis feeder og kategorier uten uleste oppføringer"
|
||||
@@ -654,6 +697,7 @@ msgstr "Noe ille skjedde akkurat..."
|
||||
msgid "Space"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Stjerne"
|
||||
@@ -681,6 +725,10 @@ msgstr "Abonner på feeden"
|
||||
msgid "Success"
|
||||
msgstr "Suksess"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Bytt til mørkt tema"
|
||||
@@ -713,6 +761,7 @@ msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Ulest"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Fjern stjerne"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Du har ingen abonnementer ennå. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fil kreves"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL kanału"
|
||||
msgid "Feed name"
|
||||
msgstr "nazwa kanału"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Wyrażenie filtrujące"
|
||||
@@ -320,6 +324,10 @@ msgstr "Przejdź do dokumentacji API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Przejdź do widoku Wszystkie"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Gadżety"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "W widoku rozszerzonym przewijanie wpisów oznacza je jako przeczytane"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Nie przeczytaj"
|
||||
@@ -404,6 +413,10 @@ msgstr "Zaloguj się"
|
||||
msgid "Logout"
|
||||
msgstr "Wyloguj"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Oznacz wszystko jako przeczytane"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Oznacz wszystkie wpisy jako przeczytane"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Oznacz jako przeczytane"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Oznacz jako przeczytane do tej pory"
|
||||
@@ -430,6 +445,10 @@ msgstr "Oznacz jako przeczytane do tej pory"
|
||||
msgid "Metrics"
|
||||
msgstr "Metryki"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Przesuń stronę w dół"
|
||||
@@ -515,6 +534,14 @@ msgstr "Otwórz bieżący wpis w nowej karcie w tle"
|
||||
msgid "Open 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
|
||||
msgid "Open next entry"
|
||||
msgstr "Otwórz następny wpis"
|
||||
@@ -581,6 +608,14 @@ msgstr "Odśwież"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Rejestracje są zamknięte w tej instancji CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Udostępnianie witryn"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
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"
|
||||
msgstr "Przestrzeń"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Gwiazda"
|
||||
@@ -681,6 +725,10 @@ msgstr "Subskrybuj kanał"
|
||||
msgid "Success"
|
||||
msgstr "Sukces"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Przełącz na ciemny motyw"
|
||||
@@ -713,6 +761,7 @@ msgstr "Wypróbuj CommaFeed z kontem demo: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Nieprzeczytane"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr ""
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Nie masz jeszcze żadnych subskrypcji. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "plik jest wymagany"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL do feed"
|
||||
msgid "Feed name"
|
||||
msgstr "Nome do feed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrando expressão"
|
||||
@@ -320,6 +324,10 @@ msgstr "Vá para a documentação da API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Ir para a visualização Tudo"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Brindes"
|
||||
@@ -348,6 +356,7 @@ msgstr "Importar"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "Na visualização expandida, rolar pelas entradas marca-as como lidas"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Manter não lido"
|
||||
@@ -404,6 +413,10 @@ msgstr "Entrar"
|
||||
msgid "Logout"
|
||||
msgstr "Sair"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Marcar todos como lidos"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Marcar todas as entradas como lidas"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Marcar como lido"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Marcar como lido até aqui"
|
||||
@@ -430,6 +445,10 @@ msgstr "Marcar como lido até aqui"
|
||||
msgid "Metrics"
|
||||
msgstr "Métricas"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Mova a página para baixo"
|
||||
@@ -515,6 +534,14 @@ msgstr "Abrir a entrada atual em uma nova aba em segundo plano"
|
||||
msgid "Open 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
|
||||
msgid "Open next entry"
|
||||
msgstr "Abrir próxima entrada"
|
||||
@@ -581,6 +608,14 @@ msgstr "Atualizar"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Os registros estão fechados nesta instância do CommaFeed"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Compartilhando sites"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Mostrar feeds e categorias sem entradas não lidas"
|
||||
@@ -654,6 +697,7 @@ msgstr "Algo ruim acabou de acontecer..."
|
||||
msgid "Space"
|
||||
msgstr "Espaço"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Estrela"
|
||||
@@ -681,6 +725,10 @@ msgstr "Inscrever-se no feed"
|
||||
msgid "Success"
|
||||
msgstr "Sucesso"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Mudar para tema escuro"
|
||||
@@ -713,6 +761,7 @@ msgstr "Experimente o CommaFeed com a conta demo: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Não lido"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Desestrelar"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Você ainda não tem nenhuma assinatura. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "o arquivo é obrigatório"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL-адрес фида"
|
||||
msgid "Feed name"
|
||||
msgstr "Имя фида"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Выражение фильтрации"
|
||||
@@ -320,6 +324,10 @@ msgstr "Перейдите к документации по API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Перейти к представлению «Все»"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Сладости"
|
||||
@@ -348,6 +356,7 @@ msgstr "Импорт"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "В развернутом виде прокрутка записей помечает их как прочитанные."
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Не читать"
|
||||
@@ -404,6 +413,10 @@ msgstr "Войти"
|
||||
msgid "Logout"
|
||||
msgstr "Выйти"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Отметить все как прочитанное"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Отметить все записи как прочитанные"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Отметить как прочитанное"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Отметить как прочитанное до этого места"
|
||||
@@ -430,6 +445,10 @@ msgstr "Отметить как прочитанное до этого мест
|
||||
msgid "Metrics"
|
||||
msgstr "Метрики"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Переместить страницу вниз"
|
||||
@@ -515,6 +534,14 @@ msgstr "Открыть текущую запись в новой вкладке
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Открыть следующую запись"
|
||||
@@ -581,6 +608,14 @@ msgstr "Обновить"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Регистрация закрыта для этого экземпляра CommaFeed."
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Обмен сайтами"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Показать каналы и категории без непрочитанных записей"
|
||||
@@ -654,6 +697,7 @@ msgstr "Только что случилось что-то плохое..."
|
||||
msgid "Space"
|
||||
msgstr "Пробел"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Звезда"
|
||||
@@ -681,6 +725,10 @@ msgstr "Подписаться на ленту"
|
||||
msgid "Success"
|
||||
msgstr "Успех"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Переключиться на темную тему"
|
||||
@@ -713,6 +761,7 @@ msgstr "Попробуйте CommaFeed на демо-счете: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "непрочитано"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Снять пометку"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "У вас пока нет подписок. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "требуется файл"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "URL informačného kanála"
|
||||
msgid "Feed name"
|
||||
msgstr "Názov informačného kanála"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrovanie výrazu"
|
||||
@@ -320,6 +324,10 @@ msgstr "Prejdite na dokumentáciu API."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Prejdite na zobrazenie Všetky"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Dobrôtky"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
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é"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Ponechať neprečítané"
|
||||
@@ -404,6 +413,10 @@ msgstr "Prihláste sa"
|
||||
msgid "Logout"
|
||||
msgstr "Odhlásenie"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Označiť všetko ako prečítané"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Označte všetky položky ako prečítané"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Označiť ako prečítané"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Označiť ako prečítané až sem"
|
||||
@@ -430,6 +445,10 @@ msgstr "Označiť ako prečítané až sem"
|
||||
msgid "Metrics"
|
||||
msgstr "Metriky"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Posuňte stránku nadol"
|
||||
@@ -515,6 +534,14 @@ msgstr "Otvorte aktuálny záznam na novej karte na pozadí"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Otvor ďalší záznam"
|
||||
@@ -581,6 +608,14 @@ msgstr "Obnoviť"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Zdieľanie stránok"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
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"
|
||||
msgstr "Vesmír"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Hviezda"
|
||||
@@ -681,6 +725,10 @@ msgstr "Prihláste sa na odber kanála"
|
||||
msgid "Success"
|
||||
msgstr "Úspech"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Prepnúť na tmavú tému"
|
||||
@@ -713,6 +761,7 @@ msgstr "Vyskúšajte CommaFeed s demo účtom: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Neprečítané"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Odobrať hviezdičku"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Zatiaľ nemáte žiadne odbery. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr ""
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Flödes-URL"
|
||||
msgid "Feed name"
|
||||
msgstr "Flödesnamn"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerande uttryck"
|
||||
@@ -320,6 +324,10 @@ msgstr "Gå till API-dokumentationen."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Gå till vyn Alla"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "Godsaker"
|
||||
@@ -348,6 +356,7 @@ msgstr ""
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "I utökad vy, rullning genom poster markerar dem som lästa"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Behåll oläst"
|
||||
@@ -404,6 +413,10 @@ msgstr "Logga in"
|
||||
msgid "Logout"
|
||||
msgstr "Logga ut"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Markera alla som lästa"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Markera alla poster som lästa"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Markera som läst"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Markera som läst hit"
|
||||
@@ -430,6 +445,10 @@ msgstr "Markera som läst hit"
|
||||
msgid "Metrics"
|
||||
msgstr "Mätverk"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Flytta sidan nedåt"
|
||||
@@ -515,6 +534,14 @@ msgstr "Öppna aktuell post i en ny flik i bakgrunden"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Öppna nästa post"
|
||||
@@ -581,6 +608,14 @@ msgstr "Uppdatera"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Registreringar är stängda på denna CommaFeed-instans"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Delningssajter"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Visa flöden och kategorier utan olästa poster"
|
||||
@@ -654,6 +697,7 @@ msgstr "Något dåligt hände precis..."
|
||||
msgid "Space"
|
||||
msgstr "Rymden"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Stjärna"
|
||||
@@ -681,6 +725,10 @@ msgstr "Prenumerera på flödet"
|
||||
msgid "Success"
|
||||
msgstr "Framgång"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Byt till mörkt tema"
|
||||
@@ -713,6 +761,7 @@ msgstr "Prova CommaFeed med demokontot: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Oläst"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr ""
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Du har inga prenumerationer än. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "fil krävs"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "Feed URL'si"
|
||||
msgid "Feed name"
|
||||
msgstr "Yayın adı"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtreleme ifadesi"
|
||||
@@ -320,6 +324,10 @@ msgstr "API belgelerine gidin."
|
||||
msgid "Go to the All view"
|
||||
msgstr "Tümü görünümüne git"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "İyilikler"
|
||||
@@ -348,6 +356,7 @@ msgstr "İçe Aktar"
|
||||
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"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "Okunmadan sakla"
|
||||
@@ -404,6 +413,10 @@ msgstr "Giriş"
|
||||
msgid "Logout"
|
||||
msgstr "Çıkış"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "Tümünü okundu olarak işaretle"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "Tüm girişleri okundu olarak işaretle"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "Okundu olarak işaretle"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "Buraya kadar okundu olarak işaretle"
|
||||
@@ -430,6 +445,10 @@ msgstr "Buraya kadar okundu olarak işaretle"
|
||||
msgid "Metrics"
|
||||
msgstr "Metrikler"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "Sayfayı aşağı taşı"
|
||||
@@ -515,6 +534,14 @@ msgstr "Geçerli girişi arka planda yeni bir sekmede aç"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "Sonraki girişi aç"
|
||||
@@ -581,6 +608,14 @@ msgstr "Yenile"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "Bu CommaFeed örneğinde kayıtlar kapalı"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "Siteleri paylaşma"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "Okunmamış girişi olmayan beslemeleri ve kategorileri göster"
|
||||
@@ -654,6 +697,7 @@ msgstr "Az önce kötü bir şey oldu..."
|
||||
msgid "Space"
|
||||
msgstr "Uzay"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "Yıldız"
|
||||
@@ -681,6 +725,10 @@ msgstr "beslemeye abone olun"
|
||||
msgid "Success"
|
||||
msgstr "Başarı"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "Karanlık temaya geç"
|
||||
@@ -713,6 +761,7 @@ msgstr "CommaFeed'i demo hesabıyla deneyin: demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "Okunmadı"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "Yıldızı kaldır"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "Henüz aboneliğiniz yok. "
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "dosya gerekli"
|
||||
|
||||
@@ -288,6 +288,10 @@ msgstr "供稿网址"
|
||||
msgid "Feed name"
|
||||
msgstr "提要名称"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Fetch all my feeds now"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "Filtering expression"
|
||||
msgstr "过滤表达式"
|
||||
@@ -320,6 +324,10 @@ msgstr "转到 API 文档。"
|
||||
msgid "Go to the All view"
|
||||
msgstr "转到全部视图"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Goodies"
|
||||
msgstr "好东西"
|
||||
@@ -348,6 +356,7 @@ msgstr "进口"
|
||||
msgid "In expanded view, scrolling through entries mark them as read"
|
||||
msgstr "在展开视图中,滚动条目将它们标记为已读"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Keep unread"
|
||||
msgstr "保持未读状态"
|
||||
@@ -404,6 +413,10 @@ msgstr "登录"
|
||||
msgid "Logout"
|
||||
msgstr "注销"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Long press"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Manage users"
|
||||
@@ -418,10 +431,12 @@ msgstr "全部标记为已读"
|
||||
msgid "Mark all entries as read"
|
||||
msgstr "将所有条目标记为已读"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read"
|
||||
msgstr "标记为已读"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Mark as read up to here"
|
||||
msgstr "标记为已读到这里"
|
||||
@@ -430,6 +445,10 @@ msgstr "标记为已读到这里"
|
||||
msgid "Metrics"
|
||||
msgstr "指标"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Middle click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Move the page down"
|
||||
msgstr "页面下移"
|
||||
@@ -515,6 +534,14 @@ msgstr "在后台的新选项卡中打开当前条目"
|
||||
msgid "Open link"
|
||||
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
|
||||
msgid "Open next entry"
|
||||
msgstr "打开下一个条目"
|
||||
@@ -581,6 +608,14 @@ msgstr "刷新"
|
||||
msgid "Registrations are closed on this CommaFeed instance"
|
||||
msgstr "此 CommaFeed 实例上的注册已关闭"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Right click"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/settings/ProfileSettings.tsx
|
||||
#: src/pages/app/CategoryDetailsPage.tsx
|
||||
@@ -632,6 +667,14 @@ msgstr "共享站点"
|
||||
msgid "Shift"
|
||||
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
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
msgstr "显示没有未读条目的提要和类别"
|
||||
@@ -654,6 +697,7 @@ msgstr "刚刚发生了不好的事情……"
|
||||
msgid "Space"
|
||||
msgstr "空间"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Star"
|
||||
msgstr "星星"
|
||||
@@ -681,6 +725,10 @@ msgstr "订阅订阅源"
|
||||
msgid "Success"
|
||||
msgstr "成功"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Swipe header to the right"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Switch to dark theme"
|
||||
msgstr "切换到深色主题"
|
||||
@@ -713,6 +761,7 @@ msgstr "使用演示帐户试用 CommaFeed:demo/demo"
|
||||
msgid "Unread"
|
||||
msgstr "未读"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
#: src/components/content/FeedEntryFooter.tsx
|
||||
msgid "Unstar"
|
||||
msgstr "解星"
|
||||
@@ -743,6 +792,10 @@ 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?"
|
||||
msgstr "您还没有任何订阅。"
|
||||
|
||||
#: src/components/header/RefreshMenu.tsx
|
||||
msgid "Your feeds have been queued for refresh."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "file is required"
|
||||
msgstr "文件是必需的"
|
||||
|
||||
@@ -2,6 +2,7 @@ import "@fontsource/open-sans"
|
||||
import { store } from "app/store"
|
||||
import dayjs from "dayjs"
|
||||
import relativeTime from "dayjs/plugin/relativeTime"
|
||||
import "react-contexify/ReactContexify.css"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { Provider } from "react-redux"
|
||||
import { App } from "./App"
|
||||
|
||||
@@ -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-worker.active": "Feed Worker active",
|
||||
"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.takeQueue": "Task Giver Take Queue",
|
||||
"com.commafeed.backend.feed.FeedQueues.giveBackQueue": "Task Giver Give Back Queue",
|
||||
"com.commafeed.backend.feed.FeedQueues.queue": "Feed Refresh queue size",
|
||||
}
|
||||
|
||||
export function MetricsPage() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { Logo } from "components/Logo"
|
||||
import { OnDesktop } from "components/responsive/OnDesktop"
|
||||
import { OnMobile } from "components/responsive/OnMobile"
|
||||
import { useAppLoading } from "hooks/useAppLoading"
|
||||
import { useWebSocket } from "hooks/useWebSocket"
|
||||
import { LoadingPage } from "pages/LoadingPage"
|
||||
import { ReactNode, Suspense, useEffect } from "react"
|
||||
import { TbPlus } from "react-icons/tb"
|
||||
@@ -85,6 +86,7 @@ export default function Layout({ sidebar, header }: LayoutProps) {
|
||||
const { loading } = useAppLoading()
|
||||
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
||||
const dispatch = useAppDispatch()
|
||||
useWebSocket()
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(reloadSettings())
|
||||
|
||||
@@ -22,6 +22,7 @@ export default defineConfig({
|
||||
port: 8082,
|
||||
proxy: {
|
||||
"/rest": "http://localhost:8083",
|
||||
"/ws": "ws://localhost:8083",
|
||||
"/swagger": "http://localhost:8083",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
@@ -261,8 +256,8 @@
|
||||
<artifactId>dropwizard-hibernate</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-migrations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
@@ -285,6 +280,11 @@
|
||||
<artifactId>dropwizard-web</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>be.tomcools</groupId>
|
||||
<artifactId>dropwizard-websocket-jee7-bundle</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
@@ -474,6 +474,11 @@
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-junit-jupiter</artifactId>
|
||||
|
||||
@@ -13,6 +13,7 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
@@ -47,17 +48,22 @@ import com.commafeed.frontend.servlet.CustomCssServlet;
|
||||
import com.commafeed.frontend.servlet.LogoutServlet;
|
||||
import com.commafeed.frontend.servlet.NextUnreadServlet;
|
||||
import com.commafeed.frontend.session.SessionHelperFactoryProvider;
|
||||
import com.commafeed.frontend.ws.WebSocketConfigurator;
|
||||
import com.commafeed.frontend.ws.WebSocketEndpoint;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
import be.tomcools.dropwizard.websocket.WebsocketBundle;
|
||||
import io.dropwizard.Application;
|
||||
import io.dropwizard.assets.AssetsBundle;
|
||||
import io.dropwizard.configuration.EnvironmentVariableSubstitutor;
|
||||
import io.dropwizard.configuration.SubstitutingSourceProvider;
|
||||
import io.dropwizard.db.DataSourceFactory;
|
||||
import io.dropwizard.forms.MultiPartBundle;
|
||||
import io.dropwizard.hibernate.HibernateBundle;
|
||||
import io.dropwizard.server.DefaultServerFactory;
|
||||
import io.dropwizard.migrations.MigrationsBundle;
|
||||
import io.dropwizard.servlets.CacheBustingFilter;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import io.dropwizard.setup.Environment;
|
||||
@@ -72,6 +78,7 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
public static final Date STARTUP_TIME = new Date();
|
||||
|
||||
private HibernateBundle<CommaFeedConfiguration> hibernateBundle;
|
||||
private WebsocketBundle<CommaFeedConfiguration> websocketBundle;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -82,6 +89,7 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
public void initialize(Bootstrap<CommaFeedConfiguration> bootstrap) {
|
||||
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,
|
||||
FeedCategory.class, FeedEntry.class, FeedEntryContent.class, FeedEntryStatus.class, FeedEntryTag.class,
|
||||
FeedSubscription.class, User.class, UserRole.class, UserSettings.class) {
|
||||
@@ -107,8 +115,19 @@ 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 MultiPartBundle());
|
||||
|
||||
// Enable variable substitution with environment variables
|
||||
bootstrap.setConfigurationSourceProvider(
|
||||
new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(), new EnvironmentVariableSubstitutor(false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,7 +145,6 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
|
||||
// REST resources
|
||||
environment.jersey().setUrlPattern("/rest/*");
|
||||
((DefaultServerFactory) config.getServerFactory()).setJerseyRootPath("/rest/*");
|
||||
environment.jersey().register(injector.getInstance(AdminREST.class));
|
||||
environment.jersey().register(injector.getInstance(CategoryREST.class));
|
||||
environment.jersey().register(injector.getInstance(EntryREST.class));
|
||||
@@ -141,6 +159,12 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
environment.servlets().addServlet("customCss", injector.getInstance(CustomCssServlet.class)).addMapping("/custom_css.css");
|
||||
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
|
||||
Set<ScheduledTask> tasks = injector.getInstance(Key.get(new TypeLiteral<Set<ScheduledTask>>() {
|
||||
}));
|
||||
|
||||
@@ -45,18 +45,14 @@ public class CommaFeedConfiguration extends Configuration {
|
||||
@JsonProperty("app")
|
||||
private ApplicationSettings applicationSettings;
|
||||
|
||||
private final ResourceBundle bundle;
|
||||
private final String version;
|
||||
private final String gitCommit;
|
||||
|
||||
public CommaFeedConfiguration() {
|
||||
bundle = ResourceBundle.getBundle("application");
|
||||
}
|
||||
ResourceBundle bundle = ResourceBundle.getBundle("application");
|
||||
|
||||
public String getVersion() {
|
||||
return bundle.getString("version");
|
||||
}
|
||||
|
||||
public String getGitCommit() {
|
||||
return bundle.getString("git.commit");
|
||||
this.version = bundle.getString("version");
|
||||
this.gitCommit = bundle.getString("git.commit");
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -182,11 +182,28 @@ public class HttpGetter {
|
||||
System.out.println(new String(result.content));
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class NotModifiedException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* if the value of this header changed, this is its new value
|
||||
*/
|
||||
private String newLastModifiedHeader;
|
||||
|
||||
/**
|
||||
* if the value of this header changed, this is its new value
|
||||
*/
|
||||
private String newEtagHeader;
|
||||
|
||||
public NotModifiedException(String message) {
|
||||
this(message, null, null);
|
||||
}
|
||||
|
||||
public NotModifiedException(String message, String newLastModifiedHeader, String newEtagHeader) {
|
||||
super(message);
|
||||
this.newLastModifiedHeader = newLastModifiedHeader;
|
||||
this.newEtagHeader = newEtagHeader;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,16 +62,23 @@ public class FeedFetcher {
|
||||
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);
|
||||
if (lastContentHash != null && hash != null && lastContentHash.equals(hash)) {
|
||||
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
|
||||
&& lastPublishedDate.getTime() == fetchedFeed.getFeed().getLastPublishedDate().getTime()) {
|
||||
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();
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -27,126 +24,90 @@ import com.commafeed.backend.model.Feed;
|
||||
@Singleton
|
||||
public class FeedQueues {
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
private final SessionFactory sessionFactory;
|
||||
private final FeedDAO feedDAO;
|
||||
private final CommaFeedConfiguration config;
|
||||
|
||||
private Queue<FeedRefreshContext> addQueue = new ConcurrentLinkedQueue<>();
|
||||
private Queue<FeedRefreshContext> takeQueue = new ConcurrentLinkedQueue<>();
|
||||
private Queue<Feed> giveBackQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private Meter refill;
|
||||
private final BlockingDeque<FeedRefreshContext> queue = new LinkedBlockingDeque<>();
|
||||
private final Meter refill;
|
||||
|
||||
@Inject
|
||||
public FeedQueues(SessionFactory sessionFactory, FeedDAO feedDAO, CommaFeedConfiguration config, MetricRegistry metrics) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.config = config;
|
||||
this.feedDAO = feedDAO;
|
||||
this.refill = metrics.meter(MetricRegistry.name(getClass(), "refill"));
|
||||
|
||||
refill = metrics.meter(MetricRegistry.name(getClass(), "refill"));
|
||||
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();
|
||||
}
|
||||
});
|
||||
metrics.register(MetricRegistry.name(getClass(), "queue"), (Gauge<Integer>) queue::size);
|
||||
}
|
||||
|
||||
/**
|
||||
* take a feed from the refresh queue
|
||||
*/
|
||||
public synchronized FeedRefreshContext take() {
|
||||
FeedRefreshContext context = takeQueue.poll();
|
||||
|
||||
if (context == null) {
|
||||
refill();
|
||||
context = takeQueue.poll();
|
||||
FeedRefreshContext context = queue.poll();
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
public void add(Feed feed, boolean urgent) {
|
||||
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
|
||||
if (feed.getLastUpdated() == null || feed.getLastUpdated().before(DateUtils.addMinutes(new Date(), -1 * refreshInterval))) {
|
||||
boolean alreadyQueued = addQueue.stream().anyMatch(c -> c.getFeed().getId().equals(feed.getId()));
|
||||
if (!alreadyQueued) {
|
||||
addQueue.add(new FeedRefreshContext(feed, urgent));
|
||||
}
|
||||
if (isFeedAlreadyQueued(feed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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() {
|
||||
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
|
||||
int count = batchSize - contexts.size();
|
||||
if (count > 0) {
|
||||
List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> feedDAO.findNextUpdatable(count, getLastLoginThreshold()));
|
||||
for (Feed feed : feeds) {
|
||||
contexts.add(new FeedRefreshContext(feed, false));
|
||||
}
|
||||
}
|
||||
int batchSize = Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
|
||||
List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> {
|
||||
List<Feed> list = feedDAO.findNextUpdatable(batchSize, getLastLoginThreshold());
|
||||
|
||||
// set the disabledDate as we use it in feedDAO to decide what to refresh next. We also use a map to remove
|
||||
// duplicates.
|
||||
Map<Long, FeedRefreshContext> map = new LinkedHashMap<>();
|
||||
for (FeedRefreshContext context : contexts) {
|
||||
Feed feed = context.getFeed();
|
||||
feed.setDisabledUntil(DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes()));
|
||||
map.put(feed.getId(), context);
|
||||
}
|
||||
// set the disabledDate as we use it in feedDAO.findNextUpdatable() to decide what to refresh next
|
||||
Date nextRefreshDate = DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes());
|
||||
list.forEach(f -> f.setDisabledUntil(nextRefreshDate));
|
||||
feedDAO.saveOrUpdate(list);
|
||||
|
||||
// refill the queue
|
||||
takeQueue.addAll(map.values());
|
||||
return list;
|
||||
});
|
||||
|
||||
// add feeds from the giveBack queue to the map, overriding duplicates
|
||||
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));
|
||||
feeds.forEach(f -> add(f, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* give a feed back, updating it to the database during the next refill()
|
||||
*/
|
||||
public void giveBack(Feed feed) {
|
||||
String normalized = FeedUtils.normalizeURL(feed.getUrl());
|
||||
feed.setNormalizedUrl(normalized);
|
||||
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized));
|
||||
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() {
|
||||
@@ -157,4 +118,7 @@ public class FeedQueues {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFeedAlreadyQueued(Feed feed) {
|
||||
return queue.stream().anyMatch(c -> c.getFeed().getId().equals(feed.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,6 @@ public class FeedRefreshTaskGiver implements Managed {
|
||||
private final ExecutorService executor;
|
||||
|
||||
private final Meter feedRefreshed;
|
||||
private final Meter threadWaited;
|
||||
|
||||
@Inject
|
||||
public FeedRefreshTaskGiver(FeedQueues queues, FeedDAO feedDAO, FeedRefreshWorker worker, CommaFeedConfiguration config,
|
||||
@@ -38,7 +37,6 @@ public class FeedRefreshTaskGiver implements Managed {
|
||||
|
||||
executor = Executors.newFixedThreadPool(1);
|
||||
feedRefreshed = metrics.meter(MetricRegistry.name(getClass(), "feedRefreshed"));
|
||||
threadWaited = metrics.meter(MetricRegistry.name(getClass(), "threadWaited"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,14 +64,6 @@ public class FeedRefreshTaskGiver implements Managed {
|
||||
if (context != null) {
|
||||
feedRefreshed.mark();
|
||||
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) {
|
||||
log.error(e.getMessage(), e);
|
||||
|
||||
@@ -32,9 +32,12 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.service.FeedUpdateService;
|
||||
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 io.dropwizard.lifecycle.Managed;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@@ -48,6 +51,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
private final CommaFeedConfiguration config;
|
||||
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
private final CacheService cache;
|
||||
private final WebSocketSessions webSocketSessions;
|
||||
|
||||
private final FeedRefreshExecutor pool;
|
||||
private final Striped<Lock> locks;
|
||||
@@ -60,7 +64,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
@Inject
|
||||
public FeedRefreshUpdater(SessionFactory sessionFactory, FeedUpdateService feedUpdateService, PubSubService pubSubService,
|
||||
FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO,
|
||||
CacheService cache) {
|
||||
CacheService cache, WebSocketSessions webSocketSessions) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.feedUpdateService = feedUpdateService;
|
||||
this.pubSubService = pubSubService;
|
||||
@@ -68,6 +72,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
this.config = config;
|
||||
this.feedSubscriptionDAO = feedSubscriptionDAO;
|
||||
this.cache = cache;
|
||||
this.webSocketSessions = webSocketSessions;
|
||||
|
||||
ApplicationSettings settings = config.getApplicationSettings();
|
||||
int threads = Math.max(settings.getDatabaseUpdateThreads(), 1);
|
||||
@@ -94,8 +99,9 @@ public class FeedRefreshUpdater implements Managed {
|
||||
pool.execute(new EntryTask(context));
|
||||
}
|
||||
|
||||
private boolean addEntry(final Feed feed, final FeedEntry entry, final List<FeedSubscription> subscriptions) {
|
||||
boolean success = false;
|
||||
private AddEntryResult addEntry(final Feed feed, final FeedEntry entry, final List<FeedSubscription> subscriptions) {
|
||||
boolean processed = false;
|
||||
boolean inserted = false;
|
||||
|
||||
// lock on feed, make sure we are not updating the same feed twice at
|
||||
// the same time
|
||||
@@ -112,14 +118,15 @@ public class FeedRefreshUpdater implements Managed {
|
||||
boolean locked1 = false;
|
||||
boolean locked2 = false;
|
||||
try {
|
||||
// try to lock, give up after 1 minute
|
||||
locked1 = lock1.tryLock(1, TimeUnit.MINUTES);
|
||||
locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
|
||||
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) {
|
||||
entryInserted.mark();
|
||||
}
|
||||
success = true;
|
||||
} else {
|
||||
log.error("lock timeout for " + feed.getUrl() + " - " + key1);
|
||||
}
|
||||
@@ -133,7 +140,7 @@ public class FeedRefreshUpdater implements Managed {
|
||||
lock2.unlock();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
return new AddEntryResult(processed, inserted);
|
||||
}
|
||||
|
||||
private void handlePubSub(final Feed feed) {
|
||||
@@ -169,7 +176,9 @@ public class FeedRefreshUpdater implements Managed {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean ok = true;
|
||||
boolean processed = true;
|
||||
boolean insertedAtLeastOneEntry = false;
|
||||
|
||||
final Feed feed = context.getFeed();
|
||||
List<FeedEntry> entries = context.getEntries();
|
||||
if (entries.isEmpty()) {
|
||||
@@ -186,7 +195,10 @@ public class FeedRefreshUpdater implements Managed {
|
||||
if (subscriptions == null) {
|
||||
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();
|
||||
} else {
|
||||
log.debug("cache hit for {}", entry.getUrl());
|
||||
@@ -199,17 +211,20 @@ public class FeedRefreshUpdater implements Managed {
|
||||
|
||||
if (subscriptions == null) {
|
||||
feed.setMessage("No new entries found");
|
||||
} else if (!subscriptions.isEmpty()) {
|
||||
} else if (insertedAtLeastOneEntry) {
|
||||
List<User> users = subscriptions.stream().map(FeedSubscription::getUser).collect(Collectors.toList());
|
||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[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()) {
|
||||
handlePubSub(feed);
|
||||
}
|
||||
if (!ok) {
|
||||
if (!processed) {
|
||||
// requeue asap
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -10,7 +9,6 @@ import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
@@ -31,15 +29,17 @@ import lombok.extern.slf4j.Slf4j;
|
||||
public class FeedRefreshWorker implements Managed {
|
||||
|
||||
private final FeedRefreshUpdater feedRefreshUpdater;
|
||||
private final FeedRefreshIntervalCalculator refreshIntervalCalculator;
|
||||
private final FeedFetcher fetcher;
|
||||
private final FeedQueues queues;
|
||||
private final CommaFeedConfiguration config;
|
||||
private final FeedRefreshExecutor pool;
|
||||
|
||||
@Inject
|
||||
public FeedRefreshWorker(FeedRefreshUpdater feedRefreshUpdater, FeedFetcher fetcher, FeedQueues queues, CommaFeedConfiguration config,
|
||||
MetricRegistry metrics) {
|
||||
public FeedRefreshWorker(FeedRefreshUpdater feedRefreshUpdater, FeedRefreshIntervalCalculator refreshIntervalCalculator,
|
||||
FeedFetcher fetcher, FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics) {
|
||||
this.feedRefreshUpdater = feedRefreshUpdater;
|
||||
this.refreshIntervalCalculator = refreshIntervalCalculator;
|
||||
this.fetcher = fetcher;
|
||||
this.config = config;
|
||||
this.queues = queues;
|
||||
@@ -62,8 +62,6 @@ public class FeedRefreshWorker implements Managed {
|
||||
|
||||
private void update(FeedRefreshContext context) {
|
||||
Feed feed = context.getFeed();
|
||||
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
|
||||
Date disabledUntil = DateUtils.addMinutes(new Date(), refreshInterval);
|
||||
try {
|
||||
String url = Optional.ofNullable(feed.getUrlAfterRedirect()).orElse(feed.getUrl());
|
||||
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());
|
||||
}
|
||||
|
||||
if (config.getApplicationSettings().getHeavyLoad()) {
|
||||
disabledUntil = FeedUtils.buildDisabledUntil(fetchedFeed.getFeed().getLastEntryDate(),
|
||||
fetchedFeed.getFeed().getAverageEntryInterval(), disabledUntil);
|
||||
}
|
||||
String urlAfterRedirect = fetchedFeed.getUrlAfterRedirect();
|
||||
if (StringUtils.equals(url, urlAfterRedirect)) {
|
||||
urlAfterRedirect = null;
|
||||
@@ -95,7 +89,7 @@ public class FeedRefreshWorker implements Managed {
|
||||
|
||||
feed.setErrorCount(0);
|
||||
feed.setMessage(null);
|
||||
feed.setDisabledUntil(disabledUntil);
|
||||
feed.setDisabledUntil(refreshIntervalCalculator.onFetchSuccess(fetchedFeed));
|
||||
|
||||
handlePubSub(feed, fetchedFeed.getFeed());
|
||||
context.setEntries(entries);
|
||||
@@ -104,12 +98,17 @@ public class FeedRefreshWorker implements Managed {
|
||||
} catch (NotModifiedException e) {
|
||||
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.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);
|
||||
} catch (Exception e) {
|
||||
@@ -118,7 +117,7 @@ public class FeedRefreshWorker implements Managed {
|
||||
|
||||
feed.setErrorCount(feed.getErrorCount() + 1);
|
||||
feed.setMessage(message);
|
||||
feed.setDisabledUntil(FeedUtils.buildDisabledUntil(feed.getErrorCount()));
|
||||
feed.setDisabledUntil(refreshIntervalCalculator.onFetchError(feed));
|
||||
|
||||
queues.giveBack(feed);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
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.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
@@ -359,57 +357,6 @@ public class FeedUtils {
|
||||
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) {
|
||||
if (entries.isEmpty() || entries.size() == 1) {
|
||||
return null;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class FeedSubscriptionService {
|
||||
sub.setTitle(FeedUtils.truncate(title, 128));
|
||||
feedSubscriptionDAO.saveOrUpdate(sub);
|
||||
|
||||
queues.add(feed, false);
|
||||
queues.add(feed, true);
|
||||
cache.invalidateUserRootCategory(user);
|
||||
return sub.getId();
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ public class CategoryREST {
|
||||
@UnitOfWork
|
||||
@ApiOperation(value = "Get unread count for feed subscriptions", response = UnreadCount.class, responseContainer = "List")
|
||||
@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);
|
||||
return Response.ok(Lists.newArrayList(unreadCount.values())).build();
|
||||
}
|
||||
|
||||
@@ -9,16 +9,26 @@ import org.eclipse.jetty.server.session.FileSessionDataStore;
|
||||
import org.eclipse.jetty.server.session.SessionCache;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import io.dropwizard.util.Duration;
|
||||
|
||||
public class SessionHandlerFactory {
|
||||
|
||||
@JsonProperty
|
||||
private String path = "sessions";
|
||||
|
||||
@JsonProperty
|
||||
private Duration cookieMaxAge = Duration.days(30);
|
||||
|
||||
@JsonProperty
|
||||
private Duration cookieRefreshAge = Duration.days(1);
|
||||
|
||||
@JsonProperty
|
||||
private Duration maxInactiveInterval = Duration.days(30);
|
||||
|
||||
@JsonProperty
|
||||
private Duration savePeriod = Duration.minutes(5);
|
||||
|
||||
public SessionHandler build() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.commafeed.backend.model.User;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor()
|
||||
@RequiredArgsConstructor
|
||||
public class SessionHelper {
|
||||
|
||||
private static final String SESSION_KEY_USER = "user";
|
||||
@@ -18,15 +18,18 @@ public class SessionHelper {
|
||||
|
||||
public Optional<User> getLoggedInUser() {
|
||||
Optional<HttpSession> session = getSession(false);
|
||||
|
||||
if (session.isPresent()) {
|
||||
User user = (User) session.get().getAttribute(SESSION_KEY_USER);
|
||||
return Optional.ofNullable(user);
|
||||
return getLoggedInUser(session.get());
|
||||
}
|
||||
|
||||
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) {
|
||||
Optional<HttpSession> session = getSession(true);
|
||||
session.get().setAttribute(SESSION_KEY_USER, user);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
10
pom.xml
@@ -28,6 +28,16 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<modules>
|
||||
<module>commafeed-client</module>
|
||||
<module>commafeed-server</module>
|
||||
|
||||
Reference in New Issue
Block a user