Compare commits

...

32 Commits
3.8.0 ... 3.9.0

Author SHA1 Message Date
Athou
6fe1c2a3c0 release 3.9.0 2023-08-17 10:01:17 +02:00
Athou
c2e453027c fix desktop entry header shortly rendered as mobile, causing a small visual glitch 2023-08-16 07:36:05 +02:00
Athou
f16bac9b59 remove "required" for more nulalble fields 2023-08-11 19:12:48 +02:00
Athou
8cca826e70 specify in documentation that we support basic auth 2023-08-11 15:12:00 +02:00
Athou
b0165bb26a "created" field is not always filled, remove "required" 2023-08-11 13:34:05 +02:00
Athou
366294ab46 show loader only while loading (#1131) 2023-08-11 12:53:27 +02:00
Jérémie Panzer
2988938440 Merge pull request #1135 from dcelasun/update-tr-locale
Update Turkish translation
2023-08-10 11:21:07 +02:00
D. Can Celasun
e865769e30 Update Turkish translation 2023-08-10 09:53:38 +01:00
Jérémie Panzer
f87be2fc03 Merge pull request #1129 from canoine/master
Update fr/messages.po
2023-08-04 13:31:58 +02:00
Athou
466846d268 add option to disable custom context menu (#1128) 2023-08-04 08:53:34 +02:00
canoine
61b6be4090 Update fr/messages.po
New entries translated
2023-08-04 08:47:34 +02:00
Athou
cb779ec494 add setting to disable mark as read confirmation (#1110) 2023-08-04 07:32:13 +02:00
Athou
da6f2050f9 log as debug because default log level is info and we don't want to see this 2023-08-02 14:39:03 +02:00
Athou
4304f84a55 restore the announcement feature 2023-08-01 16:30:42 +02:00
Athou
8a175d8221 add css parser error handler that just prints info message because it is non-blocking 2023-08-01 15:30:50 +02:00
Athou
f1896d34e2 disable context menu on shift + right click (#1052) 2023-07-21 09:58:08 +02:00
Athou
45d0e0ec98 Merge branch 'dcelasun-configurable-batch-size' 2023-07-12 15:27:18 +02:00
Athou
38c5beec2f remove unused ApplicationSettings type in the client 2023-07-12 15:26:56 +02:00
Athou
c4715dc3f7 rename field to better represent what it does 2023-07-12 15:26:56 +02:00
D. Can Celasun
6ce6b5ef0e Make database cleaning batch size configurable 2023-07-12 15:26:56 +02:00
Athou
1af3dd452c Merge branch 'dcelasun-mariadb-driver-fix' 2023-07-12 15:04:04 +02:00
Athou
1f4ec41222 change other yml files 2023-07-12 15:03:30 +02:00
D. Can Celasun
512c4cc507 Support MariaDB JDBC driver
This fixes #1113
2023-07-11 08:54:17 +01:00
Athou
d391c8f1c9 Merge branch 'ScuttleSE-master' 2023-07-09 14:37:53 +02:00
Athou
46d3e67aec update other yml files 2023-07-09 14:31:46 +02:00
Athou
d9505c4d87 Merge branch 'master' of https://github.com/ScuttleSE/commafeed into ScuttleSE-master 2023-07-09 14:31:13 +02:00
Athou
42491f5778 no need to repeat feed url in message stored in database (#1112) 2023-07-09 14:09:30 +02:00
Gustav Almstrom
9c897c9fb2 Updated MySQL driver 2023-07-09 11:10:27 +02:00
Athou
21b500a96e don't autoclose bugs 2023-07-08 15:56:02 +02:00
Athou
04c74b5daa release 3.8.1 2023-07-04 18:40:15 +02:00
Athou
3edb8a3ee2 don't scroll to entry if it's already selected (#1108) 2023-07-04 08:37:56 +02:00
Athou
922346bef6 fetch only ids to improve performance during cleanup 2023-07-01 22:54:28 +02:00
63 changed files with 1027 additions and 291 deletions

1
.github/stale.yml vendored
View File

@@ -7,6 +7,7 @@ exemptLabels:
- pinned - pinned
- security - security
- enhancement - enhancement
- bug
# Label to use when marking an issue as stale # Label to use when marking an issue as stale
staleLabel: wontfix staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable # Comment to post when marking an issue as stale. Set to `false` to disable

View File

@@ -1,5 +1,24 @@
# Changelog # Changelog
## [3.9.0]
- improve performance by disabling the loader when nothing is loading (most noticeable on mobile)
- added a setting to disable the 'mark all as read' confirmation
- added a setting to disable the custom context menu
- if the custom context is enabled, it can still be disabled by pressing the shift key
- the announcement feature is now working again and supports html ('announcement' configuration element in config.yml)
- add support for MariaDB 11+
- fix entry header shortly rendered as mobile on desktop, causing a small visual glitch
- fix an issue that could cause a feed to not refresh correctly if the url was very long
- database cleanup batch size is now configurable
- css parsing errors are no longer logged to the standard output
- fix small errors in the api documentation
## [3.8.1]
- in expanded mode, don't scroll when clicking on the body of the current entry
- improve content cleanup task performance for instances with a very large number of feeds
## [3.8.0] ## [3.8.0]
- add previous and next buttons in the toolbar - add previous and next buttons in the toolbar
@@ -90,10 +109,9 @@
## [3.0.1] ## [3.0.1]
- allow env variable substitution in config.yml - allow env variable substitution in config.yml
- e.g. having a custom config.yml file with `app.session.path=${SOME_ENV_VAR}` will substitute `SOME_ENV_VAR` with - e.g. having a custom config.yml file with `app.session.path=${SOME_ENV_VAR}` will substitute `SOME_ENV_VAR` with its value
its value
- allow env variable prefixed with `CF_` to override config.yml properties - allow env variable prefixed with `CF_` to override config.yml properties
- e.g. setting `CF_APP_ALLOWREGISTRATIONS=true` will set `app.allowRegistrations` to `true` - e.g. setting `CF_APP_ALLOWREGISTRATIONS=true` will set `app.allowRegistrations` to `true`
## [3.0.0] ## [3.0.0]

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>3.8.0</version> <version>3.9.0</version>
</parent> </parent>
<artifactId>commafeed-client</artifactId> <artifactId>commafeed-client</artifactId>
<name>CommaFeed Client</name> <name>CommaFeed Client</name>

View File

@@ -78,6 +78,7 @@ describe("entries", () => {
sourceWebsiteUrl: "", sourceWebsiteUrl: "",
entries: [{ id: "3" } as Entry], entries: [{ id: "3" } as Entry],
hasMore: true, hasMore: true,
loading: false,
scrollingToEntry: false, scrollingToEntry: false,
}, },
}, },
@@ -102,6 +103,7 @@ describe("entries", () => {
sourceWebsiteUrl: "", sourceWebsiteUrl: "",
entries: [{ id: "3", read: false } as Entry, { id: "4", read: false } as Entry], entries: [{ id: "3", read: false } as Entry, { id: "4", read: false } as Entry],
hasMore: true, hasMore: true,
loading: false,
scrollingToEntry: false, scrollingToEntry: false,
}, },
}, },
@@ -128,6 +130,7 @@ describe("entries", () => {
sourceWebsiteUrl: "", sourceWebsiteUrl: "",
entries: [{ id: "3", read: false } as Entry, { id: "4", read: false } as Entry], entries: [{ id: "3", read: false } as Entry, { id: "4", read: false } as Entry],
hasMore: true, hasMore: true,
loading: false,
scrollingToEntry: false, scrollingToEntry: false,
}, },
}, },

View File

