diff --git a/client/model.go b/client/model.go index 0a6cd4d1..e92c9b10 100644 --- a/client/model.go +++ b/client/model.go @@ -42,6 +42,7 @@ type User struct { DefaultHomePage string `json:"default_home_page"` CategoriesSortingOrder string `json:"categories_sorting_order"` MarkReadOnView bool `json:"mark_read_on_view"` + CacheForOffline bool `json:"cache_for_offline"` MediaPlaybackRate float64 `json:"media_playback_rate"` BlockFilterEntryRules string `json:"block_filter_entry_rules"` KeepFilterEntryRules string `json:"keep_filter_entry_rules"` diff --git a/internal/database/migrations.go b/internal/database/migrations.go index 2580c1c4..65103fbd 100644 --- a/internal/database/migrations.go +++ b/internal/database/migrations.go @@ -970,6 +970,7 @@ var migrations = []func(tx *sql.Tx, driver string) error{ return err }, func(tx *sql.Tx, _ string) (err error) { +<<<<<<< HEAD sql := ` ALTER TABLE integrations ADD COLUMN discord_enabled bool default 'f'; ALTER TABLE integrations ADD COLUMN discord_webhook_link text default ''; @@ -1015,4 +1016,9 @@ var migrations = []func(tx *sql.Tx, driver string) error{ _, err = tx.Exec(sql) return err }, + func(tx *sql.Tx, _ string) (err error) { + sql := `ALTER TABLE users ADD COLUMN cache_for_offline boolean default 'f'` + _, err = tx.Exec(sql) + return err + }, } diff --git a/internal/locale/translations/en_US.json b/internal/locale/translations/en_US.json index 26fcf685..3626669b 100644 --- a/internal/locale/translations/en_US.json +++ b/internal/locale/translations/en_US.json @@ -1,606 +1,1095 @@ { - "action.cancel": "cancel", - "action.download": "Download", - "action.edit": "Edit", - "action.home_screen": "Add to home screen", - "action.import": "Import", - "action.login": "Login", - "action.or": "or", - "action.remove": "Remove", - "action.remove_feed": "Remove this feed", - "action.save": "Save", - "action.subscribe": "Subscribe", - "action.update": "Update", - "alert.account_linked": "Your external account is now linked!", - "alert.account_unlinked": "Your external account is now dissociated!", - "alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.", - "alert.feed_error": "There is a problem with this feed", - "alert.no_bookmark": "There are no starred entries.", - "alert.no_category": "There is no category.", - "alert.no_category_entry": "There are no entries in this category.", - "alert.no_feed": "You don’t have any feeds.", - "alert.no_feed_entry": "There are no entries for this feed.", - "alert.no_feed_in_category": "There is no feed for this category.", - "alert.no_history": "There is no history at the moment.", - "alert.no_search_result": "There are no results for this search.", - "alert.no_shared_entry": "There is no shared entry.", - "alert.no_tag_entry": "There are no entries matching this tag.", - "alert.no_unread_entry": "There are no unread entries.", - "alert.no_user": "You are the only user.", - "alert.pocket_linked": "Your Pocket account is now linked!", - "alert.prefs_saved": "Preferences saved!", - "alert.too_many_feeds_refresh": [ - "You have triggered too many feed refreshes. Please wait %d minute before trying again.", - "You have triggered too many feed refreshes. Please wait %d minutes before trying again." - ], - "confirm.loading": "In progress…", - "confirm.no": "no", - "confirm.question": "Are you sure?", - "confirm.question.refresh": "Are you sure you want to force refresh?", - "confirm.yes": "yes", - "enclosure_media_controls.seek": "Seek:", - "enclosure_media_controls.seek.title": "Seek %s seconds", - "enclosure_media_controls.speed": "Speed:", - "enclosure_media_controls.speed.faster": "Faster", - "enclosure_media_controls.speed.faster.title": "Faster by %sx", - "enclosure_media_controls.speed.reset": "Reset", - "enclosure_media_controls.speed.reset.title": "Reset speed to 1x", - "enclosure_media_controls.speed.slower": "Slower", - "enclosure_media_controls.speed.slower.title": "Slower by %sx", - "entry.bookmark.toast.off": "Unstarred", - "entry.bookmark.toast.on": "Starred", - "entry.bookmark.toggle.off": "Unstar", - "entry.bookmark.toggle.on": "Star", - "entry.comments.label": "Comments", - "entry.comments.title": "View Comments", - "entry.estimated_reading_time": [ - "%d minute read", - "%d minutes read" - ], - "entry.external_link.label": "External link", - "entry.save.completed": "Done!", - "entry.save.label": "Save", - "entry.save.title": "Save this entry", - "entry.save.toast.completed": "Entry saved", - "entry.scraper.completed": "Done!", - "entry.scraper.label": "Download", - "entry.scraper.title": "Fetch original content", - "entry.share.label": "Share", - "entry.share.title": "Share this entry", - "entry.shared_entry.label": "Share", - "entry.shared_entry.title": "Open the public link", - "entry.state.loading": "Loading…", - "entry.state.saving": "Saving…", - "entry.status.read": "Read", - "entry.status.title": "Change entry status", - "entry.status.toast.read": "Marked as read", - "entry.status.toast.unread": "Marked as unread", - "entry.status.unread": "Unread", - "entry.tags.label": "Tags:", - "entry.unshare.label": "Unshare", - "error.api_key_already_exists": "This API Key already exists.", - "error.bad_credentials": "Invalid username or password.", - "error.category_already_exists": "This category already exists.", - "error.category_not_found": "This category does not exist or does not belong to this user.", - "error.database_error": "Database error: %v.", - "error.different_passwords": "Passwords are not the same.", - "error.duplicate_fever_username": "There is already someone else with the same Fever username!", - "error.duplicate_googlereader_username": "There is already someone else with the same Google Reader username!", - "error.duplicate_linked_account": "There is already someone associated with this provider!", - "error.duplicated_feed": "This feed already exists.", - "error.empty_file": "This file is empty.", - "error.entries_per_page_invalid": "The number of entries per page is not valid.", - "error.feed_already_exists": "This feed already exists.", - "error.feed_category_not_found": "This category does not exist or does not belong to this user.", - "error.feed_format_not_detected": "Unable to detect feed format: %v.", - "error.feed_invalid_blocklist_rule": "The block list rule is invalid.", - "error.feed_invalid_keeplist_rule": "The keep list rule is invalid.", - "error.feed_mandatory_fields": "The URL and the category are mandatory.", - "error.feed_not_found": "This feed does not exist or does not belong to this user.", - "error.feed_title_not_empty": "The feed title cannot be empty.", - "error.feed_url_not_empty": "The feed URL cannot be empty.", - "error.fields_mandatory": "All fields are mandatory.", - "error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.", - "error.http_body_read": "Unable to read the HTTP body: %v.", - "error.http_client_error": "HTTP client error: %v.", - "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?", - "error.http_empty_response_body": "The HTTP response body is empty.", - "error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?", - "error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.", - "error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.", - "error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.", - "error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.", - "error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).", - "error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.", - "error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.", - "error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.", - "error.invalid_default_home_page": "Invalid default homepage!", - "error.invalid_display_mode": "Invalid web app display mode.", - "error.invalid_entry_direction": "Invalid entry direction.", - "error.invalid_feed_url": "Invalid feed URL.", - "error.invalid_gesture_nav": "Invalid gesture navigation.", - "error.invalid_language": "Invalid language.", - "error.invalid_site_url": "Invalid site URL.", - "error.invalid_theme": "Invalid theme.", - "error.invalid_timezone": "Invalid timezone.", - "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.", - "error.network_timeout": "This website is too slow and the request timed out: %v", - "error.password_min_length": "The password must have at least 6 characters.", - "error.pocket_access_token": "Unable to fetch access token from Pocket!", - "error.pocket_request_token": "Unable to fetch request token from Pocket!", - "error.settings_block_rule_fieldname_invalid": "Invalid Block rule: rule #%d is missing a valid field name (Options: %s)", - "error.settings_block_rule_invalid_regex": "Invalid Block rule: rule #%d's pattern is not a valid regex", - "error.settings_block_rule_regex_required": "Invalid Block rule: rule #%d's pattern is not provided", - "error.settings_block_rule_separator_required": "Invalid Block rule: rule #%d's pattern is required to be seperated by a '='", - "error.settings_invalid_domain_list": "Invalid domain list. Please provide a space separated list of domains.", - "error.settings_keep_rule_fieldname_invalid": "Invalid Keep rule: rule #%d is missing a valid field name (Options: %s)", - "error.settings_keep_rule_invalid_regex": "Invalid Keep rule: rule #%d's pattern is not a valid regex", - "error.settings_keep_rule_regex_required": "Invalid Keep rule: rule #%d pattern is not provided", - "error.settings_keep_rule_separator_required": "Invalid Keep rule: rule #%d's pattern is required to be seperated by a '='", - "error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.", - "error.settings_media_playback_rate_range": "Playback speed is out of range", - "error.settings_reading_speed_is_positive": "The reading speeds must be positive integers.", - "error.site_url_not_empty": "The site URL cannot be empty.", - "error.subscription_not_found": "Unable to find any feed.", - "error.title_required": "The title is mandatory.", - "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.", - "error.unable_to_create_api_key": "Unable to create this API Key.", - "error.unable_to_create_category": "Unable to create this category.", - "error.unable_to_create_user": "Unable to create this user.", - "error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.", - "error.unable_to_parse_feed": "Unable to parse this feed: %v.", - "error.unable_to_update_category": "Unable to update this category.", - "error.unable_to_update_feed": "Unable to update this feed.", - "error.unable_to_update_user": "Unable to update this user.", - "error.unlink_account_without_password": "You must define a password otherwise you won’t be able to login again.", - "error.user_already_exists": "This user already exists.", - "error.user_mandatory_fields": "The username is mandatory.", - "form.api_key.label.description": "API Key Label", - "form.category.hide_globally": "Hide entries in global unread list", - "form.category.label.title": "Title", - "form.feed.fieldset.general": "General", - "form.feed.fieldset.integration": "Third-Party Services", - "form.feed.fieldset.network_settings": "Network Settings", - "form.feed.fieldset.rules": "Rules", - "form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates", - "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", - "form.feed.label.blocklist_rules": "Block Rules", - "form.feed.label.category": "Category", - "form.feed.label.cookie": "Set Cookies", - "form.feed.label.crawler": "Fetch original content", - "form.feed.label.description": "Description", - "form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting", - "form.feed.label.disabled": "Do not refresh this feed", - "form.feed.label.feed_password": "Feed Password", - "form.feed.label.feed_url": "Feed URL", - "form.feed.label.feed_username": "Feed Username", - "form.feed.label.fetch_via_proxy": "Fetch via proxy", - "form.feed.label.hide_globally": "Hide entries in global unread list", - "form.feed.label.ignore_http_cache": "Ignore HTTP cache", - "form.feed.label.keeplist_rules": "Keep Rules", - "form.feed.label.no_media_player": "No media player (audio/video)", - "form.feed.label.ntfy_activate": "Push entries to ntfy", - "form.feed.label.ntfy_default_priority": "Ntfy default priority", - "form.feed.label.ntfy_high_priority": "Ntfy high priority", - "form.feed.label.ntfy_low_priority": "Ntfy low priority", - "form.feed.label.ntfy_max_priority": "Ntfy max priority", - "form.feed.label.ntfy_min_priority": "Ntfy min priority", - "form.feed.label.ntfy_priority": "Ntfy priority", - "form.feed.label.ntfy_topic": "Ntfy topic (optional)", - "form.feed.label.pushover_activate": "Push entries to Pushover", - "form.feed.label.pushover_default_priority": "Default priority", - "form.feed.label.pushover_high_priority": "High priority", - "form.feed.label.pushover_low_priority": "Low priority", - "form.feed.label.pushover_max_priority": "Max priority", - "form.feed.label.pushover_min_priority": "Minimal priority", - "form.feed.label.pushover_priority": "Pushover message priority", - "form.feed.label.rewrite_rules": "Rewrite Rules", - "form.feed.label.scraper_rules": "Scraper Rules", - "form.feed.label.site_url": "Site URL", - "form.feed.label.title": "Title", - "form.feed.label.urlrewrite_rules": "URL Rewrite Rules", - "form.feed.label.user_agent": "Override Default User Agent", - "form.feed.label.webhook_url": "Override webhook url", - "form.import.label.file": "OPML file", - "form.import.label.url": "URL", - "form.integration.apprise_activate": "Push entries to Apprise", - "form.integration.apprise_services_url": "Comma separated list of Apprise service URLs", - "form.integration.apprise_url": "Apprise API URL", - "form.integration.betula_activate": "Save entries to Betula", - "form.integration.betula_token": "Betula Token", - "form.integration.betula_url": "Betula server URL", - "form.integration.cubox_activate": "Save entries to Cubox", - "form.integration.cubox_api_link": "Cubox API link", - "form.integration.discord_activate": "Push entries to Discord", - "form.integration.discord_webhook_link": "Discord Webhook link", - "form.integration.espial_activate": "Save entries to Espial", - "form.integration.espial_api_key": "Espial API key", - "form.integration.espial_endpoint": "Espial API Endpoint", - "form.integration.espial_tags": "Espial Tags", - "form.integration.fever_activate": "Activate Fever API", - "form.integration.fever_endpoint": "Fever API endpoint:", - "form.integration.fever_password": "Fever Password", - "form.integration.fever_username": "Fever Username", - "form.integration.googlereader_activate": "Activate Google Reader API", - "form.integration.googlereader_endpoint": "Google Reader API endpoint:", - "form.integration.googlereader_password": "Google Reader Password", - "form.integration.googlereader_username": "Google Reader Username", - "form.integration.instapaper_activate": "Save entries to Instapaper", - "form.integration.instapaper_password": "Instapaper Password", - "form.integration.instapaper_username": "Instapaper Username", - "form.integration.linkace_activate": "Save entries to LinkAce", - "form.integration.linkace_api_key": "LinkAce API key", - "form.integration.linkace_check_disabled": "Disable link check", - "form.integration.linkace_endpoint": "LinkAce API Endpoint", - "form.integration.linkace_is_private": "Mark link as private", - "form.integration.linkace_tags": "LinkAce Tags", - "form.integration.linkding_activate": "Save entries to Linkding", - "form.integration.linkding_api_key": "Linkding API key", - "form.integration.linkding_bookmark": "Mark bookmark as unread", - "form.integration.linkding_endpoint": "Linkding API Endpoint", - "form.integration.linkding_tags": "Linkding Tags", - "form.integration.linkwarden_activate": "Save entries to Linkwarden", - "form.integration.linkwarden_api_key": "Linkwarden API key", - "form.integration.linkwarden_endpoint": "Linkwarden API Endpoint", - "form.integration.matrix_bot_activate": "Push new entries to Matrix", - "form.integration.matrix_bot_chat_id": "ID of Matrix Room", - "form.integration.matrix_bot_password": "Password for Matrix user", - "form.integration.matrix_bot_url": "Matrix server URL", - "form.integration.matrix_bot_user": "Username for Matrix", - "form.integration.notion_activate": "Save entries to Notion", - "form.integration.notion_page_id": "Notion Page ID", - "form.integration.notion_token": "Notion Secret Token", - "form.integration.ntfy_activate": "Push entries to ntfy", - "form.integration.ntfy_api_token": "Ntfy API Token (optional)", - "form.integration.ntfy_icon_url": "Ntfy Icon URL (optional)", - "form.integration.ntfy_internal_links": "Use internal links on click (optional)", - "form.integration.ntfy_password": "Ntfy Password (optional)", - "form.integration.ntfy_topic": "Ntfy topic (default used if not set in feed)", - "form.integration.ntfy_url": "Ntfy URL (optional, default is ntfy.sh)", - "form.integration.ntfy_username": "Ntfy Username (optional)", - "form.integration.nunux_keeper_activate": "Save entries to Nunux Keeper", - "form.integration.nunux_keeper_api_key": "Nunux Keeper API key", - "form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint", - "form.integration.omnivore_activate": "Save entries to Omnivore", - "form.integration.omnivore_api_key": "Omnivore API key", - "form.integration.omnivore_url": "Omnivore API Endpoint", - "form.integration.pinboard_activate": "Save entries to Pinboard", - "form.integration.pinboard_bookmark": "Mark bookmark as unread", - "form.integration.pinboard_tags": "Pinboard Tags", - "form.integration.pinboard_token": "Pinboard API Token", - "form.integration.pocket_access_token": "Pocket Access Token", - "form.integration.pocket_activate": "Save entries to Pocket", - "form.integration.pocket_connect_link": "Connect your Pocket account", - "form.integration.pocket_consumer_key": "Pocket Consumer Key", - "form.integration.pushover_activate": "Push entries to Pushover", - "form.integration.pushover_device": "Pushover device (optional)", - "form.integration.pushover_prefix": "Pushover URL prefix (optional)", - "form.integration.pushover_token": "Pushover application API token", - "form.integration.pushover_user": "Pushover user key", - "form.integration.raindrop_activate": "Save entries to Raindrop", - "form.integration.raindrop_collection_id": "Collection ID", - "form.integration.raindrop_tags": "Tags (comma-separated)", - "form.integration.raindrop_token": "(Test) Token", - "form.integration.readeck_activate": "Save entries to readeck", - "form.integration.readeck_api_key": "Readeck API key", - "form.integration.readeck_endpoint": "Readeck URL", - "form.integration.readeck_labels": "Readeck Labels", - "form.integration.readeck_only_url": "Send only URL (instead of full content)", - "form.integration.readwise_activate": "Save entries to Readwise Reader", - "form.integration.readwise_api_key": "Readwise Reader Access Token", - "form.integration.readwise_api_key_link": "Get your Readwise Access Token", - "form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions", - "form.integration.rssbridge_url": "RSS-Bridge server URL", - "form.integration.shaarli_activate": "Save articles to Shaarli", - "form.integration.shaarli_api_secret": "Shaarli API Secret", - "form.integration.shaarli_endpoint": "Shaarli URL", - "form.integration.shiori_activate": "Save articles to Shiori", - "form.integration.shiori_endpoint": "Shiori API Endpoint", - "form.integration.shiori_password": "Shiori Password", - "form.integration.shiori_username": "Shiori Username", - "form.integration.slack_activate": "Push entries to Slack", - "form.integration.slack_webhook_link": "Slack Webhook link", - "form.integration.telegram_bot_activate": "Push new entries to Telegram chat", - "form.integration.telegram_bot_disable_buttons": "Disable buttons", - "form.integration.telegram_bot_disable_notification": "Disable notification", - "form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview", - "form.integration.telegram_bot_token": "Bot token", - "form.integration.telegram_chat_id": "Chat ID", - "form.integration.telegram_topic_id": "Topic ID", - "form.integration.wallabag_activate": "Save entries to Wallabag", - "form.integration.wallabag_client_id": "Wallabag Client ID", - "form.integration.wallabag_client_secret": "Wallabag Client Secret", - "form.integration.wallabag_endpoint": "Wallabag Base URL", - "form.integration.wallabag_only_url": "Send only URL (instead of full content)", - "form.integration.wallabag_password": "Wallabag Password", - "form.integration.wallabag_username": "Wallabag Username", - "form.integration.webhook_activate": "Enable Webhooks", - "form.integration.webhook_secret": "Webhooks Secret", - "form.integration.webhook_url": "Default Webhook URL", - "form.prefs.fieldset.application_settings": "Application Settings", - "form.prefs.fieldset.authentication_settings": "Authentication Settings", - "form.prefs.fieldset.global_feed_settings": "Global Feed Settings", - "form.prefs.fieldset.reader_settings": "Reader Settings", - "form.prefs.help.external_font_hosts": "Space separated list of external font hosts to allow. For example: \"fonts.gstatic.com fonts.googleapis.com\".", - "form.prefs.label.categories_sorting_order": "Categories sorting", - "form.prefs.label.cjk_reading_speed": "Reading speed for Chinese, Korean and Japanese (characters per minute)", - "form.prefs.label.custom_css": "Custom CSS", - "form.prefs.label.custom_js": "Custom JavaScript", - "form.prefs.label.default_home_page": "Default home page", - "form.prefs.label.default_reading_speed": "Reading speed for other languages (words per minute)", - "form.prefs.label.display_mode": "Progressive Web App (PWA) display mode", - "form.prefs.label.entries_per_page": "Entries per page", - "form.prefs.label.entry_order": "Entry sorting column", - "form.prefs.label.entry_sorting": "Entry sorting", - "form.prefs.label.entry_swipe": "Enable entry swipe on touch screens", - "form.prefs.label.external_font_hosts": "External font hosts", - "form.prefs.label.gesture_nav": "Gesture to navigate between entries", - "form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts", - "form.prefs.label.language": "Language", - "form.prefs.label.mark_read_manually": "Mark entries as read manually", - "form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion", - "form.prefs.label.mark_read_on_view": "Automatically mark entries as read when viewed", - "form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion", - "form.prefs.label.media_playback_rate": "Playback speed of the audio/video", - "form.prefs.label.show_reading_time": "Show estimated reading time for entries", - "form.prefs.label.theme": "Theme", - "form.prefs.label.timezone": "Timezone", - "form.prefs.select.alphabetical": "Alphabetical", - "form.prefs.select.browser": "Browser", - "form.prefs.select.created_time": "Entry created time", - "form.prefs.select.fullscreen": "Fullscreen", - "form.prefs.select.minimal_ui": "Minimal", - "form.prefs.select.none": "None", - "form.prefs.select.older_first": "Older entries first", - "form.prefs.select.publish_time": "Entry published time", - "form.prefs.select.recent_first": "Recent entries first", - "form.prefs.select.standalone": "Standalone", - "form.prefs.select.swipe": "Swipe", - "form.prefs.select.tap": "Double tap", - "form.prefs.select.unread_count": "Unread count", - "form.submit.loading": "Loading…", - "form.submit.saving": "Saving…", - "form.user.label.admin": "Administrator", - "form.user.label.confirmation": "Password Confirmation", - "form.user.label.password": "Password", - "form.user.label.username": "Username", - "menu.about": "About", - "menu.add_feed": "Add feed", - "menu.add_user": "Add user", - "menu.api_keys": "API Keys", - "menu.categories": "Categories", - "menu.create_api_key": "Create a new API key", - "menu.create_category": "Create a category", - "menu.edit_category": "Edit", - "menu.edit_feed": "Edit", - "menu.export": "Export", - "menu.feed_entries": "Entries", - "menu.feeds": "Feeds", - "menu.flush_history": "Flush history", - "menu.history": "History", - "menu.home_page": "Home page", - "menu.import": "Import", - "menu.integrations": "Integrations", - "menu.logout": "Logout", - "menu.mark_all_as_read": "Mark all as read", - "menu.mark_page_as_read": "Mark this page as read", - "menu.preferences": "Preferences", - "menu.refresh_all_feeds": "Refresh all feeds in the background", - "menu.refresh_feed": "Refresh", - "menu.search": "Search", - "menu.sessions": "Sessions", - "menu.settings": "Settings", - "menu.shared_entries": "Shared entries", - "menu.show_all_entries": "Show all entries", - "menu.show_only_starred_entries": "Show only starred entries", - "menu.show_only_unread_entries": "Show only unread entries", - "menu.starred": "Starred", - "menu.title": "Menu", - "menu.unread": "Unread", - "menu.users": "Users", - "page.about.author": "Author:", - "page.about.build_date": "Build Date:", - "page.about.credits": "Credits", - "page.about.db_usage": "Database size:", - "page.about.global_config_options": "Global configuration options", - "page.about.go_version": "Go version:", - "page.about.license": "License:", - "page.about.postgres_version": "Postgres version:", - "page.about.title": "About", - "page.about.version": "Version:", - "page.add_feed.choose_feed": "Choose a feed", - "page.add_feed.label.url": "URL", - "page.add_feed.legend.advanced_options": "Advanced Options", - "page.add_feed.no_category": "There is no category. You must have at least one category.", - "page.add_feed.submit": "Find a feed", - "page.add_feed.title": "New feed", - "page.api_keys.never_used": "Never Used", - "page.api_keys.table.actions": "Actions", - "page.api_keys.table.created_at": "Creation Date", - "page.api_keys.table.description": "Description", - "page.api_keys.table.last_used_at": "Last Used", - "page.api_keys.table.token": "Token", - "page.api_keys.title": "API Keys", - "page.categories_count": [ - "%d category", - "%d categories" - ], - "page.categories.entries": "Entries", - "page.categories.feed_count": [ - "There is %d feed.", - "There are %d feeds." - ], - "page.categories.feeds": "Feeds", - "page.categories.no_feed": "No feed.", - "page.categories.title": "Categories", - "page.category_label": "Category: %s", - "page.edit_category.title": "Edit Category: %s", - "page.edit_feed.etag_header": "ETag header:", - "page.edit_feed.last_check": "Last check:", - "page.edit_feed.last_modified_header": "LastModified header:", - "page.edit_feed.last_parsing_error": "Last Parsing Error", - "page.edit_feed.no_header": "None", - "page.edit_feed.title": "Edit Feed: %s", - "page.edit_user.title": "Edit User: %s", - "page.entry.attachments": "Attachments", - "page.feeds.error_count": [ - "%d error", - "%d errors" - ], - "page.feeds.last_check": "Last check:", - "page.feeds.next_check": "Next check:", - "page.feeds.read_counter": "Number of read entries", - "page.feeds.title": "Feeds", - "page.history.title": "History", - "page.import.title": "Import", - "page.integration.bookmarklet": "Bookmarklet", - "page.integration.bookmarklet.help": "This special link allows you to subscribe to a website directly by using a bookmark in your web browser.", - "page.integration.bookmarklet.instructions": "Drag and drop this link to your bookmarks.", - "page.integration.bookmarklet.name": "Add to Miniflux", - "page.integration.miniflux_api": "Miniflux API", - "page.integration.miniflux_api_endpoint": "API Endpoint", - "page.integration.miniflux_api_password": "Password", - "page.integration.miniflux_api_password_value": "Your account password", - "page.integration.miniflux_api_username": "Username", - "page.integrations.title": "Integrations", - "page.keyboard_shortcuts.close_modal": "Close modal dialog", - "page.keyboard_shortcuts.download_content": "Download original content", - "page.keyboard_shortcuts.go_to_bottom_item": "Go to bottom item", - "page.keyboard_shortcuts.go_to_categories": "Go to categories", - "page.keyboard_shortcuts.go_to_feed": "Go to feed", - "page.keyboard_shortcuts.go_to_feeds": "Go to feeds", - "page.keyboard_shortcuts.go_to_history": "Go to history", - "page.keyboard_shortcuts.go_to_next_item": "Go to next item", - "page.keyboard_shortcuts.go_to_next_page": "Go to next page", - "page.keyboard_shortcuts.go_to_previous_item": "Go to previous item", - "page.keyboard_shortcuts.go_to_previous_page": "Go to previous page", - "page.keyboard_shortcuts.go_to_search": "Set focus on search form", - "page.keyboard_shortcuts.go_to_settings": "Go to settings", - "page.keyboard_shortcuts.go_to_starred": "Go to starred", - "page.keyboard_shortcuts.go_to_top_item": "Go to top item", - "page.keyboard_shortcuts.go_to_unread": "Go to unread", - "page.keyboard_shortcuts.mark_page_as_read": "Mark current page as read", - "page.keyboard_shortcuts.open_comments": "Open comments link", - "page.keyboard_shortcuts.open_comments_same_window": "Open comments link in current tab", - "page.keyboard_shortcuts.open_item": "Open selected item", - "page.keyboard_shortcuts.open_original": "Open original link", - "page.keyboard_shortcuts.open_original_same_window": "Open original link in current tab", - "page.keyboard_shortcuts.refresh_all_feeds": "Refresh all feeds in the background", - "page.keyboard_shortcuts.remove_feed": "Remove this feed", - "page.keyboard_shortcuts.save_article": "Save entry", - "page.keyboard_shortcuts.scroll_item_to_top": "Scroll item to top", - "page.keyboard_shortcuts.show_keyboard_shortcuts": "Show keyboard shortcuts", - "page.keyboard_shortcuts.subtitle.actions": "Actions", - "page.keyboard_shortcuts.subtitle.items": "Items Navigation", - "page.keyboard_shortcuts.subtitle.pages": "Pages Navigation", - "page.keyboard_shortcuts.subtitle.sections": "Sections Navigation", - "page.keyboard_shortcuts.title": "Keyboard Shortcuts", - "page.keyboard_shortcuts.toggle_bookmark_status": "Toggle starred", - "page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments", - "page.keyboard_shortcuts.toggle_read_status_next": "Toggle read/unread, focus next", - "page.keyboard_shortcuts.toggle_read_status_prev": "Toggle read/unread, focus previous", - "page.login.google_signin": "Sign in with Google", - "page.login.oidc_signin": "Sign in with %s", - "page.login.title": "Sign In", - "page.login.webauthn_login": "Login with passkey", - "page.login.webauthn_login.error": "Unable to login with passkey", - "page.login.webauthn_login.help": "Please enter your username if you're using a security key. This is not required if you are using a Passkey (discoverable credentials).", - "page.new_api_key.title": "New API Key", - "page.new_category.title": "New Category", - "page.new_user.title": "New User", - "page.offline.message": "You are offline", - "page.offline.refresh_page": "Try to refresh the page", - "page.offline.title": "Offline Mode", - "page.read_entry_count": [ - "%d read entry", - "%d read entries" - ], - "page.search.title": "Search Results", - "page.sessions.table.actions": "Actions", - "page.sessions.table.current_session": "Current Session", - "page.sessions.table.date": "Date", - "page.sessions.table.ip": "IP Address", - "page.sessions.table.user_agent": "User Agent", - "page.sessions.title": "Sessions", - "page.settings.link_google_account": "Link my Google account", - "page.settings.link_oidc_account": "Link my %s account", - "page.settings.title": "Settings", - "page.settings.unlink_google_account": "Unlink my Google account", - "page.settings.unlink_oidc_account": "Unlink my %s account", - "page.settings.webauthn.actions": "Actions", - "page.settings.webauthn.added_on": "Added On", - "page.settings.webauthn.delete": [ - "Remove %d passkey", - "Remove %d passkeys" - ], - "page.settings.webauthn.last_seen_on": "Last Used", - "page.settings.webauthn.passkey_name": "Passkey Name", - "page.settings.webauthn.passkeys": "Passkeys", - "page.settings.webauthn.register": "Register passkey", - "page.settings.webauthn.register.error": "Unable to register passkey", - "page.shared_entries_count": [ - "%d shared entry", - "%d shared entries" - ], - "page.shared_entries.title": "Shared entries", - "page.starred_entry_count": [ - "%d starred entry", - "%d starred entries" - ], - "page.starred.title": "Starred", - "page.total_entry_count": [ - "%d entry in total", - "%d entries in total" - ], - "page.unread_entry_count": [ - "%d unread entry", - "%d unread entries" - ], - "page.unread.title": "Unread", - "page.users.actions": "Actions", - "page.users.admin.no": "No", - "page.users.admin.yes": "Yes", - "page.users.is_admin": "Administrator", - "page.users.last_login": "Last Login", - "page.users.never_logged": "Never", - "page.users.title": "Users", - "page.users.username": "Username", - "page.webauthn_rename.title": "Rename Passkey", - "pagination.first": "First", - "pagination.last": "Last", - "pagination.next": "Next", - "pagination.previous": "Previous", - "search.label": "Search", - "search.placeholder": "Search…", - "search.submit": "Search", - "skip_to_content": "Skip to content", - "time_elapsed.days": [ - "%d day ago", - "%d days ago" - ], - "time_elapsed.hours": [ - "%d hour ago", - "%d hours ago" - ], - "time_elapsed.minutes": [ - "%d minute ago", - "%d minutes ago" - ], - "time_elapsed.months": [ - "%d month ago", - "%d months ago" - ], - "time_elapsed.not_yet": "not yet", - "time_elapsed.now": "just now", - "time_elapsed.weeks": [ - "%d week ago", - "%d weeks ago" - ], - "time_elapsed.years": [ - "%d year ago", - "%d years ago" - ], - "time_elapsed.yesterday": "yesterday", - "tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s", - "tooltip.logged_user": "Logged in as %s" -} \ No newline at end of file + "action.cancel": "cancel", + "action.download": "Download", + "action.edit": "Edit", + "action.home_screen": "Add to home screen", + "action.import": "Import", + "action.login": "Login", + "action.or": "or", + "action.remove": "Remove", + "action.remove_feed": "Remove this feed", + "action.save": "Save", + "action.subscribe": "Subscribe", + "action.update": "Update", + "alert.account_linked": "Your external account is now linked!", + "alert.account_unlinked": "Your external account is now dissociated!", + "alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.", + "alert.feed_error": "There is a problem with this feed", + "alert.no_bookmark": "There are no starred entries.", + "alert.no_category": "There is no category.", + "alert.no_category_entry": "There are no entries in this category.", + "alert.no_feed": "You don’t have any feeds.", + "alert.no_feed_entry": "There are no entries for this feed.", + "alert.no_feed_in_category": "There is no feed for this category.", + "alert.no_history": "There is no history at the moment.", + "alert.no_search_result": "There are no results for this search.", + "alert.no_shared_entry": "There is no shared entry.", + "alert.no_tag_entry": "There are no entries matching this tag.", + "alert.no_unread_entry": "There are no unread entries.", + "alert.no_user": "You are the only user.", + "alert.pocket_linked": "Your Pocket account is now linked!", + "alert.prefs_saved": "Preferences saved!", + "alert.too_many_feeds_refresh": [ + "You have triggered too many feed refreshes. Please wait %d minute before trying again.", + "You have triggered too many feed refreshes. Please wait %d minutes before trying again." + ], + "confirm.loading": "In progress…", + "confirm.no": "no", + "confirm.question": "Are you sure?", + "confirm.question.refresh": "Are you sure you want to force refresh?", + "confirm.yes": "yes", + "enclosure_media_controls.seek": "Seek:", + "enclosure_media_controls.seek.title": "Seek %s seconds", + "enclosure_media_controls.speed": "Speed:", + "enclosure_media_controls.speed.faster": "Faster", + "enclosure_media_controls.speed.faster.title": "Faster by %sx", + "enclosure_media_controls.speed.reset": "Reset", + "enclosure_media_controls.speed.reset.title": "Reset speed to 1x", + "enclosure_media_controls.speed.slower": "Slower", + "enclosure_media_controls.speed.slower.title": "Slower by %sx", + "entry.bookmark.toast.off": "Unstarred", + "entry.bookmark.toast.on": "Starred", + "entry.bookmark.toggle.off": "Unstar", + "entry.bookmark.toggle.on": "Star", + "entry.comments.label": "Comments", + "entry.comments.title": "View Comments", + "entry.estimated_reading_time": ["%d minute read", "%d minutes read"], + "entry.external_link.label": "External link", + "entry.save.completed": "Done!", + "entry.save.label": "Save", + "entry.save.title": "Save this entry", + "entry.save.toast.completed": "Entry saved", + "entry.scraper.completed": "Done!", + "entry.scraper.label": "Download", + "entry.scraper.title": "Fetch original content", + "entry.share.label": "Share", + "entry.share.title": "Share this entry", + "entry.shared_entry.label": "Share", + "entry.shared_entry.title": "Open the public link", + "entry.state.loading": "Loading…", + "entry.state.saving": "Saving…", + "entry.status.read": "Read", + "entry.status.title": "Change entry status", + "entry.status.toast.read": "Marked as read", + "entry.status.toast.unread": "Marked as unread", + "entry.status.unread": "Unread", + "entry.tags.label": "Tags:", + "entry.unshare.label": "Unshare", + "error.api_key_already_exists": "This API Key already exists.", + "error.bad_credentials": "Invalid username or password.", + "error.category_already_exists": "This category already exists.", + "error.category_not_found": "This category does not exist or does not belong to this user.", + "error.database_error": "Database error: %v.", + "error.different_passwords": "Passwords are not the same.", + "error.duplicate_fever_username": "There is already someone else with the same Fever username!", + "error.duplicate_googlereader_username": "There is already someone else with the same Google Reader username!", + "error.duplicate_linked_account": "There is already someone associated with this provider!", + "error.duplicated_feed": "This feed already exists.", + "error.empty_file": "This file is empty.", + "error.entries_per_page_invalid": "The number of entries per page is not valid.", + "error.feed_already_exists": "This feed already exists.", + "error.feed_category_not_found": "This category does not exist or does not belong to this user.", + "error.feed_format_not_detected": "Unable to detect feed format: %v.", + "error.feed_invalid_blocklist_rule": "The block list rule is invalid.", + "error.feed_invalid_keeplist_rule": "The keep list rule is invalid.", + "error.feed_mandatory_fields": "The URL and the category are mandatory.", + "error.feed_not_found": "This feed does not exist or does not belong to this user.", + "error.feed_title_not_empty": "The feed title cannot be empty.", + "error.feed_url_not_empty": "The feed URL cannot be empty.", + "error.fields_mandatory": "All fields are mandatory.", + "error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.", + "error.http_body_read": "Unable to read the HTTP body: %v.", + "error.http_client_error": "HTTP client error: %v.", + "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?", + "error.http_empty_response_body": "The HTTP response body is empty.", + "error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?", + "error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.", + "error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.", + "error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.", + "error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.", + "error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).", + "error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.", + "error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.", + "error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.", + "error.invalid_default_home_page": "Invalid default homepage!", + "error.invalid_display_mode": "Invalid web app display mode.", + "error.invalid_entry_direction": "Invalid entry direction.", + "error.invalid_feed_url": "Invalid feed URL.", + "error.invalid_gesture_nav": "Invalid gesture navigation.", + "error.invalid_language": "Invalid language.", + "error.invalid_site_url": "Invalid site URL.", + "error.invalid_theme": "Invalid theme.", + "error.invalid_timezone": "Invalid timezone.", + "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.", + "error.network_timeout": "This website is too slow and the request timed out: %v", + "error.password_min_length": "The password must have at least 6 characters.", + "error.pocket_access_token": "Unable to fetch access token from Pocket!", + "error.pocket_request_token": "Unable to fetch request token from Pocket!", + "error.settings_block_rule_fieldname_invalid": "Invalid Block rule: rule #%d is missing a valid field name (Options: %s)", + "error.settings_block_rule_invalid_regex": "Invalid Block rule: rule #%d's pattern is not a valid regex", + "error.settings_block_rule_regex_required": "Invalid Block rule: rule #%d's pattern is not provided", + "error.settings_block_rule_separator_required": "Invalid Block rule: rule #%d's pattern is required to be seperated by a '='", + "error.settings_invalid_domain_list": "Invalid domain list. Please provide a space separated list of domains.", + "error.settings_keep_rule_fieldname_invalid": "Invalid Keep rule: rule #%d is missing a valid field name (Options: %s)", + "error.settings_keep_rule_invalid_regex": "Invalid Keep rule: rule #%d's pattern is not a valid regex", + "error.settings_keep_rule_regex_required": "Invalid Keep rule: rule #%d pattern is not provided", + "error.settings_keep_rule_separator_required": "Invalid Keep rule: rule #%d's pattern is required to be seperated by a '='", + "error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.", + "error.settings_media_playback_rate_range": "Playback speed is out of range", + "error.settings_reading_speed_is_positive": "The reading speeds must be positive integers.", + "error.site_url_not_empty": "The site URL cannot be empty.", + "error.subscription_not_found": "Unable to find any feed.", + "error.title_required": "The title is mandatory.", + "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.", + "error.unable_to_create_api_key": "Unable to create this API Key.", + "error.unable_to_create_category": "Unable to create this category.", + "error.unable_to_create_user": "Unable to create this user.", + "error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.", + "error.unable_to_parse_feed": "Unable to parse this feed: %v.", + "error.unable_to_update_category": "Unable to update this category.", + "error.unable_to_update_feed": "Unable to update this feed.", + "error.unable_to_update_user": "Unable to update this user.", + "error.unlink_account_without_password": "You must define a password otherwise you won’t be able to login again.", + "error.user_already_exists": "This user already exists.", + "error.user_mandatory_fields": "The username is mandatory.", + "form.api_key.label.description": "API Key Label", + "form.category.hide_globally": "Hide entries in global unread list", + "form.category.label.title": "Title", + "form.feed.fieldset.general": "General", + "form.feed.fieldset.integration": "Third-Party Services", + "form.feed.fieldset.network_settings": "Network Settings", + "form.feed.fieldset.rules": "Rules", + "form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates", + "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", + "form.feed.label.blocklist_rules": "Block Rules", + "form.feed.label.category": "Category", + "form.feed.label.cookie": "Set Cookies", + "form.feed.label.crawler": "Fetch original content", + "form.feed.label.description": "Description", + "form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting", + "form.feed.label.disabled": "Do not refresh this feed", + "form.feed.label.feed_password": "Feed Password", + "form.feed.label.feed_url": "Feed URL", + "form.feed.label.feed_username": "Feed Username", + "form.feed.label.fetch_via_proxy": "Fetch via proxy", + "form.feed.label.hide_globally": "Hide entries in global unread list", + "form.feed.label.ignore_http_cache": "Ignore HTTP cache", + "form.feed.label.keeplist_rules": "Keep Rules", + "form.feed.label.no_media_player": "No media player (audio/video)", + "form.feed.label.ntfy_activate": "Push entries to ntfy", + "form.feed.label.ntfy_default_priority": "Ntfy default priority", + "form.feed.label.ntfy_high_priority": "Ntfy high priority", + "form.feed.label.ntfy_low_priority": "Ntfy low priority", + "form.feed.label.ntfy_max_priority": "Ntfy max priority", + "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.feed.label.ntfy_priority": "Ntfy priority", + "form.feed.label.ntfy_topic": "Ntfy topic (optional)", + "form.feed.label.pushover_activate": "Push entries to Pushover", + "form.feed.label.pushover_default_priority": "Default priority", + "form.feed.label.pushover_high_priority": "High priority", + "form.feed.label.pushover_low_priority": "Low priority", + "form.feed.label.pushover_max_priority": "Max priority", + "form.feed.label.pushover_min_priority": "Minimal priority", + "form.feed.label.pushover_priority": "Pushover message priority", + "form.feed.label.rewrite_rules": "Rewrite Rules", + "form.feed.label.scraper_rules": "Scraper Rules", + "form.feed.label.site_url": "Site URL", + "form.feed.label.title": "Title", + "form.feed.label.urlrewrite_rules": "URL Rewrite Rules", + "form.feed.label.user_agent": "Override Default User Agent", + "form.feed.label.webhook_url": "Override webhook url", + "form.import.label.file": "OPML file", + "form.import.label.url": "URL", + "form.integration.apprise_activate": "Push entries to Apprise", + "form.integration.apprise_services_url": "Comma separated list of Apprise service URLs", + "form.integration.apprise_url": "Apprise API URL", + "form.integration.betula_activate": "Save entries to Betula", + "form.integration.betula_token": "Betula Token", + "form.integration.betula_url": "Betula server URL", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", + "form.integration.discord_activate": "Push entries to Discord", + "form.integration.discord_webhook_link": "Discord Webhook link", + "form.integration.espial_activate": "Save entries to Espial", + "form.integration.espial_api_key": "Espial API key", + "form.integration.espial_endpoint": "Espial API Endpoint", + "form.integration.espial_tags": "Espial Tags", + "form.integration.fever_activate": "Activate Fever API", + "form.integration.fever_endpoint": "Fever API endpoint:", + "form.integration.fever_password": "Fever Password", + "form.integration.fever_username": "Fever Username", + "form.integration.googlereader_activate": "Activate Google Reader API", + "form.integration.googlereader_endpoint": "Google Reader API endpoint:", + "form.integration.googlereader_password": "Google Reader Password", + "form.integration.googlereader_username": "Google Reader Username", + "form.integration.instapaper_activate": "Save entries to Instapaper", + "form.integration.instapaper_password": "Instapaper Password", + "form.integration.instapaper_username": "Instapaper Username", + "form.integration.linkace_activate": "Save entries to LinkAce", + "form.integration.linkace_api_key": "LinkAce API key", + "form.integration.linkace_check_disabled": "Disable link check", + "form.integration.linkace_endpoint": "LinkAce API Endpoint", + "form.integration.linkace_is_private": "Mark link as private", + "form.integration.linkace_tags": "LinkAce Tags", + "form.integration.linkding_activate": "Save entries to Linkding", + "form.integration.linkding_api_key": "Linkding API key", + "form.integration.linkding_bookmark": "Mark bookmark as unread", + "form.integration.linkding_endpoint": "Linkding API Endpoint", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkwarden_activate": "Save entries to Linkwarden", + "form.integration.linkwarden_api_key": "Linkwarden API key", + "form.integration.linkwarden_endpoint": "Linkwarden API Endpoint", + "form.integration.matrix_bot_activate": "Push new entries to Matrix", + "form.integration.matrix_bot_chat_id": "ID of Matrix Room", + "form.integration.matrix_bot_password": "Password for Matrix user", + "form.integration.matrix_bot_url": "Matrix server URL", + "form.integration.matrix_bot_user": "Username for Matrix", + "form.integration.notion_activate": "Save entries to Notion", + "form.integration.notion_page_id": "Notion Page ID", + "form.integration.notion_token": "Notion Secret Token", + "form.integration.ntfy_activate": "Push entries to ntfy", + "form.integration.ntfy_api_token": "Ntfy API Token (optional)", + "form.integration.ntfy_icon_url": "Ntfy Icon URL (optional)", + "form.integration.ntfy_internal_links": "Use internal links on click (optional)", + "form.integration.ntfy_password": "Ntfy Password (optional)", + "form.integration.ntfy_topic": "Ntfy topic (default used if not set in feed)", + "form.integration.ntfy_url": "Ntfy URL (optional, default is ntfy.sh)", + "form.integration.ntfy_username": "Ntfy Username (optional)", + "form.integration.nunux_keeper_activate": "Save entries to Nunux Keeper", + "form.integration.nunux_keeper_api_key": "Nunux Keeper API key", + "form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint", + "form.integration.omnivore_activate": "Save entries to Omnivore", + "form.integration.omnivore_api_key": "Omnivore API key", + "form.integration.omnivore_url": "Omnivore API Endpoint", + "form.integration.pinboard_activate": "Save entries to Pinboard", + "form.integration.pinboard_bookmark": "Mark bookmark as unread", + "form.integration.pinboard_tags": "Pinboard Tags", + "form.integration.pinboard_token": "Pinboard API Token", + "form.integration.pocket_access_token": "Pocket Access Token", + "form.integration.pocket_activate": "Save entries to Pocket", + "form.integration.pocket_connect_link": "Connect your Pocket account", + "form.integration.pocket_consumer_key": "Pocket Consumer Key", + "form.integration.pushover_activate": "Push entries to Pushover", + "form.integration.pushover_device": "Pushover device (optional)", + "form.integration.pushover_prefix": "Pushover URL prefix (optional)", + "form.integration.pushover_token": "Pushover application API token", + "form.integration.pushover_user": "Pushover user key", + "form.integration.raindrop_activate": "Save entries to Raindrop", + "form.integration.raindrop_collection_id": "Collection ID", + "form.integration.raindrop_tags": "Tags (comma-separated)", + "form.integration.raindrop_token": "(Test) Token", + "form.integration.readeck_activate": "Save entries to readeck", + "form.integration.readeck_api_key": "Readeck API key", + "form.integration.readeck_endpoint": "Readeck URL", + "form.integration.readeck_labels": "Readeck Labels", + "form.integration.readeck_only_url": "Send only URL (instead of full content)", + "form.integration.readwise_activate": "Save entries to Readwise Reader", + "form.integration.readwise_api_key": "Readwise Reader Access Token", + "form.integration.readwise_api_key_link": "Get your Readwise Access Token", + "form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions", + "form.integration.rssbridge_url": "RSS-Bridge server URL", + "form.integration.shaarli_activate": "Save articles to Shaarli", + "form.integration.shaarli_api_secret": "Shaarli API Secret", + "form.integration.shaarli_endpoint": "Shaarli URL", + "form.integration.shiori_activate": "Save articles to Shiori", + "form.integration.shiori_endpoint": "Shiori API Endpoint", + "form.integration.shiori_password": "Shiori Password", + "form.integration.shiori_username": "Shiori Username", + "form.integration.slack_activate": "Push entries to Slack", + "form.integration.slack_webhook_link": "Slack Webhook link", + "form.integration.telegram_bot_activate": "Push new entries to Telegram chat", + "form.integration.telegram_bot_disable_buttons": "Disable buttons", + "form.integration.telegram_bot_disable_notification": "Disable notification", + "form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview", + "form.integration.telegram_bot_token": "Bot token", + "form.integration.telegram_chat_id": "Chat ID", + "form.integration.telegram_topic_id": "Topic ID", + "form.integration.wallabag_activate": "Save entries to Wallabag", + "form.integration.wallabag_client_id": "Wallabag Client ID", + "form.integration.wallabag_client_secret": "Wallabag Client Secret", + "form.integration.wallabag_endpoint": "Wallabag Base URL", + "form.integration.wallabag_only_url": "Send only URL (instead of full content)", + "form.integration.wallabag_password": "Wallabag Password", + "form.integration.wallabag_username": "Wallabag Username", + "form.integration.webhook_activate": "Enable Webhooks", + "form.integration.webhook_secret": "Webhooks Secret", + "form.integration.webhook_url": "Default Webhook URL", + "form.prefs.fieldset.application_settings": "Application Settings", + "form.prefs.fieldset.authentication_settings": "Authentication Settings", + "form.prefs.fieldset.global_feed_settings": "Global Feed Settings", + "form.prefs.fieldset.reader_settings": "Reader Settings", + "form.prefs.help.external_font_hosts": "Space separated list of external font hosts to allow. For example: \"fonts.gstatic.com fonts.googleapis.com\".", + "form.prefs.label.categories_sorting_order": "Categories sorting", + "form.prefs.label.cjk_reading_speed": "Reading speed for Chinese, Korean and Japanese (characters per minute)", + "form.prefs.label.custom_css": "Custom CSS", + "form.prefs.label.custom_js": "Custom JavaScript", + "form.prefs.label.default_home_page": "Default home page", + "form.prefs.label.default_reading_speed": "Reading speed for other languages (words per minute)", + "form.prefs.label.display_mode": "Progressive Web App (PWA) display mode", + "form.prefs.label.entries_per_page": "Entries per page", + "form.prefs.label.entry_order": "Entry sorting column", + "form.prefs.label.entry_sorting": "Entry sorting", + "form.prefs.label.entry_swipe": "Enable entry swipe on touch screens", + "form.prefs.label.external_font_hosts": "External font hosts", + "form.prefs.label.gesture_nav": "Gesture to navigate between entries", + "form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts", + "form.prefs.label.language": "Language", + "form.prefs.label.mark_read_manually": "Mark entries as read manually", + "form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion", + "form.prefs.label.mark_read_on_view": "Automatically mark entries as read when viewed", + "form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion", + "form.prefs.label.media_playback_rate": "Playback speed of the audio/video", + "form.prefs.label.show_reading_time": "Show estimated reading time for entries", + "form.prefs.label.theme": "Theme", + "form.prefs.label.timezone": "Timezone", + "form.prefs.select.alphabetical": "Alphabetical", + "form.prefs.select.browser": "Browser", + "form.prefs.select.created_time": "Entry created time", + "form.prefs.select.fullscreen": "Fullscreen", + "form.prefs.select.minimal_ui": "Minimal", + "form.prefs.select.none": "None", + "form.prefs.select.older_first": "Older entries first", + "form.prefs.select.publish_time": "Entry published time", + "form.prefs.select.recent_first": "Recent entries first", + "form.prefs.select.standalone": "Standalone", + "form.prefs.select.swipe": "Swipe", + "form.prefs.select.tap": "Double tap", + "form.prefs.select.unread_count": "Unread count", + "form.submit.loading": "Loading…", + "form.submit.saving": "Saving…", + "form.user.label.admin": "Administrator", + "form.user.label.confirmation": "Password Confirmation", + "form.user.label.password": "Password", + "form.user.label.username": "Username", + "menu.about": "About", + "menu.add_feed": "Add feed", + "menu.add_user": "Add user", + "menu.api_keys": "API Keys", + "menu.categories": "Categories", + "menu.create_api_key": "Create a new API key", + "menu.create_category": "Create a category", + "menu.edit_category": "Edit", + "menu.edit_feed": "Edit", + "menu.export": "Export", + "menu.feed_entries": "Entries", + "menu.feeds": "Feeds", + "menu.flush_history": "Flush history", + "menu.history": "History", + "menu.home_page": "Home page", + "menu.import": "Import", + "menu.integrations": "Integrations", + "menu.logout": "Logout", + "menu.mark_all_as_read": "Mark all as read", + "menu.mark_page_as_read": "Mark this page as read", + "menu.preferences": "Preferences", + "menu.refresh_all_feeds": "Refresh all feeds in the background", + "menu.refresh_feed": "Refresh", + "menu.search": "Search", + "menu.sessions": "Sessions", + "menu.settings": "Settings", + "menu.shared_entries": "Shared entries", + "menu.show_all_entries": "Show all entries", + "menu.show_only_starred_entries": "Show only starred entries", + "menu.show_only_unread_entries": "Show only unread entries", + "menu.starred": "Starred", + "menu.title": "Menu", + "menu.unread": "Unread", + "menu.users": "Users", + "page.about.author": "Author:", + "page.about.build_date": "Build Date:", + "page.about.credits": "Credits", + "page.about.db_usage": "Database size:", + "page.about.global_config_options": "Global configuration options", + "page.about.go_version": "Go version:", + "page.about.license": "License:", + "page.about.postgres_version": "Postgres version:", + "page.about.title": "About", + "page.about.version": "Version:", + "page.add_feed.choose_feed": "Choose a feed", + "page.add_feed.label.url": "URL", + "page.add_feed.legend.advanced_options": "Advanced Options", + "page.add_feed.no_category": "There is no category. You must have at least one category.", + "page.add_feed.submit": "Find a feed", + "page.add_feed.title": "New feed", + "page.api_keys.never_used": "Never Used", + "page.api_keys.table.actions": "Actions", + "page.api_keys.table.created_at": "Creation Date", + "page.api_keys.table.description": "Description", + "page.api_keys.table.last_used_at": "Last Used", + "page.api_keys.table.token": "Token", + "page.api_keys.title": "API Keys", + "page.categories_count": ["%d category", "%d categories"], + "page.categories.entries": "Entries", + "page.categories.feed_count": ["There is %d feed.", "There are %d feeds."], + "page.categories.feeds": "Feeds", + "page.categories.no_feed": "No feed.", + "page.categories.title": "Categories", + "page.category_label": "Category: %s", + "page.edit_category.title": "Edit Category: %s", + "page.edit_feed.etag_header": "ETag header:", + "page.edit_feed.last_check": "Last check:", + "page.edit_feed.last_modified_header": "LastModified header:", + "page.edit_feed.last_parsing_error": "Last Parsing Error", + "page.edit_feed.no_header": "None", + "page.edit_feed.title": "Edit Feed: %s", + "page.edit_user.title": "Edit User: %s", + "page.entry.attachments": "Attachments", + "page.feeds.error_count": ["%d error", "%d errors"], + "page.feeds.last_check": "Last check:", + "page.feeds.next_check": "Next check:", + "page.feeds.read_counter": "Number of read entries", + "page.feeds.title": "Feeds", + "page.history.title": "History", + "page.import.title": "Import", + "page.integration.bookmarklet": "Bookmarklet", + "page.integration.bookmarklet.help": "This special link allows you to subscribe to a website directly by using a bookmark in your web browser.", + "page.integration.bookmarklet.instructions": "Drag and drop this link to your bookmarks.", + "page.integration.bookmarklet.name": "Add to Miniflux", + "page.integration.miniflux_api": "Miniflux API", + "page.integration.miniflux_api_endpoint": "API Endpoint", + "page.integration.miniflux_api_password": "Password", + "page.integration.miniflux_api_password_value": "Your account password", + "page.integration.miniflux_api_username": "Username", + "page.integrations.title": "Integrations", + "page.keyboard_shortcuts.close_modal": "Close modal dialog", + "page.keyboard_shortcuts.download_content": "Download original content", + "page.keyboard_shortcuts.go_to_bottom_item": "Go to bottom item", + "page.keyboard_shortcuts.go_to_categories": "Go to categories", + "page.keyboard_shortcuts.go_to_feed": "Go to feed", + "page.keyboard_shortcuts.go_to_feeds": "Go to feeds", + "page.keyboard_shortcuts.go_to_history": "Go to history", + "page.keyboard_shortcuts.go_to_next_item": "Go to next item", + "page.keyboard_shortcuts.go_to_next_page": "Go to next page", + "page.keyboard_shortcuts.go_to_previous_item": "Go to previous item", + "page.keyboard_shortcuts.go_to_previous_page": "Go to previous page", + "page.keyboard_shortcuts.go_to_search": "Set focus on search form", + "page.keyboard_shortcuts.go_to_settings": "Go to settings", + "page.keyboard_shortcuts.go_to_starred": "Go to starred", + "page.keyboard_shortcuts.go_to_top_item": "Go to top item", + "page.keyboard_shortcuts.go_to_unread": "Go to unread", + "page.keyboard_shortcuts.mark_page_as_read": "Mark current page as read", + "page.keyboard_shortcuts.open_comments": "Open comments link", + "page.keyboard_shortcuts.open_comments_same_window": "Open comments link in current tab", + "page.keyboard_shortcuts.open_item": "Open selected item", + "page.keyboard_shortcuts.open_original": "Open original link", + "page.keyboard_shortcuts.open_original_same_window": "Open original link in current tab", + "page.keyboard_shortcuts.refresh_all_feeds": "Refresh all feeds in the background", + "page.keyboard_shortcuts.remove_feed": "Remove this feed", + "page.keyboard_shortcuts.save_article": "Save entry", + "page.keyboard_shortcuts.scroll_item_to_top": "Scroll item to top", + "page.keyboard_shortcuts.show_keyboard_shortcuts": "Show keyboard shortcuts", + "page.keyboard_shortcuts.subtitle.actions": "Actions", + "page.keyboard_shortcuts.subtitle.items": "Items Navigation", + "page.keyboard_shortcuts.subtitle.pages": "Pages Navigation", + "page.keyboard_shortcuts.subtitle.sections": "Sections Navigation", + "page.keyboard_shortcuts.title": "Keyboard Shortcuts", + "page.keyboard_shortcuts.toggle_bookmark_status": "Toggle starred", + "page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments", + "page.keyboard_shortcuts.toggle_read_status_next": "Toggle read/unread, focus next", + "page.keyboard_shortcuts.toggle_read_status_prev": "Toggle read/unread, focus previous", + "page.login.google_signin": "Sign in with Google", + "page.login.oidc_signin": "Sign in with %s", + "page.login.title": "Sign In", + "page.login.webauthn_login": "Login with passkey", + "page.login.webauthn_login.error": "Unable to login with passkey", + "page.login.webauthn_login.help": "Please enter your username if you're using a security key. This is not required if you are using a Passkey (discoverable credentials).", + "page.new_api_key.title": "New API Key", + "page.new_category.title": "New Category", + "page.new_user.title": "New User", + "page.offline.message": "You are offline", + "page.offline.refresh_page": "Try to refresh the page", + "page.offline.title": "Offline Mode", + "page.read_entry_count": ["%d read entry", "%d read entries"], + "page.search.title": "Search Results", + "page.sessions.table.actions": "Actions", + "page.sessions.table.current_session": "Current Session", + "page.sessions.table.date": "Date", + "page.sessions.table.ip": "IP Address", + "page.sessions.table.user_agent": "User Agent", + "page.sessions.title": "Sessions", + "page.settings.link_google_account": "Link my Google account", + "page.settings.link_oidc_account": "Link my %s account", + "page.settings.title": "Settings", + "page.settings.unlink_google_account": "Unlink my Google account", + "page.settings.unlink_oidc_account": "Unlink my %s account", + "page.settings.webauthn.actions": "Actions", + "page.settings.webauthn.added_on": "Added On", + "page.settings.webauthn.delete": ["Remove %d passkey", "Remove %d passkeys"], + "page.settings.webauthn.last_seen_on": "Last Used", + "page.settings.webauthn.passkey_name": "Passkey Name", + "page.settings.webauthn.passkeys": "Passkeys", + "page.settings.webauthn.register": "Register passkey", + "page.settings.webauthn.register.error": "Unable to register passkey", + "page.shared_entries_count": ["%d shared entry", "%d shared entries"], + "page.shared_entries.title": "Shared entries", + "page.starred_entry_count": ["%d starred entry", "%d starred entries"], + "page.starred.title": "Starred", + "page.total_entry_count": ["%d entry in total", "%d entries in total"], + "page.unread_entry_count": ["%d unread entry", "%d unread entries"], + "page.unread.title": "Unread", + "page.users.actions": "Actions", + "page.users.admin.no": "No", + "page.users.admin.yes": "Yes", + "page.users.is_admin": "Administrator", + "page.users.last_login": "Last Login", + "page.users.never_logged": "Never", + "page.users.title": "Users", + "page.users.username": "Username", + "page.webauthn_rename.title": "Rename Passkey", + "pagination.first": "First", + "pagination.last": "Last", + "pagination.next": "Next", + "pagination.previous": "Previous", + "search.label": "Search", + "search.placeholder": "Search…", + "search.submit": "Search", + "skip_to_content": "Skip to content", + "time_elapsed.days": ["%d day ago", "%d days ago"], + "time_elapsed.hours": ["%d hour ago", "%d hours ago"], + "time_elapsed.minutes": ["%d minute ago", "%d minutes ago"], + "time_elapsed.months": ["%d month ago", "%d months ago"], + "time_elapsed.not_yet": "not yet", + "time_elapsed.now": "just now", + "time_elapsed.weeks": ["%d week ago", "%d weeks ago"], + "time_elapsed.years": ["%d year ago", "%d years ago"], + "time_elapsed.yesterday": "yesterday", + "tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s", + "tooltip.logged_user": "Logged in as %s", + "skip_to_content": "Skip to content", + "confirm.question": "Are you sure?", + "confirm.question.refresh": "Are you sure you want to force refresh?", + "confirm.yes": "yes", + "confirm.no": "no", + "confirm.loading": "In progress…", + "action.subscribe": "Subscribe", + "action.save": "Save", + "action.or": "or", + "action.cancel": "cancel", + "action.remove": "Remove", + "action.remove_feed": "Remove this feed", + "action.update": "Update", + "action.edit": "Edit", + "action.download": "Download", + "action.import": "Import", + "action.login": "Login", + "action.home_screen": "Add to home screen", + "tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s", + "tooltip.logged_user": "Logged in as %s", + "menu.title": "Menu", + "menu.home_page": "Home page", + "menu.unread": "Unread", + "menu.starred": "Starred", + "menu.history": "History", + "menu.feeds": "Feeds", + "menu.categories": "Categories", + "menu.settings": "Settings", + "menu.logout": "Logout", + "menu.preferences": "Preferences", + "menu.integrations": "Integrations", + "menu.sessions": "Sessions", + "menu.users": "Users", + "menu.about": "About", + "menu.export": "Export", + "menu.import": "Import", + "menu.search": "Search", + "menu.create_category": "Create a category", + "menu.mark_page_as_read": "Mark this page as read", + "menu.mark_all_as_read": "Mark all as read", + "menu.show_all_entries": "Show all entries", + "menu.show_only_starred_entries": "Show only starred entries", + "menu.show_only_unread_entries": "Show only unread entries", + "menu.refresh_feed": "Refresh", + "menu.refresh_all_feeds": "Refresh all feeds in the background", + "menu.edit_feed": "Edit", + "menu.edit_category": "Edit", + "menu.add_feed": "Add feed", + "menu.add_user": "Add user", + "menu.flush_history": "Flush history", + "menu.feed_entries": "Entries", + "menu.api_keys": "API Keys", + "menu.create_api_key": "Create a new API key", + "menu.shared_entries": "Shared entries", + "search.label": "Search", + "search.placeholder": "Search…", + "search.submit": "Search", + "pagination.last": "Last", + "pagination.next": "Next", + "pagination.first": "First", + "pagination.previous": "Previous", + "entry.status.unread": "Unread", + "entry.status.read": "Read", + "entry.status.toast.unread": "Marked as unread", + "entry.status.toast.read": "Marked as read", + "entry.status.title": "Change entry status", + "entry.bookmark.toggle.on": "Star", + "entry.bookmark.toggle.off": "Unstar", + "entry.bookmark.toast.on": "Starred", + "entry.bookmark.toast.off": "Unstarred", + "entry.state.saving": "Saving…", + "entry.state.loading": "Loading…", + "entry.save.label": "Save", + "entry.save.title": "Save this entry", + "entry.save.completed": "Done!", + "entry.save.toast.completed": "Entry saved", + "entry.scraper.label": "Download", + "entry.scraper.title": "Fetch original content", + "entry.scraper.completed": "Done!", + "entry.external_link.label": "External link", + "entry.comments.label": "Comments", + "entry.comments.title": "View Comments", + "entry.share.label": "Share", + "entry.share.title": "Share this entry", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Open the public link", + "entry.shared_entry.label": "Share", + "entry.estimated_reading_time": ["%d minute read", "%d minutes read"], + "entry.tags.label": "Tags:", + "page.shared_entries.title": "Shared entries", + "page.shared_entries_count": ["%d shared entry", "%d shared entries"], + "page.unread.title": "Unread", + "page.unread_entry_count": ["%d unread entry", "%d unread entries"], + "page.total_entry_count": ["%d entry in total", "%d entries in total"], + "page.starred.title": "Starred", + "page.starred_entry_count": ["%d starred entry", "%d starred entries"], + "page.categories.title": "Categories", + "page.categories.no_feed": "No feed.", + "page.categories.entries": "Entries", + "page.categories.feeds": "Feeds", + "page.categories.feed_count": ["There is %d feed.", "There are %d feeds."], + "page.categories_count": ["%d category", "%d categories"], + "page.new_category.title": "New Category", + "page.new_user.title": "New User", + "page.edit_category.title": "Edit Category: %s", + "page.edit_user.title": "Edit User: %s", + "page.feeds.title": "Feeds", + "page.category_label": "Category: %s", + "page.feeds.last_check": "Last check:", + "page.feeds.next_check": "Next check:", + "page.feeds.read_counter": "Number of read entries", + "page.feeds.error_count": ["%d error", "%d errors"], + "page.history.title": "History", + "page.read_entry_count": ["%d read entry", "%d read entries"], + "page.import.title": "Import", + "page.search.title": "Search Results", + "page.about.title": "About", + "page.about.credits": "Credits", + "page.about.version": "Version:", + "page.about.build_date": "Build Date:", + "page.about.author": "Author:", + "page.about.license": "License:", + "page.about.global_config_options": "Global configuration options", + "page.about.postgres_version": "Postgres version:", + "page.about.go_version": "Go version:", + "page.add_feed.title": "New feed", + "page.add_feed.no_category": "There is no category. You must have at least one category.", + "page.add_feed.label.url": "URL", + "page.add_feed.submit": "Find a feed", + "page.add_feed.legend.advanced_options": "Advanced Options", + "page.add_feed.choose_feed": "Choose a feed", + "page.edit_feed.title": "Edit Feed: %s", + "page.edit_feed.last_check": "Last check:", + "page.edit_feed.last_modified_header": "LastModified header:", + "page.edit_feed.etag_header": "ETag header:", + "page.edit_feed.no_header": "None", + "page.edit_feed.last_parsing_error": "Last Parsing Error", + "page.entry.attachments": "Attachments", + "page.keyboard_shortcuts.title": "Keyboard Shortcuts", + "page.keyboard_shortcuts.subtitle.sections": "Sections Navigation", + "page.keyboard_shortcuts.subtitle.items": "Items Navigation", + "page.keyboard_shortcuts.subtitle.pages": "Pages Navigation", + "page.keyboard_shortcuts.subtitle.actions": "Actions", + "page.keyboard_shortcuts.go_to_unread": "Go to unread", + "page.keyboard_shortcuts.go_to_starred": "Go to starred", + "page.keyboard_shortcuts.go_to_history": "Go to history", + "page.keyboard_shortcuts.go_to_feeds": "Go to feeds", + "page.keyboard_shortcuts.go_to_categories": "Go to categories", + "page.keyboard_shortcuts.go_to_settings": "Go to settings", + "page.keyboard_shortcuts.show_keyboard_shortcuts": "Show keyboard shortcuts", + "page.keyboard_shortcuts.go_to_previous_item": "Go to previous item", + "page.keyboard_shortcuts.go_to_next_item": "Go to next item", + "page.keyboard_shortcuts.go_to_feed": "Go to feed", + "page.keyboard_shortcuts.go_to_top_item": "Go to top item", + "page.keyboard_shortcuts.go_to_bottom_item": "Go to bottom item", + "page.keyboard_shortcuts.go_to_previous_page": "Go to previous page", + "page.keyboard_shortcuts.go_to_next_page": "Go to next page", + "page.keyboard_shortcuts.open_item": "Open selected item", + "page.keyboard_shortcuts.open_original": "Open original link", + "page.keyboard_shortcuts.open_original_same_window": "Open original link in current tab", + "page.keyboard_shortcuts.open_comments": "Open comments link", + "page.keyboard_shortcuts.open_comments_same_window": "Open comments link in current tab", + "page.keyboard_shortcuts.toggle_read_status_next": "Toggle read/unread, focus next", + "page.keyboard_shortcuts.toggle_read_status_prev": "Toggle read/unread, focus previous", + "page.keyboard_shortcuts.refresh_all_feeds": "Refresh all feeds in the background", + "page.keyboard_shortcuts.mark_page_as_read": "Mark current page as read", + "page.keyboard_shortcuts.download_content": "Download original content", + "page.keyboard_shortcuts.toggle_bookmark_status": "Toggle starred", + "page.keyboard_shortcuts.save_article": "Save entry", + "page.keyboard_shortcuts.scroll_item_to_top": "Scroll item to top", + "page.keyboard_shortcuts.remove_feed": "Remove this feed", + "page.keyboard_shortcuts.go_to_search": "Set focus on search form", + "page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments", + "page.keyboard_shortcuts.close_modal": "Close modal dialog", + "page.users.title": "Users", + "page.users.username": "Username", + "page.users.never_logged": "Never", + "page.users.admin.yes": "Yes", + "page.users.admin.no": "No", + "page.users.actions": "Actions", + "page.users.last_login": "Last Login", + "page.users.is_admin": "Administrator", + "page.settings.title": "Settings", + "page.settings.link_google_account": "Link my Google account", + "page.settings.unlink_google_account": "Unlink my Google account", + "page.settings.link_oidc_account": "Link my %s account", + "page.settings.unlink_oidc_account": "Unlink my %s account", + "page.settings.webauthn.passkeys": "Passkeys", + "page.settings.webauthn.actions": "Actions", + "page.settings.webauthn.passkey_name": "Passkey Name", + "page.settings.webauthn.added_on": "Added On", + "page.settings.webauthn.last_seen_on": "Last Used", + "page.settings.webauthn.register": "Register passkey", + "page.settings.webauthn.register.error": "Unable to register passkey", + "page.settings.webauthn.delete": ["Remove %d passkey", "Remove %d passkeys"], + "page.login.title": "Sign In", + "page.login.google_signin": "Sign in with Google", + "page.login.oidc_signin": "Sign in with %s", + "page.login.webauthn_login": "Login with passkey", + "page.login.webauthn_login.error": "Unable to login with passkey", + "page.login.webauthn_login.help": "Please enter your username if you're using a security key. This is not required if you are using a Passkey (discoverable credentials).", + "page.integrations.title": "Integrations", + "page.integration.miniflux_api": "Miniflux API", + "page.integration.miniflux_api_endpoint": "API Endpoint", + "page.integration.miniflux_api_username": "Username", + "page.integration.miniflux_api_password": "Password", + "page.integration.miniflux_api_password_value": "Your account password", + "page.integration.bookmarklet": "Bookmarklet", + "page.integration.bookmarklet.name": "Add to Miniflux", + "page.integration.bookmarklet.instructions": "Drag and drop this link to your bookmarks.", + "page.integration.bookmarklet.help": "This special link allows you to subscribe to a website directly by using a bookmark in your web browser.", + "page.sessions.title": "Sessions", + "page.sessions.table.date": "Date", + "page.sessions.table.ip": "IP Address", + "page.sessions.table.user_agent": "User Agent", + "page.sessions.table.actions": "Actions", + "page.sessions.table.current_session": "Current Session", + "page.api_keys.title": "API Keys", + "page.api_keys.table.description": "Description", + "page.api_keys.table.token": "Token", + "page.api_keys.table.last_used_at": "Last Used", + "page.api_keys.table.created_at": "Creation Date", + "page.api_keys.table.actions": "Actions", + "page.api_keys.never_used": "Never Used", + "page.new_api_key.title": "New API Key", + "page.offline.title": "Offline Mode", + "page.offline.message": "You are offline", + "page.offline.refresh_page": "Try to refresh the page", + "page.webauthn_rename.title": "Rename Passkey", + "alert.no_shared_entry": "There is no shared entry.", + "alert.no_bookmark": "There are no starred entries.", + "alert.no_category": "There is no category.", + "alert.no_category_entry": "There are no entries in this category.", + "alert.no_tag_entry": "There are no entries matching this tag.", + "alert.no_feed_entry": "There are no entries for this feed.", + "alert.no_feed": "You don’t have any feeds.", + "alert.no_feed_in_category": "There is no feed for this category.", + "alert.no_history": "There is no history at the moment.", + "alert.feed_error": "There is a problem with this feed", + "alert.no_search_result": "There are no results for this search.", + "alert.no_unread_entry": "There are no unread entries.", + "alert.no_user": "You are the only user.", + "alert.account_unlinked": "Your external account is now dissociated!", + "alert.account_linked": "Your external account is now linked!", + "alert.pocket_linked": "Your Pocket account is now linked!", + "alert.prefs_saved": "Preferences saved!", + "error.unlink_account_without_password": "You must define a password otherwise you won’t be able to login again.", + "error.duplicate_linked_account": "There is already someone associated with this provider!", + "error.duplicate_fever_username": "There is already someone else with the same Fever username!", + "error.duplicate_googlereader_username": "There is already someone else with the same Google Reader username!", + "error.pocket_request_token": "Unable to fetch request token from Pocket!", + "error.pocket_access_token": "Unable to fetch access token from Pocket!", + "error.category_already_exists": "This category already exists.", + "error.unable_to_create_category": "Unable to create this category.", + "error.unable_to_update_category": "Unable to update this category.", + "error.user_already_exists": "This user already exists.", + "error.unable_to_create_user": "Unable to create this user.", + "error.unable_to_update_user": "Unable to update this user.", + "error.unable_to_update_feed": "Unable to update this feed.", + "error.subscription_not_found": "Unable to find any feed.", + "error.invalid_theme": "Invalid theme.", + "error.invalid_language": "Invalid language.", + "error.invalid_timezone": "Invalid timezone.", + "error.invalid_entry_direction": "Invalid entry direction.", + "error.invalid_display_mode": "Invalid web app display mode.", + "error.invalid_gesture_nav": "Invalid gesture navigation.", + "error.invalid_default_home_page": "Invalid default homepage!", + "error.empty_file": "This file is empty.", + "error.bad_credentials": "Invalid username or password.", + "error.fields_mandatory": "All fields are mandatory.", + "error.title_required": "The title is mandatory.", + "error.different_passwords": "Passwords are not the same.", + "error.password_min_length": "The password must have at least 6 characters.", + "error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.", + "error.settings_reading_speed_is_positive": "The reading speeds must be positive integers.", + "error.settings_block_rule_fieldname_invalid": "Invalid Block rule: rule #%d is missing a valid field name (Options: %s)", + "error.settings_block_rule_separator_required": "Invalid Block rule: rule #%d's pattern is required to be seperated by a '='", + "error.settings_block_rule_regex_required": "Invalid Block rule: rule #%d's pattern is not provided", + "error.settings_block_rule_invalid_regex": "Invalid Block rule: rule #%d's pattern is not a valid regex", + "error.settings_keep_rule_fieldname_invalid": "Invalid Keep rule: rule #%d is missing a valid field name (Options: %s)", + "error.settings_keep_rule_separator_required": "Invalid Keep rule: rule #%d's pattern is required to be seperated by a '='", + "error.settings_keep_rule_regex_required": "Invalid Keep rule: rule #%d pattern is not provided", + "error.settings_keep_rule_invalid_regex": "Invalid Keep rule: rule #%d's pattern is not a valid regex", + "error.entries_per_page_invalid": "The number of entries per page is not valid.", + "error.feed_mandatory_fields": "The URL and the category are mandatory.", + "error.feed_already_exists": "This feed already exists.", + "error.invalid_feed_url": "Invalid feed URL.", + "error.invalid_site_url": "Invalid site URL.", + "error.feed_url_not_empty": "The feed URL cannot be empty.", + "error.site_url_not_empty": "The site URL cannot be empty.", + "error.feed_title_not_empty": "The feed title cannot be empty.", + "error.feed_category_not_found": "This category does not exist or does not belong to this user.", + "error.feed_invalid_blocklist_rule": "The block list rule is invalid.", + "error.feed_invalid_keeplist_rule": "The keep list rule is invalid.", + "error.user_mandatory_fields": "The username is mandatory.", + "error.api_key_already_exists": "This API Key already exists.", + "error.unable_to_create_api_key": "Unable to create this API Key.", + "form.feed.label.title": "Title", + "form.feed.label.site_url": "Site URL", + "form.feed.label.feed_url": "Feed URL", + "form.feed.label.description": "Description", + "form.feed.label.category": "Category", + "form.feed.label.crawler": "Fetch original content", + "form.feed.label.feed_username": "Feed Username", + "form.feed.label.feed_password": "Feed Password", + "form.feed.label.user_agent": "Override Default User Agent", + "form.feed.label.cookie": "Set Cookies", + "form.feed.label.scraper_rules": "Scraper Rules", + "form.feed.label.rewrite_rules": "Rewrite Rules", + "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", + "form.feed.label.blocklist_rules": "Block Rules", + "form.feed.label.keeplist_rules": "Keep Rules", + "form.feed.label.urlrewrite_rules": "URL Rewrite Rules", + "form.feed.label.ignore_http_cache": "Ignore HTTP cache", + "form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates", + "form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting", + "form.feed.label.fetch_via_proxy": "Fetch via proxy", + "form.feed.label.disabled": "Do not refresh this feed", + "form.feed.label.no_media_player": "No media player (audio/video)", + "form.feed.label.hide_globally": "Hide entries in global unread list", + "form.feed.label.ntfy_activate": "Push entries to ntfy", + "form.feed.label.ntfy_priority": "Ntfy priority", + "form.feed.label.ntfy_max_priority": "Ntfy max priority", + "form.feed.label.ntfy_high_priority": "Ntfy high priority", + "form.feed.label.ntfy_default_priority": "Ntfy default priority", + "form.feed.label.ntfy_low_priority": "Ntfy low priority", + "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.feed.fieldset.general": "General", + "form.feed.fieldset.rules": "Rules", + "form.feed.fieldset.network_settings": "Network Settings", + "form.feed.fieldset.integration": "Third-Party Services", + "form.category.label.title": "Title", + "form.category.hide_globally": "Hide entries in global unread list", + "form.user.label.username": "Username", + "form.user.label.password": "Password", + "form.user.label.confirmation": "Password Confirmation", + "form.user.label.admin": "Administrator", + "form.prefs.label.language": "Language", + "form.prefs.label.timezone": "Timezone", + "form.prefs.label.theme": "Theme", + "form.prefs.label.entry_sorting": "Entry sorting", + "form.prefs.label.entries_per_page": "Entries per page", + "form.prefs.label.default_reading_speed": "Reading speed for other languages (words per minute)", + "form.prefs.label.cjk_reading_speed": "Reading speed for Chinese, Korean and Japanese (characters per minute)", + "form.prefs.label.display_mode": "Progressive Web App (PWA) display mode", + "form.prefs.select.older_first": "Older entries first", + "form.prefs.select.recent_first": "Recent entries first", + "form.prefs.select.fullscreen": "Fullscreen", + "form.prefs.select.standalone": "Standalone", + "form.prefs.select.minimal_ui": "Minimal", + "form.prefs.select.browser": "Browser", + "form.prefs.select.publish_time": "Entry published time", + "form.prefs.select.created_time": "Entry created time", + "form.prefs.select.alphabetical": "Alphabetical", + "form.prefs.select.unread_count": "Unread count", + "form.prefs.select.none": "None", + "form.prefs.select.tap": "Double tap", + "form.prefs.select.swipe": "Swipe", + "form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts", + "form.prefs.label.entry_swipe": "Enable entry swipe on touch screens", + "form.prefs.label.gesture_nav": "Gesture to navigate between entries", + "form.prefs.label.show_reading_time": "Show estimated reading time for entries", + "form.prefs.label.custom_css": "Custom CSS", + "form.prefs.label.custom_js": "Custom JavaScript", + "form.prefs.label.entry_order": "Entry sorting column", + "form.prefs.label.default_home_page": "Default home page", + "form.prefs.label.categories_sorting_order": "Categories sorting", + "form.prefs.label.mark_read_on_view": "Automatically mark entries as read when viewed", + "form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion", + "form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion", + "form.prefs.label.mark_read_manually": "Mark entries as read manually", + "form.prefs.label.cache_for_offline": "Cache entries for offline reading", + "form.prefs.fieldset.application_settings": "Application Settings", + "form.prefs.fieldset.authentication_settings": "Authentication Settings", + "form.prefs.fieldset.reader_settings": "Reader Settings", + "form.prefs.fieldset.global_feed_settings": "Global Feed Settings", + "form.prefs.label.external_font_hosts": "External font hosts", + "form.prefs.help.external_font_hosts": "Space separated list of external font hosts to allow. For example: \"fonts.gstatic.com fonts.googleapis.com\".", + "error.settings_invalid_domain_list": "Invalid domain list. Please provide a space separated list of domains.", + "form.import.label.file": "OPML file", + "form.import.label.url": "URL", + "form.integration.betula_activate": "Save entries to Betula", + "form.integration.betula_url": "Betula server URL", + "form.integration.betula_token": "Betula Token", + "form.integration.fever_activate": "Activate Fever API", + "form.integration.fever_username": "Fever Username", + "form.integration.fever_password": "Fever Password", + "form.integration.fever_endpoint": "Fever API endpoint:", + "form.integration.googlereader_activate": "Activate Google Reader API", + "form.integration.googlereader_username": "Google Reader Username", + "form.integration.googlereader_password": "Google Reader Password", + "form.integration.googlereader_endpoint": "Google Reader API endpoint:", + "form.integration.pinboard_activate": "Save entries to Pinboard", + "form.integration.pinboard_token": "Pinboard API Token", + "form.integration.pinboard_tags": "Pinboard Tags", + "form.integration.pinboard_bookmark": "Mark bookmark as unread", + "form.integration.instapaper_activate": "Save entries to Instapaper", + "form.integration.instapaper_username": "Instapaper Username", + "form.integration.instapaper_password": "Instapaper Password", + "form.integration.pocket_activate": "Save entries to Pocket", + "form.integration.pocket_consumer_key": "Pocket Consumer Key", + "form.integration.pocket_access_token": "Pocket Access Token", + "form.integration.pocket_connect_link": "Connect your Pocket account", + "form.integration.wallabag_activate": "Save entries to Wallabag", + "form.integration.wallabag_only_url": "Send only URL (instead of full content)", + "form.integration.wallabag_endpoint": "Wallabag API Endpoint", + "form.integration.wallabag_client_id": "Wallabag Client ID", + "form.integration.wallabag_client_secret": "Wallabag Client Secret", + "form.integration.wallabag_username": "Wallabag Username", + "form.integration.wallabag_password": "Wallabag Password", + "form.integration.notion_activate": "Save entries to Notion", + "form.integration.notion_page_id": "Notion Page ID", + "form.integration.notion_token": "Notion Secret Token", + "form.integration.apprise_activate": "Push entries to Apprise", + "form.integration.apprise_url": "Apprise API URL", + "form.integration.apprise_services_url": "Comma separated list of Apprise service URLs", + "form.integration.nunux_keeper_activate": "Save entries to Nunux Keeper", + "form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint", + "form.integration.nunux_keeper_api_key": "Nunux Keeper API key", + "form.integration.omnivore_activate": "Save entries to Omnivore", + "form.integration.omnivore_api_key": "Omnivore API key", + "form.integration.omnivore_url": "Omnivore API Endpoint", + "form.integration.espial_activate": "Save entries to Espial", + "form.integration.espial_endpoint": "Espial API Endpoint", + "form.integration.espial_api_key": "Espial API key", + "form.integration.espial_tags": "Espial Tags", + "form.integration.readwise_activate": "Save entries to Readwise Reader", + "form.integration.readwise_api_key": "Readwise Reader Access Token", + "form.integration.readwise_api_key_link": "Get your Readwise Access Token", + "form.integration.telegram_bot_activate": "Push new entries to Telegram chat", + "form.integration.telegram_bot_token": "Bot token", + "form.integration.telegram_chat_id": "Chat ID", + "form.integration.telegram_topic_id": "Topic ID", + "form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview", + "form.integration.telegram_bot_disable_notification": "Disable notification", + "form.integration.telegram_bot_disable_buttons": "Disable buttons", + "form.integration.linkace_activate": "Save entries to LinkAce", + "form.integration.linkace_endpoint": "LinkAce API Endpoint", + "form.integration.linkace_api_key": "LinkAce API key", + "form.integration.linkace_tags": "LinkAce Tags", + "form.integration.linkace_is_private": "Mark link as private", + "form.integration.linkace_check_disabled": "Disable link check", + "form.integration.linkding_activate": "Save entries to Linkding", + "form.integration.linkding_endpoint": "Linkding API Endpoint", + "form.integration.linkding_api_key": "Linkding API key", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Mark bookmark as unread", + "form.integration.linkwarden_activate": "Save entries to Linkwarden", + "form.integration.linkwarden_endpoint": "Linkwarden API Endpoint", + "form.integration.linkwarden_api_key": "Linkwarden API key", + "form.integration.matrix_bot_activate": "Push new entries to Matrix", + "form.integration.matrix_bot_user": "Username for Matrix", + "form.integration.matrix_bot_password": "Password for Matrix user", + "form.integration.matrix_bot_url": "Matrix server URL", + "form.integration.matrix_bot_chat_id": "ID of Matrix Room", + "form.integration.raindrop_activate": "Save entries to Raindrop", + "form.integration.raindrop_token": "(Test) Token", + "form.integration.raindrop_collection_id": "Collection ID", + "form.integration.raindrop_tags": "Tags (comma-separated)", + "form.integration.readeck_activate": "Save entries to readeck", + "form.integration.readeck_endpoint": "Readeck URL", + "form.integration.readeck_api_key": "Readeck API key", + "form.integration.readeck_labels": "Readeck Labels", + "form.integration.readeck_only_url": "Send only URL (instead of full content)", + "form.integration.shiori_activate": "Save articles to Shiori", + "form.integration.shiori_endpoint": "Shiori API Endpoint", + "form.integration.shiori_username": "Shiori Username", + "form.integration.shiori_password": "Shiori Password", + "form.integration.shaarli_activate": "Save articles to Shaarli", + "form.integration.shaarli_endpoint": "Shaarli URL", + "form.integration.shaarli_api_secret": "Shaarli API Secret", + "form.integration.webhook_activate": "Enable Webhook", + "form.integration.webhook_url": "Webhook URL", + "form.integration.webhook_secret": "Webhook Secret", + "form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions", + "form.integration.rssbridge_url": "RSS-Bridge server URL", + "form.integration.ntfy_activate": "Push entries to ntfy", + "form.integration.ntfy_topic": "Ntfy topic", + "form.integration.ntfy_url": "Ntfy URL (optional, default is ntfy.sh)", + "form.integration.ntfy_api_token": "Ntfy API Token (optional)", + "form.integration.ntfy_username": "Ntfy Username (optional)", + "form.integration.ntfy_password": "Ntfy Password (optional)", + "form.integration.ntfy_icon_url": "Ntfy Icon URL (optional)", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", + "form.api_key.label.description": "API Key Label", + "form.submit.loading": "Loading…", + "form.submit.saving": "Saving…", + "time_elapsed.not_yet": "not yet", + "time_elapsed.yesterday": "yesterday", + "time_elapsed.now": "just now", + "time_elapsed.minutes": ["%d minute ago", "%d minutes ago"], + "time_elapsed.hours": ["%d hour ago", "%d hours ago"], + "time_elapsed.days": ["%d day ago", "%d days ago"], + "time_elapsed.weeks": ["%d week ago", "%d weeks ago"], + "time_elapsed.months": ["%d month ago", "%d months ago"], + "time_elapsed.years": ["%d year ago", "%d years ago"], + "alert.too_many_feeds_refresh": [ + "You have triggered too many feed refreshes. Please wait %d minute before trying again.", + "You have triggered too many feed refreshes. Please wait %d minutes before trying again." + ], + "alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.", + "error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).", + "error.http_body_read": "Unable to read the HTTP body: %v.", + "error.http_empty_response_body": "The HTTP response body is empty.", + "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?", + "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.", + "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.", + "error.network_timeout": "This website is too slow and the request timed out: %v", + "error.http_client_error": "HTTP client error: %v.", + "error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.", + "error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.", + "error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?", + "error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.", + "error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.", + "error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.", + "error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.", + "error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.", + "error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.", + "error.database_error": "Database error: %v.", + "error.category_not_found": "This category does not exist or does not belong to this user.", + "error.duplicated_feed": "This feed already exists.", + "error.unable_to_parse_feed": "Unable to parse this feed: %v.", + "error.feed_not_found": "This feed does not exist or does not belong to this user.", + "error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.", + "error.feed_format_not_detected": "Unable to detect feed format: %v.", + "form.prefs.label.media_playback_rate": "Playback speed of the audio/video", + "error.settings_media_playback_rate_range": "Playback speed is out of range", + "enclosure_media_controls.seek": "Seek:", + "enclosure_media_controls.seek.title": "Seek %s seconds", + "enclosure_media_controls.speed": "Speed:", + "enclosure_media_controls.speed.faster": "Faster", + "enclosure_media_controls.speed.faster.title": "Faster by %sx", + "enclosure_media_controls.speed.slower": "Slower", + "enclosure_media_controls.speed.slower.title": "Slower by %sx", + "enclosure_media_controls.speed.reset": "Reset", + "enclosure_media_controls.speed.reset.title": "Reset speed to 1x" +} diff --git a/internal/model/user.go b/internal/model/user.go index ad070904..cc0aecdc 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -41,6 +41,7 @@ type User struct { MediaPlaybackRate float64 `json:"media_playback_rate"` BlockFilterEntryRules string `json:"block_filter_entry_rules"` KeepFilterEntryRules string `json:"keep_filter_entry_rules"` + CacheForOffline bool `json:"cache_for_offline"` } // UserCreationRequest represents the request to create a user. @@ -82,6 +83,7 @@ type UserModificationRequest struct { MediaPlaybackRate *float64 `json:"media_playback_rate"` BlockFilterEntryRules *string `json:"block_filter_entry_rules"` KeepFilterEntryRules *string `json:"keep_filter_entry_rules"` + CacheForOffline *bool `json:"cache_for_offline"` } // Patch updates the User object with the modification request. @@ -197,6 +199,9 @@ func (u *UserModificationRequest) Patch(user *User) { if u.KeepFilterEntryRules != nil { user.KeepFilterEntryRules = *u.KeepFilterEntryRules } + if u.CacheForOffline != nil { + user.CacheForOffline = *u.CacheForOffline + } } // UseTimezone converts last login date to the given timezone. diff --git a/internal/storage/user.go b/internal/storage/user.go index 64b6d503..94e3ec24 100644 --- a/internal/storage/user.go +++ b/internal/storage/user.go @@ -96,7 +96,8 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m mark_read_on_view, media_playback_rate, block_filter_entry_rules, - keep_filter_entry_rules + keep_filter_entry_rules, + cache_for_offline ` tx, err := s.db.Begin() @@ -140,6 +141,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m &user.MediaPlaybackRate, &user.BlockFilterEntryRules, &user.KeepFilterEntryRules, + &user.CacheForOffline, ) if err != nil { tx.Rollback() @@ -204,9 +206,10 @@ func (s *Storage) UpdateUser(user *model.User) error { mark_read_on_media_player_completion=$25, media_playback_rate=$26, block_filter_entry_rules=$27, - keep_filter_entry_rules=$28 + keep_filter_entry_rules=$28, + cache_for_offline=$29 WHERE - id=$29 + id=$30 ` _, err = s.db.Exec( @@ -239,6 +242,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.MediaPlaybackRate, user.BlockFilterEntryRules, user.KeepFilterEntryRules, + user.CacheForOffline, user.ID, ) if err != nil { @@ -273,9 +277,10 @@ func (s *Storage) UpdateUser(user *model.User) error { mark_read_on_media_player_completion=$24, media_playback_rate=$25, block_filter_entry_rules=$26, - keep_filter_entry_rules=$27 + keep_filter_entry_rules=$27, + cache_for_offline=$28 WHERE - id=$28 + id=$29 ` _, err := s.db.Exec( @@ -307,6 +312,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.MediaPlaybackRate, user.BlockFilterEntryRules, user.KeepFilterEntryRules, + user.CacheForOffline, user.ID, ) @@ -360,7 +366,8 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) { mark_read_on_media_player_completion, media_playback_rate, block_filter_entry_rules, - keep_filter_entry_rules + keep_filter_entry_rules, + cache_for_offline FROM users WHERE @@ -401,7 +408,8 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) { mark_read_on_media_player_completion, media_playback_rate, block_filter_entry_rules, - keep_filter_entry_rules + keep_filter_entry_rules, + cache_for_offline FROM users WHERE @@ -442,7 +450,8 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) { mark_read_on_media_player_completion, media_playback_rate, block_filter_entry_rules, - keep_filter_entry_rules + keep_filter_entry_rules, + cache_for_offline FROM users WHERE @@ -490,7 +499,8 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) { u.mark_read_on_media_player_completion, media_playback_rate, u.block_filter_entry_rules, - u.keep_filter_entry_rules + u.keep_filter_entry_rules, + u.cache_for_offline FROM users u LEFT JOIN @@ -533,6 +543,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err &user.MediaPlaybackRate, &user.BlockFilterEntryRules, &user.KeepFilterEntryRules, + &user.CacheForOffline, ) if err == sql.ErrNoRows { @@ -646,7 +657,9 @@ func (s *Storage) Users() (model.Users, error) { mark_read_on_media_player_completion, media_playback_rate, block_filter_entry_rules, - keep_filter_entry_rules + keep_filter_entry_rules, + media_playback_rate, + cache_for_offline FROM users ORDER BY username ASC @@ -690,6 +703,7 @@ func (s *Storage) Users() (model.Users, error) { &user.MediaPlaybackRate, &user.BlockFilterEntryRules, &user.KeepFilterEntryRules, + &user.CacheForOffline, ) if err != nil { diff --git a/internal/ui/form/settings.go b/internal/ui/form/settings.go index 34f3a281..b00effaa 100644 --- a/internal/ui/form/settings.go +++ b/internal/ui/form/settings.go @@ -52,6 +52,7 @@ type SettingsForm struct { MediaPlaybackRate float64 BlockFilterEntryRules string KeepFilterEntryRules string + CacheForOffline bool } // MarkAsReadBehavior returns the MarkReadBehavior from the given MarkReadOnView and MarkReadOnMediaPlayerCompletion values. @@ -119,6 +120,8 @@ func (s *SettingsForm) Merge(user *model.User) *model.User { user.MarkReadOnView = MarkReadOnView user.MarkReadOnMediaPlayerCompletion = MarkReadOnMediaPlayerCompletion + user.CacheForOffline = s.CacheForOffline + if s.Password != "" { user.Password = s.Password } @@ -205,5 +208,6 @@ func NewSettingsForm(r *http.Request) *SettingsForm { MediaPlaybackRate: mediaPlaybackRate, BlockFilterEntryRules: r.FormValue("block_filter_entry_rules"), KeepFilterEntryRules: r.FormValue("keep_filter_entry_rules"), + CacheForOffline: r.FormValue("cache_for_offline") == "1", } } diff --git a/internal/ui/settings_show.go b/internal/ui/settings_show.go index 6f2b92af..c5cc3aa9 100644 --- a/internal/ui/settings_show.go +++ b/internal/ui/settings_show.go @@ -46,6 +46,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) { MediaPlaybackRate: user.MediaPlaybackRate, BlockFilterEntryRules: user.BlockFilterEntryRules, KeepFilterEntryRules: user.KeepFilterEntryRules, + CacheForOffline: user.CacheForOffline, } timezones, err := h.store.Timezones() diff --git a/internal/ui/static/js/app.js b/internal/ui/static/js/app.js index bb6b997a..e05f4d20 100644 --- a/internal/ui/static/js/app.js +++ b/internal/ui/static/js/app.js @@ -1,72 +1,87 @@ // OnClick attaches a listener to the elements that match the selector. function onClick(selector, callback, noPreventDefault) { - document.querySelectorAll(selector).forEach((element) => { - element.onclick = (event) => { - if (!noPreventDefault) { - event.preventDefault(); - } - callback(event); - }; - }); + document.querySelectorAll(selector).forEach((element) => { + element.onclick = (event) => { + if (!noPreventDefault) { + event.preventDefault(); + } + callback(event); + }; + }); } function onAuxClick(selector, callback, noPreventDefault) { - document.querySelectorAll(selector).forEach((element) => { - element.onauxclick = (event) => { - if (!noPreventDefault) { - event.preventDefault(); - } - callback(event); - }; - }); + document.querySelectorAll(selector).forEach((element) => { + element.onauxclick = (event) => { + if (!noPreventDefault) { + event.preventDefault(); + } + callback(event); + }; + }); } // make logo element as button on mobile layout function checkMenuToggleModeByLayout() { - const logoElement = document.querySelector(".logo"); - if (!logoElement) return; + const logoElement = document.querySelector(".logo"); + if (!logoElement) return; - const homePageLinkElement = document.querySelector(".logo > a"); + const homePageLinkElement = document.querySelector(".logo > a"); - if (document.documentElement.clientWidth < 620) { - const navMenuElement = document.getElementById("header-menu"); - const navMenuElementIsExpanded = navMenuElement.classList.contains("js-menu-show"); - const logoToggleButtonLabel = logoElement.getAttribute("data-toggle-button-label"); - logoElement.setAttribute("role", "button"); - logoElement.setAttribute("tabindex", "0"); - logoElement.setAttribute("aria-label", logoToggleButtonLabel); - logoElement.setAttribute("aria-expanded", navMenuElementIsExpanded?"true":"false"); - homePageLinkElement.setAttribute("tabindex", "-1"); - } else { - logoElement.removeAttribute("role"); - logoElement.removeAttribute("tabindex"); - logoElement.removeAttribute("aria-expanded"); - logoElement.removeAttribute("aria-label"); - homePageLinkElement.removeAttribute("tabindex"); - } + if (document.documentElement.clientWidth < 620) { + const navMenuElement = document.getElementById("header-menu"); + const navMenuElementIsExpanded = + navMenuElement.classList.contains("js-menu-show"); + const logoToggleButtonLabel = logoElement.getAttribute( + "data-toggle-button-label", + ); + logoElement.setAttribute("role", "button"); + logoElement.setAttribute("tabindex", "0"); + logoElement.setAttribute("aria-label", logoToggleButtonLabel); + logoElement.setAttribute( + "aria-expanded", + navMenuElementIsExpanded ? "true" : "false", + ); + homePageLinkElement.setAttribute("tabindex", "-1"); + } else { + logoElement.removeAttribute("role"); + logoElement.removeAttribute("tabindex"); + logoElement.removeAttribute("aria-expanded"); + logoElement.removeAttribute("aria-label"); + homePageLinkElement.removeAttribute("tabindex"); + } } function fixVoiceOverDetailsSummaryBug() { - document.querySelectorAll("details").forEach((details) => { - const summaryElement = details.querySelector("summary"); - summaryElement.setAttribute("role", "button"); - summaryElement.setAttribute("aria-expanded", details.open? "true": "false"); + document.querySelectorAll("details").forEach((details) => { + const summaryElement = details.querySelector("summary"); + summaryElement.setAttribute("role", "button"); + summaryElement.setAttribute( + "aria-expanded", + details.open ? "true" : "false", + ); - details.addEventListener("toggle", () => { - summaryElement.setAttribute("aria-expanded", details.open? "true": "false"); - }); + details.addEventListener("toggle", () => { + summaryElement.setAttribute( + "aria-expanded", + details.open ? "true" : "false", + ); }); + }); } // Show and hide the main menu on mobile devices. function toggleMainMenu(event) { - if (event.type === "keydown" && !(event.key === "Enter" || event.key === " ")) { - return; - } + if ( + event.type === "keydown" && + !(event.key === "Enter" || event.key === " ") + ) { + return; + } - if (event.currentTarget.getAttribute("role")) { - event.preventDefault(); - } + if (event.currentTarget.getAttribute("role")) { + event.preventDefault(); + } const menu = document.querySelector(".header nav ul"); const menuToggleButton = document.querySelector(".logo"); @@ -80,64 +95,66 @@ function toggleMainMenu(event) { // Handle click events for the main menu (