@@ -27,6 +27,7 @@ interface EntriesState {
timestamp?: number timestamp?: number
selectedEntryId?: string selectedEntryId?: string
hasMore: boolean hasMore: boolean
loading: boolean
search?: string search?: string
scrollingToEntry: boolean scrollingToEntry: boolean
} }
@@ -40,6 +41,7 @@ const initialState: EntriesState = {
sourceWebsiteUrl: "", sourceWebsiteUrl: "",
entries: [], entries: [],
hasMore: true, hasMore: true,
loading: false,
scrollingToEntry: false, scrollingToEntry: false,
} }
@@ -329,6 +331,10 @@ export const entriesSlice = createSlice({
state.sourceWebsiteUrl = "" state.sourceWebsiteUrl = ""
state.hasMore = true state.hasMore = true
state.selectedEntryId = undefined state.selectedEntryId = undefined
state.loading = true
})
builder.addCase(loadMoreEntries.pending, state => {
state.loading = true
}) })
builder.addCase(loadEntries.fulfilled, (state, action) => { builder.addCase(loadEntries.fulfilled, (state, action) => {
state.entries = action.payload.entries state.entries = action.payload.entries
@@ -336,12 +342,14 @@ export const entriesSlice = createSlice({
state.sourceLabel = action.payload.name state.sourceLabel = action.payload.name
state.sourceWebsiteUrl = action.payload.feedLink state.sourceWebsiteUrl = action.payload.feedLink
state.hasMore = action.payload.hasMore state.hasMore = action.payload.hasMore
state.loading = false
}) })
builder.addCase(loadMoreEntries.fulfilled, (state, action) => { builder.addCase(loadMoreEntries.fulfilled, (state, action) => {
// remove already existing entries // remove already existing entries
const entriesToAdd = action.payload.entries.filter(e => !state.entries.some(e2 => e.id === e2.id)) const entriesToAdd = action.payload.entries.filter(e => !state.entries.some(e2 => e.id === e2.id))
state.entries = [...state.entries, ...entriesToAdd] state.entries = [...state.entries, ...entriesToAdd]
state.hasMore = action.payload.hasMore state.hasMore = action.payload.hasMore
state.loading = false
}) })
builder.addCase(tagEntry.pending, (state, action) => { builder.addCase(tagEntry.pending, (state, action) => {
state.entries state.entries

View File

@@ -91,6 +91,28 @@ export const changeAlwaysScrollToEntry = createAsyncThunk<
if (!settings) return if (!settings) return
client.user.saveSettings({ ...settings, alwaysScrollToEntry }) client.user.saveSettings({ ...settings, alwaysScrollToEntry })
}) })
export const changeMarkAllAsReadConfirmation = createAsyncThunk<
void,
boolean,
{
state: RootState
}
>("settings/markAllAsReadConfirmation", (markAllAsReadConfirmation, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, markAllAsReadConfirmation })
})
export const changeCustomContextMenu = createAsyncThunk<
void,
boolean,
{
state: RootState
}
>("settings/customContextMenu", (customContextMenu, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, customContextMenu })
})
export const changeSharingSetting = createAsyncThunk< export const changeSharingSetting = createAsyncThunk<
void, void,
{ site: keyof SharingSettings; value: boolean }, { site: keyof SharingSettings; value: boolean },
@@ -151,6 +173,14 @@ export const userSlice = createSlice({
if (!state.settings) return if (!state.settings) return
state.settings.alwaysScrollToEntry = action.meta.arg state.settings.alwaysScrollToEntry = action.meta.arg
}) })
builder.addCase(changeMarkAllAsReadConfirmation.pending, (state, action) => {
if (!state.settings) return
state.settings.markAllAsReadConfirmation = action.meta.arg
})
builder.addCase(changeCustomContextMenu.pending, (state, action) => {
if (!state.settings) return
state.settings.customContextMenu = action.meta.arg
})
builder.addCase(changeSharingSetting.pending, (state, action) => { builder.addCase(changeSharingSetting.pending, (state, action) => {
if (!state.settings) return if (!state.settings) return
state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value
@@ -162,6 +192,8 @@ export const userSlice = createSlice({
changeShowRead.fulfilled, changeShowRead.fulfilled,
changeScrollMarks.fulfilled, changeScrollMarks.fulfilled,
changeAlwaysScrollToEntry.fulfilled, changeAlwaysScrollToEntry.fulfilled,
changeMarkAllAsReadConfirmation.fulfilled,
changeCustomContextMenu.fulfilled,
changeSharingSetting.fulfilled changeSharingSetting.fulfilled
), ),
() => { () => {

View File

@@ -3,38 +3,6 @@ export interface AddCategoryRequest {
parentId?: string parentId?: string
} }
export interface ApplicationSettings {
publicUrl: string
allowRegistrations: boolean
createDemoAccount: boolean
googleAnalyticsTrackingCode?: string
googleAuthKey?: string
backgroundThreads: number
databaseUpdateThreads: number
smtpHost?: string
smtpPort?: number
smtpTls?: boolean
smtpUserName?: string
smtpPassword?: string
smtpFromAddress?: string
graphiteEnabled?: boolean
graphitePrefix?: string
graphiteHost?: string
graphitePort?: number
graphiteInterval?: number
heavyLoad: boolean
pubsubhubbub: boolean
imageProxyEnabled: boolean
queryTimeout: number
keepStatusDays: number
maxFeedCapacity: number
refreshIntervalMinutes: number
cache: ApplicationSettingsCache
announcement?: string
userAgent?: string
unreadThreshold?: Date
}
export interface Category { export interface Category {
id: string id: string
parentId?: string parentId?: string
@@ -234,6 +202,8 @@ export interface Settings {
customJs?: string customJs?: string
scrollSpeed: number scrollSpeed: number
alwaysScrollToEntry: boolean alwaysScrollToEntry: boolean
markAllAsReadConfirmation: boolean
customContextMenu: boolean
sharingSettings: SharingSettings sharingSettings: SharingSettings
} }
@@ -300,8 +270,6 @@ export interface UserModel {
admin: boolean admin: boolean
} }
export type ApplicationSettingsCache = "NOOP" | "REDIS"
export type ReadingMode = "all" | "unread" export type ReadingMode = "all" | "unread"
export type ReadingOrder = "asc" | "desc" export type ReadingOrder = "asc" | "desc"

View File

@@ -0,0 +1,36 @@
import { Trans } from "@lingui/macro"
import { Box, Dialog, Text } from "@mantine/core"
import { useAppSelector } from "app/store"
import { Content } from "components/content/Content"
import { useAsync } from "react-async-hook"
import useLocalStorage from "use-local-storage"
const sha256Hex = async (input: string | undefined) => {
const data = new TextEncoder().encode(input)
const buffer = await crypto.subtle.digest("SHA-256", data)
const array = Array.from(new Uint8Array(buffer))
return array.map(b => b.toString(16).padStart(2, "0")).join("")
}
export function AnnouncementDialog() {
const announcement = useAppSelector(state => state.server.serverInfos?.announcement)
const announcementHash = useAsync(sha256Hex, [announcement]).result
const [localStorageHash, setLocalStorageHash] = useLocalStorage("announcement-hash", "no-hash")
const opened = !!announcementHash && announcementHash !== localStorageHash
const onClosed = () => setLocalStorageHash(announcementHash)
if (!announcement) return null
return (
<Dialog opened={opened} withCloseButton onClose={onClosed} size="xl" radius="md">
<Box>
<Text weight="bold">
<Trans>Announcement</Trans>
</Text>
</Box>
<Box>
<Content content={announcement} />
</Box>
</Dialog>
)
}

View File

@@ -161,6 +161,20 @@ export function KeyboardShortcutsHelp() {
</Kbd> </Kbd>
</td> </td>
</tr> </tr>
<tr>
<td>
<Trans>Show native menu (desktop)</Trans>
</td>
<td>
<Kbd>
<Trans>Shift</Trans>
</Kbd>
<span> + </span>
<Kbd>
<Trans>Right click</Trans>
</Kbd>
</td>
</tr>
<tr> <tr>
<td> <td>
<Trans>Show entry menu (mobile)</Trans> <Trans>Show entry menu (mobile)</Trans>

View File

@@ -1,4 +1,5 @@
import { Trans } from "@lingui/macro" import { Trans } from "@lingui/macro"
import { Box } from "@mantine/core"
import { openModal } from "@mantine/modals" import { openModal } from "@mantine/modals"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { import {
@@ -31,9 +32,11 @@ export function FeedEntries() {
const entriesTimestamp = useAppSelector(state => state.entries.timestamp) const entriesTimestamp = useAppSelector(state => state.entries.timestamp)
const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId) const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId)
const hasMore = useAppSelector(state => state.entries.hasMore) const hasMore = useAppSelector(state => state.entries.hasMore)
const loading = useAppSelector(state => state.entries.loading)
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks) const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
const scrollingToEntry = useAppSelector(state => state.entries.scrollingToEntry) const scrollingToEntry = useAppSelector(state => state.entries.scrollingToEntry)
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible) const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
const customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
const { viewMode } = useViewMode() const { viewMode } = useViewMode()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { openLinkInBackgroundTab } = useBrowserExtension() const { openLinkInBackgroundTab } = useBrowserExtension()
@@ -62,6 +65,8 @@ export function FeedEntries() {
const contextMenu = useContextMenu() const contextMenu = useContextMenu()
const headerRightClicked = (entry: ExpendableEntry, event: React.MouseEvent) => { const headerRightClicked = (entry: ExpendableEntry, event: React.MouseEvent) => {
if (event.shiftKey || !customContextMenu) return
event.preventDefault() event.preventDefault()
contextMenu.show({ contextMenu.show({
id: Constants.dom.entryContextMenuId(entry), id: Constants.dom.entryContextMenuId(entry),
@@ -71,6 +76,10 @@ export function FeedEntries() {
const bodyClicked = (entry: ExpendableEntry) => { const bodyClicked = (entry: ExpendableEntry) => {
if (viewMode !== "expanded") return if (viewMode !== "expanded") return
// entry is already selected
if (entry.id === selectedEntryId) return
dispatch( dispatch(
selectEntry({ selectEntry({
entry, entry,
@@ -276,9 +285,9 @@ export function FeedEntries() {
<InfiniteScroll <InfiniteScroll
id="entries" id="entries"
initialLoad={false} initialLoad={false}
loadMore={() => dispatch(loadMoreEntries())} loadMore={() => !loading && dispatch(loadMoreEntries())}
hasMore={hasMore} hasMore={hasMore}
loader={<Loader key={0} />} loader={<Box key={0}>{loading && <Loader />}</Box>}
> >
{entries.map(entry => ( {entries.map(entry => (
<div <div

View File

@@ -13,8 +13,27 @@ export function MarkAllAsReadButton(props: { iconSize: number }) {
const source = useAppSelector(state => state.entries.source) const source = useAppSelector(state => state.entries.source)
const sourceLabel = useAppSelector(state => state.entries.sourceLabel) const sourceLabel = useAppSelector(state => state.entries.sourceLabel)
const entriesTimestamp = useAppSelector(state => state.entries.timestamp) ?? Date.now() const entriesTimestamp = useAppSelector(state => state.entries.timestamp) ?? Date.now()
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const buttonClicked = () => {
if (markAllAsReadConfirmation) {
setThreshold(0)
setOpened(true)
} else {
dispatch(
markAllEntries({
sourceType: source.type,
req: {
id: source.id,
read: true,
olderThan: entriesTimestamp,
},
})
)
}
}
return ( return (
<> <>
<Modal opened={opened} onClose={() => setOpened(false)} title={<Trans>Mark all entries as read</Trans>}> <Modal opened={opened} onClose={() => setOpened(false)} title={<Trans>Mark all entries as read</Trans>}>
@@ -70,14 +89,7 @@ export function MarkAllAsReadButton(props: { iconSize: number }) {
</Group> </Group>
</Stack> </Stack>
</Modal> </Modal>
<ActionButton <ActionButton icon={<TbChecks size={props.iconSize} />} label={<Trans>Mark all as read</Trans>} onClick={buttonClicked} />
icon={<TbChecks size={props.iconSize} />}
label={<Trans>Mark all as read</Trans>}
onClick={() => {
setThreshold(0)
setOpened(true)
}}
/>
</> </>
) )
} }

View File

@@ -3,7 +3,9 @@ import { Divider, Select, SimpleGrid, Stack, Switch } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { import {
changeAlwaysScrollToEntry, changeAlwaysScrollToEntry,
changeCustomContextMenu,
changeLanguage, changeLanguage,
changeMarkAllAsReadConfirmation,
changeScrollMarks, changeScrollMarks,
changeScrollSpeed, changeScrollSpeed,
changeSharingSetting, changeSharingSetting,
@@ -19,6 +21,8 @@ export function DisplaySettings() {
const showRead = useAppSelector(state => state.user.settings?.showRead) const showRead = useAppSelector(state => state.user.settings?.showRead)
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks) const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
const alwaysScrollToEntry = useAppSelector(state => state.user.settings?.alwaysScrollToEntry) const alwaysScrollToEntry = useAppSelector(state => state.user.settings?.alwaysScrollToEntry)
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
const customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@@ -58,6 +62,18 @@ export function DisplaySettings() {
onChange={e => dispatch(changeScrollMarks(e.currentTarget.checked))} onChange={e => dispatch(changeScrollMarks(e.currentTarget.checked))}
/> />
<Switch
label={<Trans>Show confirmation when marking all entries as read</Trans>}
checked={markAllAsReadConfirmation}
onChange={e => dispatch(changeMarkAllAsReadConfirmation(e.currentTarget.checked))}
/>
<Switch
label={<Trans>Show CommaFeed's own context menu on right click</Trans>}
checked={customContextMenu}
onChange={e => dispatch(changeCustomContextMenu(e.currentTarget.checked))}
/>
<Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" /> <Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" />
<SimpleGrid cols={2}> <SimpleGrid cols={2}>

View File

@@ -1,4 +1,7 @@
import { useMediaQuery } from "@mantine/hooks" import { useMediaQuery } from "@mantine/hooks"
import { Constants } from "app/constants" import { Constants } from "app/constants"
export const useMobile = (breakpoint: string = Constants.layout.mobileBreakpoint) => !useMediaQuery(`(min-width: ${breakpoint})`) export const useMobile = (breakpoint: string = Constants.layout.mobileBreakpoint) =>
!useMediaQuery(`(min-width: ${breakpoint})`, undefined, {
getInitialValueInEffect: false,
})

View File

@@ -87,6 +87,10 @@ msgstr "ملف opml هو ملف XML يحتوي على عناوين URL للتغ
msgid "Analyze feed" msgid "Analyze feed"
msgstr "تحليل التغذية" msgstr "تحليل التغذية"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "مفتاح API" msgstr "مفتاح API"
@@ -665,6 +669,7 @@ msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "مشاركة"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "مشاركة المواقع" msgstr "مشاركة المواقع"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "الحلقة" msgstr "الحلقة"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "إظهار موجز ويب والفئات التي لا تحتوي عل
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "إظهار تعليمات اختصار لوحة المفاتيح" msgstr "إظهار تعليمات اختصار لوحة المفاتيح"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Un fitxer opml és un fitxer XML que conté URL i categories de canals.
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analitzar el feed" msgstr "Analitzar el feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "clau API" msgstr "clau API"
@@ -665,6 +669,7 @@ msgstr "Els registres estan tancats en aquesta instància de CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Comparteix"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Compartir llocs" msgstr "Compartir llocs"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "canvi" msgstr "canvi"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Mostra feeds i categories sense entrades no llegides"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Mostra l'ajuda de la drecera del teclat" msgstr "Mostra l'ajuda de la drecera del teclat"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Soubor opml je soubor XML obsahující adresy URL a kategorie zdrojů. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyzujte krmivo" msgstr "Analyzujte krmivo"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "Klíč API" msgstr "Klíč API"
@@ -665,6 +669,7 @@ msgstr "V této instanci CommaFeed jsou registrace uzavřeny"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Sdílejte"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Stránky pro sdílení" msgstr "Stránky pro sdílení"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Směna" msgstr "Směna"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Zobrazit kanály a kategorie bez nepřečtených položek"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Zobrazit nápovědu ke klávesovým zkratkám" msgstr "Zobrazit nápovědu ke klávesovým zkratkám"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Mae ffeil opml yn ffeil XML sy'n cynnwys URLs porthiant a chategorïau.
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Dadansoddi porthiant" msgstr "Dadansoddi porthiant"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "Allwedd API" msgstr "Allwedd API"
@@ -665,6 +669,7 @@ msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Rhannu"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Rhannu gwefannau" msgstr "Rhannu gwefannau"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "shifft" msgstr "shifft"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Dangos ffrydiau a chategorïau heb unrhyw gofnodion heb eu darllen"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Dangos cymorth llwybr byr bysellfwrdd" msgstr "Dangos cymorth llwybr byr bysellfwrdd"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "En opml-fil er en XML-fil, der indeholder feed-URL'er og kategorier. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyser foder" msgstr "Analyser foder"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-nøgle" msgstr "API-nøgle"
@@ -665,6 +669,7 @@ msgstr "Registreringer er lukket på denne CommaFeed-instans"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Del"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Delingssider" msgstr "Delingssider"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Vis feeds og kategorier uden ulæste poster"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Vis hjælp til tastaturgenveje" msgstr "Vis hjælp til tastaturgenveje"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Eine opml-Datei ist eine XML-Datei, die Feed-URLs und Kategorien enthäl
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Feed analysieren" msgstr "Feed analysieren"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-Schlüssel" msgstr "API-Schlüssel"
@@ -665,6 +669,7 @@ msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen"
msgid "REST API" msgid "REST API"
msgstr "REST-API" msgstr "REST-API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Teilen"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Seiten teilen" msgstr "Seiten teilen"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Verschiebung" msgstr "Verschiebung"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Feeds und Kategorien ohne ungelesene Einträge anzeigen"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Tastenkürzel-Hilfe anzeigen" msgstr "Tastenkürzel-Hilfe anzeigen"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "An opml file is an XML file containing feed URLs and categories. You can
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyze feed" msgstr "Analyze feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr "Announcement"
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API key" msgstr "API key"
@@ -665,6 +669,7 @@ msgstr "Registrations are closed on this CommaFeed instance"
msgid "REST API" msgid "REST API"
msgstr "REST API" msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "Right click" msgstr "Right click"
@@ -716,11 +721,20 @@ msgstr "Share"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Sharing sites" msgstr "Sharing sites"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Shift" msgstr "Shift"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr "Show CommaFeed's own context menu on right click"
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr "Show confirmation when marking all entries as read"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "Show entry menu (desktop)" msgstr "Show entry menu (desktop)"
@@ -737,6 +751,10 @@ msgstr "Show feeds and categories with no unread entries"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Show keyboard shortcut help" msgstr "Show keyboard shortcut help"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr "Show native menu (desktop)"
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Un archivo opml es un archivo XML que contiene categorías y direcciones
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analizar alimentación" msgstr "Analizar alimentación"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "clave API" msgstr "clave API"
@@ -665,6 +669,7 @@ msgstr "Los registros están cerrados en esta instancia de CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Compartir"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Compartir sitios" msgstr "Compartir sitios"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Cambio" msgstr "Cambio"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Mostrar feeds y categorías sin entradas no leídas"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Mostrar ayuda de atajo de teclado" msgstr "Mostrar ayuda de atajo de teclado"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "یک فایل opml یک فایل XML است که حاوی آدرس‌ه
msgid "Analyze feed" msgid "Analyze feed"
msgstr "خوراک را تجزیه و تحلیل کنید" msgstr "خوراک را تجزیه و تحلیل کنید"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "کلید API" msgstr "کلید API"
@@ -665,6 +669,7 @@ msgstr "ثبت نام در این نمونه CommaFeed بسته شده است"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "به اشتراک بگذارید"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "اشتراک گذاری سایت ها" msgstr "اشتراک گذاری سایت ها"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "شیفت" msgstr "شیفت"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "فیدها و دسته ها را بدون ورودی خوانده نشد
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "نمایش راهنمایی میانبر صفحه کلید" msgstr "نمایش راهنمایی میانبر صفحه کلید"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Opml-tiedosto on XML-tiedosto, joka sisältää syötteen URL-osoitteet
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analysoi syöte" msgstr "Analysoi syöte"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-avain" msgstr "API-avain"
@@ -665,6 +669,7 @@ msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Jaa"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Sivustojen jakaminen" msgstr "Sivustojen jakaminen"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Vaihto" msgstr "Vaihto"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Näytä syötteet ja luokat ilman lukemattomia merkintöjä"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Näytä pikanäppäimen ohje" msgstr "Näytä pikanäppäimen ohje"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Un fichier OPML est un fichier XML contenant des URL de flux et des cat
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyser le flux" msgstr "Analyser le flux"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr "Annonces"
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "Clé API" msgstr "Clé API"
@@ -264,7 +268,7 @@ msgstr "Affichage"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/app/DonatePage.tsx #: src/pages/app/DonatePage.tsx
msgid "Donate" msgid "Donate"
msgstr "Faites un don" msgstr "Faire un don"
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "Download" msgid "Download"
@@ -665,6 +669,7 @@ msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "Clic droit" msgstr "Clic droit"
@@ -716,11 +721,20 @@ msgstr "Partager"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Sites de partage" msgstr "Sites de partage"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Maj" msgstr "Maj"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr "Demander une confirmation avant de tout marquer comme lu"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "Afficher les options de l'entrée (ordinateur)" msgstr "Afficher les options de l'entrée (ordinateur)"
@@ -737,6 +751,10 @@ msgstr "Afficher les flux et les catégories pour lesquels tout est déjà lu"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Montrer les raccourcis clavier" msgstr "Montrer les raccourcis clavier"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr "Afficher les options du navigateur (ordinateur)"
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Un ficheiro opml é un ficheiro XML que contén URL e categorías de fon
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analizar feed" msgstr "Analizar feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "chave API" msgstr "chave API"
@@ -665,6 +669,7 @@ msgstr "Os rexistros están pechados nesta instancia de CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Compartir"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Compartir sitios" msgstr "Compartir sitios"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "quendas" msgstr "quendas"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Mostrar fontes e categorías sen entradas sen ler"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Mostrar axuda do atallo do teclado" msgstr "Mostrar axuda do atallo do teclado"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Az opml-fájl olyan XML-fájl, amely feed URL-címeket és kategóriáka
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Hírcsatorna elemzése" msgstr "Hírcsatorna elemzése"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API kulcs" msgstr "API kulcs"
@@ -665,6 +669,7 @@ msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Oszd meg"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Webhelyek megosztása" msgstr "Webhelyek megosztása"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "" msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Hírcsatornák és kategóriák megjelenítése olvasatlan bejegyzések
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "A billentyűparancsok súgójának megjelenítése" msgstr "A billentyűparancsok súgójának megjelenítése"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "File opml adalah file XML yang berisi URL dan kategori feed. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analisis umpan" msgstr "Analisis umpan"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "kunci API" msgstr "kunci API"
@@ -665,6 +669,7 @@ msgstr "Pendaftaran ditutup pada instans CommaFeed ini"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Bagikan"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Berbagi situs" msgstr "Berbagi situs"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Pergeseran" msgstr "Pergeseran"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Tampilkan umpan dan kategori tanpa entri yang belum dibaca"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Tampilkan bantuan pintasan keyboard" msgstr "Tampilkan bantuan pintasan keyboard"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Un file opml è un file XML contenente URL e categorie di feed. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analizza feed" msgstr "Analizza feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "Chiave API" msgstr "Chiave API"
@@ -665,6 +669,7 @@ msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Condividi"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Condivisione di siti" msgstr "Condivisione di siti"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Cambio" msgstr "Cambio"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Mostra feed e categorie senza voci non lette"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Mostra la guida alle scorciatoie da tastiera" msgstr "Mostra la guida alle scorciatoie da tastiera"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "opml ファイルは、フィードの URL とカテゴリを含む XML
msgid "Analyze feed" msgid "Analyze feed"
msgstr "フィードを分析する" msgstr "フィードを分析する"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "APIキー" msgstr "APIキー"
@@ -665,6 +669,7 @@ msgstr "この CommaFeed インスタンスの登録は終了しています"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "シェア"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "共有サイト" msgstr "共有サイト"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "シフト" msgstr "シフト"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "未読エントリのないフィードとカテゴリを表示する"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "キーボード ショートカットのヘルプを表示" msgstr "キーボード ショートカットのヘルプを表示"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "opml 파일은 피드 URL과 카테고리를 포함하는 XML 파일입
msgid "Analyze feed" msgid "Analyze feed"
msgstr "피드 분석" msgstr "피드 분석"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API 키" msgstr "API 키"
@@ -665,6 +669,7 @@ msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다."
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "공유"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "사이트 공유" msgstr "사이트 공유"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "시프트" msgstr "시프트"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "읽지 않은 항목이 없는 피드 및 카테고리 표시"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "키보드 단축키 도움말 표시" msgstr "키보드 단축키 도움말 표시"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Fail opml ialah fail XML yang mengandungi URL suapan dan kategori. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Menganalisis suapan" msgstr "Menganalisis suapan"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "Kunci API" msgstr "Kunci API"
@@ -665,6 +669,7 @@ msgstr "Pendaftaran ditutup pada contoh CommaFeed ini"
msgid "REST API" msgid "REST API"
msgstr "REHAT API" msgstr "REHAT API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Kongsi"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Berkongsi tapak" msgstr "Berkongsi tapak"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Anjakan" msgstr "Anjakan"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Tunjukkan suapan dan kategori tanpa entri yang belum dibaca"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Tunjukkan bantuan pintasan papan kekunci" msgstr "Tunjukkan bantuan pintasan papan kekunci"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "En opml-fil er en XML-fil som inneholder feed-URLer og kategorier. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyser feed" msgstr "Analyser feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-nøkkel" msgstr "API-nøkkel"
@@ -665,6 +669,7 @@ msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Del"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Delingssider" msgstr "Delingssider"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Vis feeder og kategorier uten uleste oppføringer"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Vis hurtigtasthjelp" msgstr "Vis hurtigtasthjelp"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Een opml-bestand is een XML-bestand met feed-URL's en categorieën. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyseer feed" msgstr "Analyseer feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-sleutel" msgstr "API-sleutel"
@@ -665,6 +669,7 @@ msgstr "Registraties zijn gesloten op deze CommaFeed-instantie"
msgid "REST API" msgid "REST API"
msgstr "REST-API" msgstr "REST-API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Delen"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Sites delen" msgstr "Sites delen"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "" msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Toon feeds en categorieën zonder ongelezen items"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Toon hulp bij sneltoetsen" msgstr "Toon hulp bij sneltoetsen"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "En opml-fil er en XML-fil som inneholder feed-URLer og kategorier. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyser feed" msgstr "Analyser feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-nøkkel" msgstr "API-nøkkel"
@@ -665,6 +669,7 @@ msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Del"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Delingssider" msgstr "Delingssider"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Vis feeder og kategorier uten uleste oppføringer"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Vis hurtigtasthjelp" msgstr "Vis hurtigtasthjelp"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Plik opml to plik XML zawierający adresy URL i kategorie kanałów. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analizuj kanał" msgstr "Analizuj kanał"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "klucz API" msgstr "klucz API"
@@ -665,6 +669,7 @@ msgstr "Rejestracje są zamknięte w tej instancji CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Udostępnij"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Udostępnianie witryn" msgstr "Udostępnianie witryn"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "zmiana" msgstr "zmiana"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Pokaż kanały i kategorie bez nieprzeczytanych wpisów"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Pokaż pomoc dotyczącą skrótów klawiaturowych" msgstr "Pokaż pomoc dotyczącą skrótów klawiaturowych"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Um arquivo opml é um arquivo XML contendo URLs e categorias de feed. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analisar feed" msgstr "Analisar feed"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "chave de API" msgstr "chave de API"
@@ -665,6 +669,7 @@ msgstr "Os registros estão fechados nesta instância do CommaFeed"
msgid "REST API" msgid "REST API"
msgstr "API REST" msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Compartilhar"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Compartilhando sites" msgstr "Compartilhando sites"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Mudar" msgstr "Mudar"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Mostrar feeds e categorias sem entradas não lidas"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Mostrar ajuda de atalho de teclado" msgstr "Mostrar ajuda de atalho de teclado"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "OPML-файл — это XML-файл, содержащий URL-адре
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Анализ канала" msgstr "Анализ канала"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "ключ API" msgstr "ключ API"
@@ -665,6 +669,7 @@ msgstr "Регистрация закрыта для этого экземпля
msgid "REST API" msgid "REST API"
msgstr "ОТДЫХА API" msgstr "ОТДЫХА API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Поделиться"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Обмен сайтами" msgstr "Обмен сайтами"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Сдвиг" msgstr "Сдвиг"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Показать каналы и категории без непроч
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Показать справку по сочетаниям клавиш." msgstr "Показать справку по сочетаниям клавиш."
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "Súbor opml je súbor XML obsahujúci adresy URL kanálov a kategórie.
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analyzujte krmivo" msgstr "Analyzujte krmivo"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "Kľúč API" msgstr "Kľúč API"
@@ -665,6 +669,7 @@ msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Zdieľať"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Zdieľanie stránok" msgstr "Zdieľanie stránok"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Smena" msgstr "Smena"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Zobraziť kanály a kategórie bez neprečítaných záznamov"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Zobraziť pomoc s klávesovými skratkami" msgstr "Zobraziť pomoc s klávesovými skratkami"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -87,6 +87,10 @@ msgstr "En opml-fil är en XML-fil som innehåller feed-URL:er och kategorier. "
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Analysera foder" msgstr "Analysera foder"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API-nyckel" msgstr "API-nyckel"
@@ -665,6 +669,7 @@ msgstr "Registreringar är stängda på denna CommaFeed-instans"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "Dela"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Delningssajter" msgstr "Delningssajter"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Skift" msgstr "Skift"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "Visa flöden och kategorier utan olästa poster"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Visa kortkommandohjälp" msgstr "Visa kortkommandohjälp"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -15,15 +15,15 @@ msgstr ""
#: src/components/content/add/CategorySelect.tsx #: src/components/content/add/CategorySelect.tsx
msgid "{0} (in {1})" msgid "{0} (in {1})"
msgstr "" msgstr "{0} ({1} içinde)"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>." msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
msgstr "" msgstr "<0>CommaFeed açık kaynak kodlu bir proje. Kaynak kodları </0><1>GitHub</1>'da."
#: src/pages/app/FeedDetailsPage.tsx #: src/pages/app/FeedDetailsPage.tsx
msgid "<0>Complete syntax is available </0><1>here</1>." msgid "<0>Complete syntax is available </0><1>here</1>."
msgstr "" msgstr "<0>Tüm sözdizimi </0><1>burada</1>."
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
msgid "<0>Have an account?</0><1>Log in!</1>" msgid "<0>Have an account?</0><1>Log in!</1>"
@@ -31,7 +31,7 @@ msgstr "<0>Hesabınız var mı?</0><1>Giriş yapın!</1>"
#: src/pages/app/DonatePage.tsx #: src/pages/app/DonatePage.tsx
msgid "<0>Hey,</0><1>I'm Jérémie from Belgium and I've been working on CommaFeed in my free time for over 10 years now. Thanks for taking an interest in helping me continue supporting CommaFeed.</1>" msgid "<0>Hey,</0><1>I'm Jérémie from Belgium and I've been working on CommaFeed in my free time for over 10 years now. Thanks for taking an interest in helping me continue supporting CommaFeed.</1>"
msgstr "" msgstr "<0>Merhaba,</0><1>Ben Belçika'dan Jérémie ve 10 yıldır boş zamanlarımda CommaFeed üzerinde çalışıyorum. CommaFeed'i desteklememe ilgi gösterdiğiniz için teşekkürler.</1>"
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "<0>Need an account?</0><1>Sign up!</1>" msgid "<0>Need an account?</0><1>Sign up!</1>"
@@ -73,7 +73,7 @@ msgstr "Tümü"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Always scroll selected entry to the top of the page, even if it fits entirely on screen" msgid "Always scroll selected entry to the top of the page, even if it fits entirely on screen"
msgstr "" msgstr "Seçilen girişi her zaman sayfanın üstüne kaydır, ekrana tamamen sığsa bile"
#: src/pages/auth/PasswordRecoveryPage.tsx #: src/pages/auth/PasswordRecoveryPage.tsx
msgid "An email has been sent if this address was registered. Check your inbox." msgid "An email has been sent if this address was registered. Check your inbox."
@@ -87,6 +87,10 @@ msgstr "Bir opml dosyası, besleme URL'lerini ve kategorilerini içeren bir XML
msgid "Analyze feed" msgid "Analyze feed"
msgstr "Feed'i analiz et" msgstr "Feed'i analiz et"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr "Duyuru"
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API anahtarı" msgstr "API anahtarı"
@@ -137,7 +141,7 @@ msgstr ""
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Browser extention" msgid "Browser extention"
msgstr "" msgstr "Tarayıcı eklentisi"
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/content/add/AddCategory.tsx #: src/components/content/add/AddCategory.tsx
@@ -173,7 +177,7 @@ msgstr "Feed'in çalışıp çalışmadığını kontrol edin"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "CommaFeed browser extension version {browserExtensionVersion}." msgid "CommaFeed browser extension version {browserExtensionVersion}."
msgstr "" msgstr "CommaFeed tarayıcı eklentisi sürüm {browserExtensionVersion}."
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item" msgid "CommaFeed next unread item"
@@ -181,7 +185,7 @@ msgstr "CommaFeed sonraki okunmamış öğe"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "CommaFeed version {version} ({revision})." msgid "CommaFeed version {version} ({revision})."
msgstr "" msgstr "CommaFeed sürüm {version} ({revision})."
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Compact" msgid "Compact"
@@ -209,7 +213,7 @@ msgstr "Etiket oluştur: {query}"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl" msgid "Ctrl"
msgstr "" msgstr "Ctrl"
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "Current password" msgid "Current password"
@@ -217,15 +221,15 @@ msgstr "Geçerli şifre"
#: src/pages/app/SettingsPage.tsx #: src/pages/app/SettingsPage.tsx
msgid "Custom code" msgid "Custom code"
msgstr "" msgstr "Özel kod"
#: src/components/settings/CustomCodeSettings.tsx #: src/components/settings/CustomCodeSettings.tsx
msgid "Custom CSS rules that will be applied" msgid "Custom CSS rules that will be applied"
msgstr "" msgstr "Uygulanacak özel CSS kuralları"
#: src/components/settings/CustomCodeSettings.tsx #: src/components/settings/CustomCodeSettings.tsx
msgid "Custom JS code that will be executed on page load" msgid "Custom JS code that will be executed on page load"
msgstr "" msgstr "Sayfa yüklendiğinde çalıştırılacak özel JS kodu"
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
msgid "Date created" msgid "Date created"
@@ -323,7 +327,7 @@ msgstr "Aboneliklerinizi ve kategorilerinizi diğer besleme okuma hizmetlerinde
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx
msgid "Extension options" msgid "Extension options"
msgstr "" msgstr "Eklenti ayarları"
#: src/components/content/add/Subscribe.tsx #: src/components/content/add/Subscribe.tsx
msgid "Feed name" msgid "Feed name"
@@ -337,7 +341,7 @@ msgstr "Feed URL'si"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Fetch all my feeds now" msgid "Fetch all my feeds now"
msgstr "" msgstr "Tüm feed'lerimi şimdi çek"
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "file is required" msgid "file is required"
@@ -369,7 +373,7 @@ msgstr "Oluşturulan besleme url'si"
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
msgid "Go to {0}" msgid "Go to {0}"
msgstr "" msgstr "{0}'a git"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Go to the All view" msgid "Go to the All view"
@@ -463,7 +467,7 @@ msgstr "Çıkış"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Long press" msgid "Long press"
msgstr "" msgstr "Uzun bas"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/admin/AdminUsersPage.tsx #: src/pages/admin/AdminUsersPage.tsx
@@ -495,7 +499,7 @@ msgstr "Metrikler"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Middle click" msgid "Middle click"
msgstr "" msgstr "Orta tuş ile tıkla"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Move the page down" msgid "Move the page down"
@@ -560,7 +564,7 @@ msgstr "Hata!"
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
msgid "Open CommaFeed" msgid "Open CommaFeed"
msgstr "" msgstr "CommaFeed'i aç"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open current entry in a new tab" msgid "Open current entry in a new tab"
@@ -576,11 +580,11 @@ msgstr "Bağlantıyı aç"
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new background tab" msgid "Open link in new background tab"
msgstr "" msgstr "Bağlantıyı arkaplanda yeni sekmede aç"
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
msgid "Open link in new tab" msgid "Open link in new tab"
msgstr "" msgstr "Bağlantıyı yeni sekmede aç"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Open next entry" msgid "Open next entry"
@@ -596,7 +600,7 @@ msgstr "Geçerli girişi aç/kapat"
#: src/pages/app/AddPage.tsx #: src/pages/app/AddPage.tsx
msgid "OPML" msgid "OPML"
msgstr "" msgstr "OPML"
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "OPML export" msgid "OPML export"
@@ -642,7 +646,7 @@ msgstr "Konum"
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
msgid "Previous" msgid "Previous"
msgstr "" msgstr "Önceki"
#: src/pages/app/SettingsPage.tsx #: src/pages/app/SettingsPage.tsx
msgid "Profile" msgid "Profile"
@@ -663,11 +667,12 @@ msgstr "Bu CommaFeed örneğinde kayıtlar kapalı"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr "Sağ tık"
#: src/components/admin/UserEdit.tsx #: src/components/admin/UserEdit.tsx
#: src/components/settings/CustomCodeSettings.tsx #: src/components/settings/CustomCodeSettings.tsx
@@ -716,18 +721,27 @@ msgstr "Paylaş"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "Siteleri paylaşma" msgstr "Siteleri paylaşma"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "Vardiya" msgstr "Vardiya"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr "Sağ tıkta CommaFeed'in kendi menüsünü göster"
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr "Tüm girişleri okundu işaretlerken onay iste"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr "Giriş menüsünü göster (masaüstü)"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (mobile)" msgid "Show entry menu (mobile)"
msgstr "" msgstr "Giriş menüsünü göster (mobil)"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show feeds and categories with no unread entries" msgid "Show feeds and categories with no unread entries"
@@ -737,6 +751,10 @@ msgstr "Okunmamış girişi olmayan beslemeleri ve kategorileri göster"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "Klavye kısayolu yardımını göster" msgstr "Klavye kısayolu yardımını göster"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr "Orijinal tarayıcı menüsünü göster (masaüstü)"
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx
@@ -782,7 +800,7 @@ msgstr "Başarı"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Swipe header to the right" msgid "Swipe header to the right"
msgstr "" msgstr "Başlığı sağa kaydır"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx
@@ -792,7 +810,7 @@ msgstr "Karanlık temaya geç"
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx
msgid "Switch to light theme" msgid "Switch to light theme"
msgstr "Açık temaya geç" msgstr "Aydınlık temaya geç"
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
msgid "Tags" msgid "Tags"
@@ -812,7 +830,7 @@ msgstr "Geçerli girişin okuma durumunu değiştir"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Toggle sidebar" msgid "Toggle sidebar"
msgstr "" msgstr "Kenar çubuğunu göster/gizle"
#: src/pages/auth/LoginPage.tsx #: src/pages/auth/LoginPage.tsx
msgid "Try out CommaFeed with the demo account: demo/demo" msgid "Try out CommaFeed with the demo account: demo/demo"
@@ -820,11 +838,11 @@ msgstr "CommaFeed'i demo hesabıyla deneyin: demo/demo"
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx
msgid "Try the demo!" msgid "Try the demo!"
msgstr "" msgstr "Demo'yu deneyin!"
#: src/components/header/Header.tsx #: src/components/header/Header.tsx
msgid "Unread" msgid "Unread"
msgstr "Okunmadı" msgstr "Okunmamış"
#: src/components/content/FeedEntryContextMenu.tsx #: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx #: src/components/content/FeedEntryFooter.tsx
@@ -855,8 +873,8 @@ msgstr "Web sitesi"
#: src/pages/app/FeedEntriesPage.tsx #: src/pages/app/FeedEntriesPage.tsx
msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?" msgid "You don't have any subscriptions yet. Why not try adding one by clicking on the + sign at the top of the page?"
msgstr "Henüz aboneliğiniz yok. " msgstr "Henüz aboneliğiniz yok. Sayfanın üstündeki + işaretiyle feed ekleyebilirsiniz."
#: src/components/header/ProfileMenu.tsx #: src/components/header/ProfileMenu.tsx
msgid "Your feeds have been queued for refresh." msgid "Your feeds have been queued for refresh."
msgstr "" msgstr "Feed'leriniz yenileme için sıraya alındı."

View File

@@ -87,6 +87,10 @@ msgstr "opml 文件是包含提要 URL 和类别的 XML 文件。"
msgid "Analyze feed" msgid "Analyze feed"
msgstr "分析饲料" msgstr "分析饲料"
#: src/components/AnnouncementDialog.tsx
msgid "Announcement"
msgstr ""
#: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx
msgid "API key" msgid "API key"
msgstr "API 密钥" msgstr "API 密钥"
@@ -665,6 +669,7 @@ msgstr "此 CommaFeed 实例上的注册已关闭"
msgid "REST API" msgid "REST API"
msgstr "" msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click" msgid "Right click"
msgstr "" msgstr ""
@@ -716,11 +721,20 @@ msgstr "分享"
msgid "Sharing sites" msgid "Sharing sites"
msgstr "共享站点" msgstr "共享站点"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift" msgid "Shift"
msgstr "换档" msgstr "换档"
#: src/components/settings/DisplaySettings.tsx
msgid "Show CommaFeed's own context menu on right click"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Show confirmation when marking all entries as read"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
msgid "Show entry menu (desktop)" msgid "Show entry menu (desktop)"
msgstr "" msgstr ""
@@ -737,6 +751,10 @@ msgstr "显示没有未读条目的提要和类别"
msgid "Show keyboard shortcut help" msgid "Show keyboard shortcut help"
msgstr "显示键盘快捷键帮助" msgstr "显示键盘快捷键帮助"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Show native menu (desktop)"
msgstr ""
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx #: src/pages/WelcomePage.tsx

View File

@@ -18,6 +18,7 @@ import { redirectToAdd, redirectToRootCategory } from "app/slices/redirect"
import { reloadTree, setMobileMenuOpen, setSidebarWidth } from "app/slices/tree" import { reloadTree, setMobileMenuOpen, setSidebarWidth } from "app/slices/tree"
import { reloadProfile, reloadSettings, reloadTags } from "app/slices/user" import { reloadProfile, reloadSettings, reloadTags } from "app/slices/user"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { AnnouncementDialog } from "components/AnnouncementDialog"
import { Loader } from "components/Loader" import { Loader } from "components/Loader"
import { Logo } from "components/Logo" import { Logo } from "components/Logo"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "components/responsive/OnDesktop"
@@ -197,6 +198,7 @@ export default function Layout(props: LayoutProps) {
> >
<Box id="content" className={classes.mainContent}> <Box id="content" className={classes.mainContent}>
<Suspense fallback={<Loader />}> <Suspense fallback={<Loader />}>
<AnnouncementDialog />
<Outlet /> <Outlet />
</Suspense> </Suspense>
</Box> </Box>

View File

@@ -27,7 +27,10 @@ app:
# number of database updating threads # number of database updating threads
databaseUpdateThreads: 1 databaseUpdateThreads: 1
# rows to delete per query while cleaning up old entries
databaseCleanupBatchSize: 100
# settings for sending emails (password recovery) # settings for sending emails (password recovery)
smtpHost: localhost smtpHost: localhost
smtpPort: 25 smtpPort: 25
@@ -81,8 +84,12 @@ app:
# Database connection # Database connection
# ------------------- # -------------------
# for MariaDB
# driverClass is org.mariadb.jdbc.Driver
# url is jdbc:mariadb://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
#
# for MySQL # for MySQL
# driverClass is com.mysql.jdbc.Driver # driverClass is com.mysql.cj.jdbc.Driver
# url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true # url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
# #
# for PostgreSQL # for PostgreSQL

View File

@@ -6,28 +6,31 @@ app:
# whether to expose a robots.txt file that disallows web crawlers and search engine indexers # whether to expose a robots.txt file that disallows web crawlers and search engine indexers
hideFromWebCrawlers: true hideFromWebCrawlers: true
# whether to allow user registrations # whether to allow user registrations
allowRegistrations: false allowRegistrations: false
# whether to enable strict password validation (1 uppercase char, 1 lowercase char, 1 digit, 1 special char) # whether to enable strict password validation (1 uppercase char, 1 lowercase char, 1 digit, 1 special char)
strictPasswordPolicy: true strictPasswordPolicy: true
# create a demo account the first time the app starts # create a demo account the first time the app starts
createDemoAccount: false createDemoAccount: false
# put your google analytics tracking code here # put your google analytics tracking code here
googleAnalyticsTrackingCode: googleAnalyticsTrackingCode:
# put your google server key (used for youtube favicon fetching) # put your google server key (used for youtube favicon fetching)
googleAuthKey: googleAuthKey:
# number of http threads # number of http threads
backgroundThreads: 3 backgroundThreads: 3
# number of database updating threads # number of database updating threads
databaseUpdateThreads: 1 databaseUpdateThreads: 1
# rows to delete per query while cleaning up old entries
databaseCleanupBatchSize: 100
# settings for sending emails (password recovery) # settings for sending emails (password recovery)
smtpHost: smtpHost:
smtpPort: smtpPort:
@@ -43,28 +46,28 @@ app:
graphiteHost: "localhost" graphiteHost: "localhost"
graphitePort: 2003 graphitePort: 2003
graphiteInterval: 60 graphiteInterval: 60
# whether this commafeed instance has a lot of feeds to refresh # whether this commafeed instance has a lot of feeds to refresh
# leave this to false in almost all cases # leave this to false in almost all cases
heavyLoad: false heavyLoad: false
# minimum amount of time commafeed will wait before refreshing the same feed # minimum amount of time commafeed will wait before refreshing the same feed
refreshIntervalMinutes: 5 refreshIntervalMinutes: 5
# whether to enable pubsub # whether to enable pubsub
# probably not needed if refreshIntervalMinutes is low # probably not needed if refreshIntervalMinutes is low
pubsubhubbub: false pubsubhubbub: false
# if enabled, images in feed entries will be proxied through the server instead of accessed directly by the browser # if enabled, images in feed entries will be proxied through the server instead of accessed directly by the browser
# useful if commafeed is usually accessed through a restricting proxy # useful if commafeed is usually accessed through a restricting proxy
imageProxyEnabled: false imageProxyEnabled: false
# database query timeout (in milliseconds), 0 to disable # database query timeout (in milliseconds), 0 to disable
queryTimeout: 0 queryTimeout: 0
# time to keep unread statuses (in days), 0 to disable # time to keep unread statuses (in days), 0 to disable
keepStatusDays: 0 keepStatusDays: 0
# entries to keep per feed, old entries will be deleted, 0 to disable # entries to keep per feed, old entries will be deleted, 0 to disable
maxFeedCapacity: 500 maxFeedCapacity: 500
@@ -73,17 +76,21 @@ app:
# cache service to use, possible values are 'noop' and 'redis' # cache service to use, possible values are 'noop' and 'redis'
cache: noop cache: noop
# announcement string displayed on the main page # announcement string displayed on the main page
announcement: announcement:
# user-agent string that will be used by the http client, leave empty for the default one # user-agent string that will be used by the http client, leave empty for the default one
userAgent: userAgent:
# Database connection # Database connection
# ------------------- # -------------------
# for MariaDB
# driverClass is org.mariadb.jdbc.Driver
# url is jdbc:mariadb://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
#
# for MySQL # for MySQL
# driverClass is com.mysql.jdbc.Driver # driverClass is com.mysql.cj.jdbc.Driver
# url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true # url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
# #
# for PostgreSQL # for PostgreSQL
@@ -105,7 +112,7 @@ database:
minSize: 1 minSize: 1
maxSize: 50 maxSize: 50
maxConnectionAge: 30m maxConnectionAge: 30m
server: server:
applicationConnectors: applicationConnectors:
- type: http - type: http
@@ -115,7 +122,7 @@ server:
port: 8084 port: 8084
requestLog: requestLog:
appenders: [ ] appenders: [ ]
logging: logging:
level: ERROR level: ERROR
loggers: loggers:
@@ -131,7 +138,7 @@ logging:
archivedLogFilenamePattern: log/commafeed-%d.log archivedLogFilenamePattern: log/commafeed-%d.log
archivedFileCount: 5 archivedFileCount: 5
timeZone: UTC timeZone: UTC
# Redis pool configuration # Redis pool configuration
# (only used if app.cache is 'redis') # (only used if app.cache is 'redis')
# ----------------------------------- # -----------------------------------
@@ -140,8 +147,7 @@ redis:
port: 6379 port: 6379
# username is only required when using ACLs # username is only required when using ACLs
username: username:
password: password:
timeout: 2000 timeout: 2000
database: 0 database: 0
maxTotal: 500 maxTotal: 500

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>3.8.0</version> <version>3.9.0</version>
</parent> </parent>
<artifactId>commafeed-server</artifactId> <artifactId>commafeed-server</artifactId>
<name>CommaFeed Server</name> <name>CommaFeed Server</name>
@@ -142,6 +142,12 @@
<title>CommaFeed</title> <title>CommaFeed</title>
<version>${project.version}</version> <version>${project.version}</version>
</info> </info>
<securityDefinitions>
<securityDefinition>
<name>basicAuth</name>
<type>basic</type>
</securityDefinition>
</securityDefinitions>
<typesToSkip> <typesToSkip>
<typeToSkip>com.commafeed.backend.model.User</typeToSkip> <typeToSkip>com.commafeed.backend.model.User</typeToSkip>
</typesToSkip> </typesToSkip>
@@ -226,7 +232,7 @@
<dependency> <dependency>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed-client</artifactId> <artifactId>commafeed-client</artifactId>
<version>3.8.0</version> <version>3.9.0</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -444,6 +450,11 @@
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version> <version>8.0.33</version>
</dependency> </dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.1.4</version>
</dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
@@ -494,4 +505,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -7,6 +7,7 @@ import javax.validation.Valid;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
@@ -95,6 +96,11 @@ public class CommaFeedConfiguration extends Configuration {
@Valid @Valid
private Integer databaseUpdateThreads; private Integer databaseUpdateThreads;
@NotNull
@Positive
@Valid
private Integer databaseCleanupBatchSize = 100;
private String smtpHost; private String smtpHost;
private int smtpPort; private int smtpPort;
private boolean smtpTls; private boolean smtpTls;

View File

@@ -28,13 +28,10 @@ public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
return query().select(content).from(content).where(content.contentHash.eq(contentHash), content.titleHash.eq(titleHash)).fetch(); return query().select(content).from(content).where(content.contentHash.eq(contentHash), content.titleHash.eq(titleHash)).fetch();
} }
public int deleteWithoutEntries(int max) { public long deleteWithoutEntries(int max) {
JPQLQuery<Integer> subQuery = JPAExpressions.selectOne().from(entry).where(entry.content.id.eq(content.id)); JPQLQuery<Integer> subQuery = JPAExpressions.selectOne().from(entry).where(entry.content.id.eq(content.id));
List<FeedEntryContent> list = query().selectFrom(content).where(subQuery.notExists()).limit(max).fetch(); List<Long> ids = query().select(content.id).from(content).where(subQuery.notExists()).limit(max).fetch();
int deleted = list.size(); return deleteQuery(content).where(content.id.in(ids)).execute();
delete(list);
return deleted;
} }
} }

View File

@@ -48,7 +48,6 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
} }
public int delete(Long feedId, long max) { public int delete(Long feedId, long max) {
List<FeedEntry> list = query().selectFrom(entry).where(entry.feed.id.eq(feedId)).limit(max).fetch(); List<FeedEntry> list = query().selectFrom(entry).where(entry.feed.id.eq(feedId)).limit(max).fetch();
return delete(list); return delete(list);
} }

View File

@@ -270,8 +270,13 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return results; return results;
} }
public List<FeedEntryStatus> getOldStatuses(Date olderThan, int limit) { public long deleteOldStatuses(Date olderThan, int limit) {
return query().selectFrom(status).where(status.entryInserted.lt(olderThan), status.starred.isFalse()).limit(limit).fetch(); List<Long> ids = query().select(status.id)
.from(status)
.where(status.entryInserted.lt(olderThan), status.starred.isFalse())
.limit(limit)
.fetch();
return deleteQuery(status).where(status.id.in(ids)).execute();
} }
} }

View File

@@ -7,6 +7,7 @@ import org.hibernate.annotations.QueryHints;
import com.commafeed.backend.model.AbstractModel; import com.commafeed.backend.model.AbstractModel;
import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.EntityPath;
import com.querydsl.jpa.impl.JPADeleteClause;
import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import com.querydsl.jpa.impl.JPAUpdateClause; import com.querydsl.jpa.impl.JPAUpdateClause;
@@ -30,6 +31,10 @@ public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T>
return new JPAUpdateClause(currentSession(), entityPath); return new JPAUpdateClause(currentSession(), entityPath);
} }
protected JPADeleteClause deleteQuery(EntityPath<T> entityPath) {
return new JPADeleteClause(currentSession(), entityPath);
}
public void saveOrUpdate(T model) { public void saveOrUpdate(T model) {
persist(model); persist(model);
} }

View File

@@ -94,11 +94,10 @@ public class FeedRefreshWorker {
return new FeedRefreshWorkerResult(feed, Collections.emptyList()); return new FeedRefreshWorkerResult(feed, Collections.emptyList());
} catch (Exception e) { } catch (Exception e) {
String message = "Unable to refresh feed " + feed.getUrl() + " : " + e.getMessage(); log.debug("unable to refresh feed {}", feed.getUrl(), e);
log.debug(e.getClass().getName() + " " + message, e);
feed.setErrorCount(feed.getErrorCount() + 1); feed.setErrorCount(feed.getErrorCount() + 1);
feed.setMessage(message); feed.setMessage("Unable to refresh feed : " + e.getMessage());
feed.setDisabledUntil(refreshIntervalCalculator.onFetchError(feed)); feed.setDisabledUntil(refreshIntervalCalculator.onFetchError(feed));
return new FeedRefreshWorkerResult(feed, Collections.emptyList()); return new FeedRefreshWorkerResult(feed, Collections.emptyList());

View File

@@ -1,11 +1,8 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import java.io.StringReader;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
@@ -22,16 +19,10 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Document.OutputSettings;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities.EscapeMode;
import org.jsoup.safety.Cleaner;
import org.jsoup.safety.Safelist;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.netpreserve.urlcanon.Canonicalizer; import org.netpreserve.urlcanon.Canonicalizer;
import org.netpreserve.urlcanon.ParsedUrl; import org.netpreserve.urlcanon.ParsedUrl;
import org.w3c.css.sac.InputSource;
import org.w3c.dom.css.CSSStyleDeclaration;
import com.commafeed.backend.feed.FeedEntryKeyword.Mode; import com.commafeed.backend.feed.FeedEntryKeyword.Mode;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
@@ -41,7 +32,6 @@ import com.google.gwt.i18n.client.HasDirection.Direction;
import com.google.gwt.i18n.shared.BidiUtils; import com.google.gwt.i18n.shared.BidiUtils;
import com.ibm.icu.text.CharsetDetector; import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch; import com.ibm.icu.text.CharsetMatch;
import com.steadystate.css.parser.CSSOMParser;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -54,12 +44,6 @@ public class FeedUtils {
private static final String ESCAPED_QUESTION_MARK = Pattern.quote("?"); private static final String ESCAPED_QUESTION_MARK = Pattern.quote("?");
private static final List<String> ALLOWED_IFRAME_CSS_RULES = Arrays.asList("height", "width", "border");
private static final List<String> ALLOWED_IMG_CSS_RULES = Arrays.asList("display", "width", "height");
private static final char[] FORBIDDEN_CSS_RULE_CHARACTERS = new char[] { '(', ')' };
private static final Safelist WHITELIST = buildWhiteList();
public static String truncate(String string, int length) { public static String truncate(String string, int length) {
if (string != null) { if (string != null) {
string = string.substring(0, Math.min(length, string.length())); string = string.substring(0, Math.min(length, string.length()));
@@ -67,40 +51,6 @@ public class FeedUtils {
return string; return string;
} }
private static synchronized Safelist buildWhiteList() {
Safelist whitelist = new Safelist();
whitelist.addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt", "em", "h1",
"h2", "h3", "h4", "h5", "h6", "i", "iframe", "img", "li", "ol", "p", "pre", "q", "small", "strike", "strong", "sub", "sup",
"table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
whitelist.addAttributes("div", "dir");
whitelist.addAttributes("pre", "dir");
whitelist.addAttributes("code", "dir");
whitelist.addAttributes("table", "dir");
whitelist.addAttributes("p", "dir");
whitelist.addAttributes("a", "href", "title");
whitelist.addAttributes("blockquote", "cite");
whitelist.addAttributes("col", "span", "width");
whitelist.addAttributes("colgroup", "span", "width");
whitelist.addAttributes("iframe", "src", "height", "width", "allowfullscreen", "frameborder", "style");
whitelist.addAttributes("img", "align", "alt", "height", "src", "title", "width", "style");
whitelist.addAttributes("ol", "start", "type");
whitelist.addAttributes("q", "cite");
whitelist.addAttributes("table", "border", "bordercolor", "summary", "width");
whitelist.addAttributes("td", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "width");
whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width");
whitelist.addAttributes("ul", "type");
whitelist.addProtocols("a", "href", "ftp", "http", "https", "magnet", "mailto");
whitelist.addProtocols("blockquote", "cite", "http", "https");
whitelist.addProtocols("img", "src", "http", "https");
whitelist.addProtocols("q", "cite", "http", "https");
whitelist.addEnforcedAttribute("a", "target", "_blank");
whitelist.addEnforcedAttribute("a", "rel", "noreferrer");
return whitelist;
}
/** /**
* Detect feed encoding by using the declared encoding in the xml processing instruction and by detecting the characters used in the * Detect feed encoding by using the declared encoding in the xml processing instruction and by detecting the characters used in the
* feed * feed
@@ -233,87 +183,6 @@ public class FeedUtils {
return encoding; return encoding;
} }
public static String handleContent(String content, String baseUri, boolean keepTextOnly) {
if (StringUtils.isNotBlank(content)) {
baseUri = StringUtils.trimToEmpty(baseUri);
Document dirty = Jsoup.parseBodyFragment(content, baseUri);
Cleaner cleaner = new Cleaner(WHITELIST);
Document clean = cleaner.clean(dirty);
for (Element e : clean.select("iframe[style]")) {
String style = e.attr("style");
String escaped = escapeIFrameCss(style);
e.attr("style", escaped);
}
for (Element e : clean.select("img[style]")) {
String style = e.attr("style");
String escaped = escapeImgCss(style);
e.attr("style", escaped);
}
clean.outputSettings(new OutputSettings().escapeMode(EscapeMode.base).prettyPrint(false));
Element body = clean.body();
if (keepTextOnly) {
content = body.text();
} else {
content = body.html();
}
}
return content;
}
public static String escapeIFrameCss(String orig) {
String rule = "";
CSSOMParser parser = new CSSOMParser();
try {
List<String> rules = new ArrayList<>();
CSSStyleDeclaration decl = parser.parseStyleDeclaration(new InputSource(new StringReader(orig)));
for (int i = 0; i < decl.getLength(); i++) {
String property = decl.item(i);
String value = decl.getPropertyValue(property);
if (StringUtils.isBlank(property) || StringUtils.isBlank(value)) {
continue;
}
if (ALLOWED_IFRAME_CSS_RULES.contains(property) && StringUtils.containsNone(value, FORBIDDEN_CSS_RULE_CHARACTERS)) {
rules.add(property + ":" + decl.getPropertyValue(property) + ";");
}
}
rule = StringUtils.join(rules, "");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return rule;
}
public static String escapeImgCss(String orig) {
String rule = "";
CSSOMParser parser = new CSSOMParser();
try {
List<String> rules = new ArrayList<>();
CSSStyleDeclaration decl = parser.parseStyleDeclaration(new InputSource(new StringReader(orig)));
for (int i = 0; i < decl.getLength(); i++) {
String property = decl.item(i);
String value = decl.getPropertyValue(property);
if (StringUtils.isBlank(property) || StringUtils.isBlank(value)) {
continue;
}
if (ALLOWED_IMG_CSS_RULES.contains(property) && StringUtils.containsNone(value, FORBIDDEN_CSS_RULE_CHARACTERS)) {
rules.add(property + ":" + decl.getPropertyValue(property) + ";");
}
}
rule = StringUtils.join(rules, "");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return rule;
}
public static boolean isRTL(FeedEntry entry) { public static boolean isRTL(FeedEntry entry) {
String text = entry.getContent().getContent(); String text = entry.getContent().getContent();

View File

@@ -66,6 +66,8 @@ public class UserSettings extends AbstractModel {
private int scrollSpeed; private int scrollSpeed;
private boolean alwaysScrollToEntry; private boolean alwaysScrollToEntry;
private boolean markAllAsReadConfirmation;
private boolean customContextMenu;
private boolean email; private boolean email;
private boolean gmail; private boolean gmail;

View File

@@ -6,6 +6,7 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.FeedDAO; import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.dao.FeedEntryContentDAO; import com.commafeed.backend.dao.FeedEntryContentDAO;
import com.commafeed.backend.dao.FeedEntryDAO; import com.commafeed.backend.dao.FeedEntryDAO;
@@ -14,7 +15,6 @@ import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.UnitOfWork; import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
@@ -22,11 +22,10 @@ import lombok.extern.slf4j.Slf4j;
* *
*/ */
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Singleton @Singleton
public class DatabaseCleaningService { public class DatabaseCleaningService {
private static final int BATCH_SIZE = 100; private final int batchSize;
private final UnitOfWork unitOfWork; private final UnitOfWork unitOfWork;
private final FeedDAO feedDAO; private final FeedDAO feedDAO;
@@ -34,17 +33,28 @@ public class DatabaseCleaningService {
private final FeedEntryContentDAO feedEntryContentDAO; private final FeedEntryContentDAO feedEntryContentDAO;
private final FeedEntryStatusDAO feedEntryStatusDAO; private final FeedEntryStatusDAO feedEntryStatusDAO;
public long cleanFeedsWithoutSubscriptions() { @Inject
public DatabaseCleaningService(CommaFeedConfiguration config, UnitOfWork unitOfWork, FeedDAO feedDAO, FeedEntryDAO feedEntryDAO,
FeedEntryContentDAO feedEntryContentDAO, FeedEntryStatusDAO feedEntryStatusDAO) {
this.unitOfWork = unitOfWork;
this.feedDAO = feedDAO;
this.feedEntryDAO = feedEntryDAO;
this.feedEntryContentDAO = feedEntryContentDAO;
this.feedEntryStatusDAO = feedEntryStatusDAO;
this.batchSize = config.getApplicationSettings().getDatabaseCleanupBatchSize();
}
public void cleanFeedsWithoutSubscriptions() {
log.info("cleaning feeds without subscriptions"); log.info("cleaning feeds without subscriptions");
long total = 0; long total = 0;
int deleted = 0; int deleted;
long entriesTotal = 0; long entriesTotal = 0;
do { do {
List<Feed> feeds = unitOfWork.call(() -> feedDAO.findWithoutSubscriptions(1)); List<Feed> feeds = unitOfWork.call(() -> feedDAO.findWithoutSubscriptions(1));
for (Feed feed : feeds) { for (Feed feed : feeds) {
int entriesDeleted = 0; long entriesDeleted;
do { do {
entriesDeleted = unitOfWork.call(() -> feedEntryDAO.delete(feed.getId(), BATCH_SIZE)); entriesDeleted = unitOfWork.call(() -> feedEntryDAO.delete(feed.getId(), batchSize));
entriesTotal += entriesDeleted; entriesTotal += entriesDeleted;
log.info("removed {} entries for feeds without subscriptions", entriesTotal); log.info("removed {} entries for feeds without subscriptions", entriesTotal);
} while (entriesDeleted > 0); } while (entriesDeleted > 0);
@@ -54,26 +64,24 @@ public class DatabaseCleaningService {
log.info("removed {} feeds without subscriptions", total); log.info("removed {} feeds without subscriptions", total);
} while (deleted != 0); } while (deleted != 0);
log.info("cleanup done: {} feeds without subscriptions deleted", total); log.info("cleanup done: {} feeds without subscriptions deleted", total);
return total;
} }
public long cleanContentsWithoutEntries() { public void cleanContentsWithoutEntries() {
log.info("cleaning contents without entries"); log.info("cleaning contents without entries");
long total = 0; long total = 0;
int deleted = 0; long deleted;
do { do {
deleted = unitOfWork.call(() -> feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE)); deleted = unitOfWork.call(() -> feedEntryContentDAO.deleteWithoutEntries(batchSize));
total += deleted; total += deleted;
log.info("removed {} contents without entries", total); log.info("removed {} contents without entries", total);
} while (deleted != 0); } while (deleted != 0);
log.info("cleanup done: {} contents without entries deleted", total); log.info("cleanup done: {} contents without entries deleted", total);
return total;
} }
public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) { public void cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) {
long total = 0; long total = 0;
while (true) { while (true) {
List<FeedCapacity> feeds = unitOfWork.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE)); List<FeedCapacity> feeds = unitOfWork.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, batchSize));
if (feeds.isEmpty()) { if (feeds.isEmpty()) {
break; break;
} }
@@ -82,7 +90,7 @@ public class DatabaseCleaningService {
long remaining = feed.getCapacity() - maxFeedCapacity; long remaining = feed.getCapacity() - maxFeedCapacity;
do { do {
final long rem = remaining; final long rem = remaining;
int deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem))); int deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(batchSize, rem)));
total += deleted; total += deleted;
remaining -= deleted; remaining -= deleted;
log.info("removed {} entries for feeds exceeding capacity", total); log.info("removed {} entries for feeds exceeding capacity", total);
@@ -90,19 +98,17 @@ public class DatabaseCleaningService {
} }
} }
log.info("cleanup done: {} entries for feeds exceeding capacity deleted", total); log.info("cleanup done: {} entries for feeds exceeding capacity deleted", total);
return total;
} }
public long cleanStatusesOlderThan(final Date olderThan) { public void cleanStatusesOlderThan(final Date olderThan) {
log.info("cleaning old read statuses"); log.info("cleaning old read statuses");
long total = 0; long total = 0;
int deleted = 0; long deleted;
do { do {
deleted = unitOfWork.call(() -> feedEntryStatusDAO.delete(feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE))); deleted = unitOfWork.call(() -> feedEntryStatusDAO.deleteOldStatuses(olderThan, batchSize));
total += deleted; total += deleted;
log.info("removed {} old read statuses", total); log.info("removed {} old read statuses", total);
} while (deleted != 0); } while (deleted != 0);
log.info("cleanup done: {} old read statuses deleted", total); log.info("cleanup done: {} old read statuses deleted", total);
return total;
} }
} }

View File

@@ -1,5 +1,8 @@
package com.commafeed.backend.service; package com.commafeed.backend.service;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -8,27 +11,47 @@ import javax.inject.Singleton;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Document.OutputSettings;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities.EscapeMode;
import org.jsoup.safety.Cleaner;
import org.jsoup.safety.Safelist;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.CSSParseException;
import org.w3c.css.sac.ErrorHandler;
import org.w3c.css.sac.InputSource;
import org.w3c.dom.css.CSSStyleDeclaration;
import com.commafeed.backend.dao.FeedEntryContentDAO; import com.commafeed.backend.dao.FeedEntryContentDAO;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
import com.steadystate.css.parser.CSSOMParser;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Slf4j
@Singleton @Singleton
public class FeedEntryContentService { public class FeedEntryContentService {
private static final Safelist HTML_WHITELIST = buildWhiteList();
private static final List<String> ALLOWED_IFRAME_CSS_RULES = Arrays.asList("height", "width", "border");
private static final List<String> ALLOWED_IMG_CSS_RULES = Arrays.asList("display", "width", "height");
private static final char[] FORBIDDEN_CSS_RULE_CHARACTERS = new char[] { '(', ')' };
private final FeedEntryContentDAO feedEntryContentDAO; private final FeedEntryContentDAO feedEntryContentDAO;
/** /**
* this is NOT thread-safe * this is NOT thread-safe
*/ */
public FeedEntryContent findOrCreate(FeedEntryContent content, String baseUrl) { public FeedEntryContent findOrCreate(FeedEntryContent content, String baseUrl) {
content.setAuthor(FeedUtils.truncate(FeedUtils.handleContent(content.getAuthor(), baseUrl, true), 128)); content.setAuthor(FeedUtils.truncate(handleContent(content.getAuthor(), baseUrl, true), 128));
content.setTitle(FeedUtils.truncate(FeedUtils.handleContent(content.getTitle(), baseUrl, true), 2048)); content.setTitle(FeedUtils.truncate(handleContent(content.getTitle(), baseUrl, true), 2048));
content.setContent(FeedUtils.handleContent(content.getContent(), baseUrl, false)); content.setContent(handleContent(content.getContent(), baseUrl, false));
content.setMediaDescription(FeedUtils.handleContent(content.getMediaDescription(), baseUrl, false)); content.setMediaDescription(handleContent(content.getMediaDescription(), baseUrl, false));
String contentHash = DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.getContent())); String contentHash = DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.getContent()));
content.setContentHash(contentHash); content.setContentHash(contentHash);
@@ -37,7 +60,7 @@ public class FeedEntryContentService {
content.setTitleHash(titleHash); content.setTitleHash(titleHash);
List<FeedEntryContent> existing = feedEntryContentDAO.findExisting(contentHash, titleHash); List<FeedEntryContent> existing = feedEntryContentDAO.findExisting(contentHash, titleHash);
Optional<FeedEntryContent> equivalentContent = existing.stream().filter(c -> content.equivalentTo(c)).findFirst(); Optional<FeedEntryContent> equivalentContent = existing.stream().filter(content::equivalentTo).findFirst();
if (equivalentContent.isPresent()) { if (equivalentContent.isPresent()) {
return equivalentContent.get(); return equivalentContent.get();
} }
@@ -45,4 +68,140 @@ public class FeedEntryContentService {
feedEntryContentDAO.saveOrUpdate(content); feedEntryContentDAO.saveOrUpdate(content);
return content; return content;
} }
private static Safelist buildWhiteList() {
Safelist whitelist = new Safelist();
whitelist.addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt", "em", "h1",
"h2", "h3", "h4", "h5", "h6", "i", "iframe", "img", "li", "ol", "p", "pre", "q", "small", "strike", "strong", "sub", "sup",
"table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
whitelist.addAttributes("div", "dir");
whitelist.addAttributes("pre", "dir");
whitelist.addAttributes("code", "dir");
whitelist.addAttributes("table", "dir");
whitelist.addAttributes("p", "dir");
whitelist.addAttributes("a", "href", "title");
whitelist.addAttributes("blockquote", "cite");
whitelist.addAttributes("col", "span", "width");
whitelist.addAttributes("colgroup", "span", "width");
whitelist.addAttributes("iframe", "src", "height", "width", "allowfullscreen", "frameborder", "style");
whitelist.addAttributes("img", "align", "alt", "height", "src", "title", "width", "style");
whitelist.addAttributes("ol", "start", "type");
whitelist.addAttributes("q", "cite");
whitelist.addAttributes("table", "border", "bordercolor", "summary", "width");
whitelist.addAttributes("td", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "width");
whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width");
whitelist.addAttributes("ul", "type");
whitelist.addProtocols("a", "href", "ftp", "http", "https", "magnet", "mailto");
whitelist.addProtocols("blockquote", "cite", "http", "https");
whitelist.addProtocols("img", "src", "http", "https");
whitelist.addProtocols("q", "cite", "http", "https");
whitelist.addEnforcedAttribute("a", "target", "_blank");
whitelist.addEnforcedAttribute("a", "rel", "noreferrer");
return whitelist;
}
private String handleContent(String content, String baseUri, boolean keepTextOnly) {
if (StringUtils.isNotBlank(content)) {
baseUri = StringUtils.trimToEmpty(baseUri);
Document dirty = Jsoup.parseBodyFragment(content, baseUri);
Cleaner cleaner = new Cleaner(HTML_WHITELIST);
Document clean = cleaner.clean(dirty);
for (Element e : clean.select("iframe[style]")) {
String style = e.attr("style");
String escaped = escapeIFrameCss(style);
e.attr("style", escaped);
}
for (Element e : clean.select("img[style]")) {
String style = e.attr("style");
String escaped = escapeImgCss(style);
e.attr("style", escaped);
}
clean.outputSettings(new OutputSettings().escapeMode(EscapeMode.base).prettyPrint(false));
Element body = clean.body();
if (keepTextOnly) {
content = body.text();
} else {
content = body.html();
}
}
return content;
}
private String escapeIFrameCss(String orig) {
String rule = "";
try {
List<String> rules = new ArrayList<>();
CSSStyleDeclaration decl = buildCssParser().parseStyleDeclaration(new InputSource(new StringReader(orig)));
for (int i = 0; i < decl.getLength(); i++) {
String property = decl.item(i);
String value = decl.getPropertyValue(property);
if (StringUtils.isBlank(property) || StringUtils.isBlank(value)) {
continue;
}
if (ALLOWED_IFRAME_CSS_RULES.contains(property) && StringUtils.containsNone(value, FORBIDDEN_CSS_RULE_CHARACTERS)) {
rules.add(property + ":" + decl.getPropertyValue(property) + ";");
}
}
rule = StringUtils.join(rules, "");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return rule;
}
private String escapeImgCss(String orig) {
String rule = "";
try {
List<String> rules = new ArrayList<>();
CSSStyleDeclaration decl = buildCssParser().parseStyleDeclaration(new InputSource(new StringReader(orig)));
for (int i = 0; i < decl.getLength(); i++) {
String property = decl.item(i);
String value = decl.getPropertyValue(property);
if (StringUtils.isBlank(property) || StringUtils.isBlank(value)) {
continue;
}
if (ALLOWED_IMG_CSS_RULES.contains(property) && StringUtils.containsNone(value, FORBIDDEN_CSS_RULE_CHARACTERS)) {
rules.add(property + ":" + decl.getPropertyValue(property) + ";");
}
}
rule = StringUtils.join(rules, "");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return rule;
}
private CSSOMParser buildCssParser() {
CSSOMParser parser = new CSSOMParser();
parser.setErrorHandler(new ErrorHandler() {
@Override
public void warning(CSSParseException exception) throws CSSException {
log.debug("warning while parsing css: {}", exception.getMessage(), exception);
}
@Override
public void error(CSSParseException exception) throws CSSException {
log.debug("error while parsing css: {}", exception.getMessage(), exception);
}
@Override
public void fatalError(CSSParseException exception) throws CSSException {
log.debug("fatal error while parsing css: {}", exception.getMessage(), exception);
}
});
return parser;
}
} }

View File

@@ -38,6 +38,12 @@ public class Settings implements Serializable {
@ApiModelProperty(value = "always scroll selected entry to the top of the page, even if it fits entirely on screen", required = true) @ApiModelProperty(value = "always scroll selected entry to the top of the page, even if it fits entirely on screen", required = true)
private boolean alwaysScrollToEntry; private boolean alwaysScrollToEntry;
@ApiModelProperty(value = "ask for confirmation when marking all entries as read", required = true)
private boolean markAllAsReadConfirmation;
@ApiModelProperty(value = "show commafeed's own context menu on right click", required = true)
private boolean customContextMenu;
@ApiModelProperty(value = "sharing settings", required = true) @ApiModelProperty(value = "sharing settings", required = true)
private SharingSettings sharingSettings = new SharingSettings(); private SharingSettings sharingSettings = new SharingSettings();

View File

@@ -23,19 +23,16 @@ public class Subscription implements Serializable {
@ApiModelProperty(value = "subscription name", required = true) @ApiModelProperty(value = "subscription name", required = true)
private String name; private String name;
@ApiModelProperty(value = "error message while fetching the feed", required = true) @ApiModelProperty(value = "error message while fetching the feed")
private String message; private String message;
@ApiModelProperty(value = "error count", required = true) @ApiModelProperty(value = "error count", required = true)
private int errorCount; private int errorCount;
@ApiModelProperty(value = "last time the feed was refreshed", dataType = "number", required = true) @ApiModelProperty(value = "last time the feed was refreshed", dataType = "number")
private Date lastRefresh; private Date lastRefresh;
@ApiModelProperty( @ApiModelProperty(value = "next time the feed refresh is planned, null if refresh is already queued", dataType = "number")
value = "next time the feed refresh is planned, null if refresh is already queued",
dataType = "number",
required = true)
private Date nextRefresh; private Date nextRefresh;
@ApiModelProperty(value = "this subscription's feed url", required = true) @ApiModelProperty(value = "this subscription's feed url", required = true)

View File

@@ -30,7 +30,7 @@ public class UserModel implements Serializable {
@ApiModelProperty(value = "account status", required = true) @ApiModelProperty(value = "account status", required = true)
private boolean enabled; private boolean enabled;
@ApiModelProperty(value = "account creation date", dataType = "number", required = true) @ApiModelProperty(value = "account creation date", dataType = "number")
private Date created; private Date created;
@ApiModelProperty(value = "last login date", dataType = "number") @ApiModelProperty(value = "last login date", dataType = "number")

View File

@@ -104,6 +104,8 @@ public class UserREST {
s.setLanguage(settings.getLanguage()); s.setLanguage(settings.getLanguage());
s.setScrollSpeed(settings.getScrollSpeed()); s.setScrollSpeed(settings.getScrollSpeed());
s.setAlwaysScrollToEntry(settings.isAlwaysScrollToEntry()); s.setAlwaysScrollToEntry(settings.isAlwaysScrollToEntry());
s.setMarkAllAsReadConfirmation(settings.isMarkAllAsReadConfirmation());
s.setCustomContextMenu(settings.isCustomContextMenu());
} else { } else {
s.setReadingMode(ReadingMode.unread.name()); s.setReadingMode(ReadingMode.unread.name());
s.setReadingOrder(ReadingOrder.desc.name()); s.setReadingOrder(ReadingOrder.desc.name());
@@ -122,6 +124,8 @@ public class UserREST {
s.setLanguage("en"); s.setLanguage("en");
s.setScrollSpeed(400); s.setScrollSpeed(400);
s.setAlwaysScrollToEntry(false); s.setAlwaysScrollToEntry(false);
s.setMarkAllAsReadConfirmation(true);
s.setCustomContextMenu(true);
} }
return Response.ok(s).build(); return Response.ok(s).build();
} }
@@ -148,6 +152,8 @@ public class UserREST {
s.setLanguage(settings.getLanguage()); s.setLanguage(settings.getLanguage());
s.setScrollSpeed(settings.getScrollSpeed()); s.setScrollSpeed(settings.getScrollSpeed());
s.setAlwaysScrollToEntry(settings.isAlwaysScrollToEntry()); s.setAlwaysScrollToEntry(settings.isAlwaysScrollToEntry());
s.setMarkAllAsReadConfirmation(settings.isMarkAllAsReadConfirmation());
s.setCustomContextMenu(settings.isCustomContextMenu());
s.setEmail(settings.getSharingSettings().isEmail()); s.setEmail(settings.getSharingSettings().isEmail());
s.setGmail(settings.getSharingSettings().isGmail()); s.setGmail(settings.getSharingSettings().isGmail());

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="mark-all-as-read-confirmation" author="athou">
<addColumn tableName="USERSETTINGS">
<column name="markAllAsReadConfirmation" type="BOOLEAN" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="custom-context-menu" author="athou">
<addColumn tableName="USERSETTINGS">
<column name="customContextMenu" type="BOOLEAN" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -21,5 +21,6 @@
<include file="changelogs/db.changelog-3.5.xml" /> <include file="changelogs/db.changelog-3.5.xml" />
<include file="changelogs/db.changelog-3.6.xml" /> <include file="changelogs/db.changelog-3.6.xml" />
<include file="changelogs/db.changelog-3.8.xml" /> <include file="changelogs/db.changelog-3.8.xml" />
<include file="changelogs/db.changelog-3.9.xml" />
</databaseChangeLog> </databaseChangeLog>

View File

@@ -28,6 +28,9 @@ app:
# number of database updating threads # number of database updating threads
databaseUpdateThreads: 1 databaseUpdateThreads: 1
# rows to delete per query while cleaning up old entries
databaseCleanupBatchSize: 100
# settings for sending emails (password recovery) # settings for sending emails (password recovery)
smtpHost: localhost smtpHost: localhost
smtpPort: 25 smtpPort: 25
@@ -80,9 +83,13 @@ app:
userAgent: userAgent:
# Database connection # Database connection
# ------------------- # -------------------
# for MariaDB
# driverClass is org.mariadb.jdbc.Driver
# url is jdbc:mariadb://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
#
# for MySQL # for MySQL
# driverClass is com.mysql.jdbc.Driver # driverClass is com.mysql.cj.jdbc.Driver
# url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true # url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
# #
# for PostgreSQL # for PostgreSQL

View File

@@ -5,7 +5,7 @@
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>3.8.0</version> <version>3.9.0</version>
<name>CommaFeed</name> <name>CommaFeed</name>
<packaging>pom</packaging> <packaging>pom</packaging>