mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-01-28 18:06:02 +00:00
Compare commits
17 Commits
old-seleni
...
mute-pause
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eadc8e398 | ||
|
|
f9f7f103f0 | ||
|
|
5e5674f48d | ||
|
|
272e68ad2e | ||
|
|
01e06979d8 | ||
|
|
e45c77d51d | ||
|
|
bee1130c6e | ||
|
|
5f8448d0e2 | ||
|
|
9438d38dc6 | ||
|
|
d0c66758c2 | ||
|
|
9e8a9d5907 | ||
|
|
7449be39fb | ||
|
|
e9f3d0bce4 | ||
|
|
2abc8aa9b4 | ||
|
|
69b70a2a07 | ||
|
|
0c42bcb8d6 | ||
|
|
091c708a28 |
@@ -84,6 +84,7 @@ jobs:
|
||||
docker run test-changedetectionio bash -c 'python3 -m unittest changedetectionio.tests.unit.test_watch_model'
|
||||
docker run test-changedetectionio bash -c 'python3 -m unittest changedetectionio.tests.unit.test_jinja2_security'
|
||||
docker run test-changedetectionio bash -c 'python3 -m unittest changedetectionio.tests.unit.test_semver'
|
||||
docker run test-changedetectionio bash -c 'python3 -m unittest changedetectionio.tests.unit.test_html_to_text'
|
||||
|
||||
# Basic pytest tests with ancillary services
|
||||
basic-tests:
|
||||
|
||||
@@ -287,7 +287,9 @@ def main():
|
||||
return dict(right_sticky="v{}".format(datastore.data['version_tag']),
|
||||
new_version_available=app.config['NEW_VERSION_AVAILABLE'],
|
||||
has_password=datastore.data['settings']['application']['password'] != False,
|
||||
socket_io_enabled=datastore.data['settings']['application']['ui'].get('socket_io_enabled', True)
|
||||
socket_io_enabled=datastore.data['settings']['application']['ui'].get('socket_io_enabled', True),
|
||||
all_paused=datastore.data['settings']['application'].get('all_paused', False),
|
||||
all_muted=datastore.data['settings']['application'].get('all_muted', False)
|
||||
)
|
||||
|
||||
# Monitored websites will not receive a Referer header when a user clicks on an outgoing link.
|
||||
|
||||
@@ -2,7 +2,9 @@ from changedetectionio import queuedWatchMetaData
|
||||
from changedetectionio import worker_handler
|
||||
from flask_expects_json import expects_json
|
||||
from flask_restful import abort, Resource
|
||||
from loguru import logger
|
||||
|
||||
import threading
|
||||
from flask import request
|
||||
from . import auth
|
||||
|
||||
@@ -28,18 +30,36 @@ class Tag(Resource):
|
||||
abort(404, message=f'No tag exists with the UUID of {uuid}')
|
||||
|
||||
if request.args.get('recheck'):
|
||||
# Recheck all, including muted
|
||||
# Get most overdue first
|
||||
i=0
|
||||
# Recheck all watches with this tag, including muted
|
||||
# First collect watches to queue
|
||||
watches_to_queue = []
|
||||
for k in sorted(self.datastore.data['watching'].items(), key=lambda item: item[1].get('last_checked', 0)):
|
||||
watch_uuid = k[0]
|
||||
watch = k[1]
|
||||
if not watch['paused'] and tag['uuid'] not in watch['tags']:
|
||||
continue
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': watch_uuid}))
|
||||
i+=1
|
||||
if not watch['paused'] and tag['uuid'] in watch['tags']:
|
||||
watches_to_queue.append(watch_uuid)
|
||||
|
||||
return f"OK, {i} watches queued", 200
|
||||
# If less than 20 watches, queue synchronously for immediate feedback
|
||||
if len(watches_to_queue) < 20:
|
||||
for watch_uuid in watches_to_queue:
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': watch_uuid}))
|
||||
return {'status': f'OK, queued {len(watches_to_queue)} watches for rechecking'}, 200
|
||||
else:
|
||||
# 20+ watches - queue in background thread to avoid blocking API response
|
||||
def queue_watches_background():
|
||||
"""Background thread to queue watches - discarded after completion."""
|
||||
try:
|
||||
for watch_uuid in watches_to_queue:
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': watch_uuid}))
|
||||
logger.info(f"Background queueing complete for tag {tag['uuid']}: {len(watches_to_queue)} watches queued")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in background queueing for tag {tag['uuid']}: {e}")
|
||||
|
||||
# Start background thread and return immediately
|
||||
thread = threading.Thread(target=queue_watches_background, daemon=True, name=f"QueueTag-{tag['uuid'][:8]}")
|
||||
thread.start()
|
||||
|
||||
return {'status': f'OK, queueing {len(watches_to_queue)} watches in background'}, 202
|
||||
|
||||
if request.args.get('muted', '') == 'muted':
|
||||
self.datastore.data['settings']['application']['tags'][uuid]['notification_muted'] = True
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import threading
|
||||
|
||||
from changedetectionio.validate_url import is_safe_valid_url
|
||||
|
||||
@@ -438,7 +439,8 @@ class CreateWatch(Resource):
|
||||
|
||||
new_uuid = self.datastore.add_watch(url=url, extras=extras, tag=tags)
|
||||
if new_uuid:
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': new_uuid}))
|
||||
# Dont queue because the scheduler will check that it hasnt been checked before anyway
|
||||
# worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': new_uuid}))
|
||||
return {'uuid': new_uuid}, 201
|
||||
else:
|
||||
return "Invalid or unsupported URL", 400
|
||||
@@ -468,8 +470,29 @@ class CreateWatch(Resource):
|
||||
}
|
||||
|
||||
if request.args.get('recheck_all'):
|
||||
for uuid in self.datastore.data['watching'].keys():
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
return {'status': "OK"}, 200
|
||||
# Collect all watches to queue
|
||||
watches_to_queue = self.datastore.data['watching'].keys()
|
||||
|
||||
# If less than 20 watches, queue synchronously for immediate feedback
|
||||
if len(watches_to_queue) < 20:
|
||||
for uuid in watches_to_queue:
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
return {'status': f'OK, queued {len(watches_to_queue)} watches for rechecking'}, 200
|
||||
else:
|
||||
# 20+ watches - queue in background thread to avoid blocking API response
|
||||
def queue_all_watches_background():
|
||||
"""Background thread to queue all watches - discarded after completion."""
|
||||
try:
|
||||
for uuid in watches_to_queue:
|
||||
worker_handler.queue_item_async_safe(self.update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
logger.info(f"Background queueing complete: {len(watches_to_queue)} watches queued")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in background queueing all watches: {e}")
|
||||
|
||||
# Start background thread and return immediately
|
||||
thread = threading.Thread(target=queue_all_watches_background, daemon=True, name="QueueAllWatches-Background")
|
||||
thread.start()
|
||||
|
||||
return {'status': f'OK, queueing {len(watches_to_queue)} watches in background'}, 202
|
||||
|
||||
return list, 200
|
||||
@@ -8,6 +8,7 @@ from changedetectionio.flask_app import watch_check_update
|
||||
import asyncio
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from loguru import logger
|
||||
@@ -15,6 +16,9 @@ from loguru import logger
|
||||
# Async version of update_worker
|
||||
# Processes jobs from AsyncSignalPriorityQueue instead of threaded queue
|
||||
|
||||
IN_PYTEST = "pytest" in sys.modules or "PYTEST_CURRENT_TEST" in os.environ
|
||||
DEFER_SLEEP_TIME_ALREADY_QUEUED = 0.3 if IN_PYTEST else 10.0
|
||||
|
||||
async def async_update_worker(worker_id, q, notification_q, app, datastore, executor=None):
|
||||
"""
|
||||
Async worker function that processes watch check jobs from the queue.
|
||||
@@ -71,14 +75,13 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
|
||||
continue
|
||||
|
||||
uuid = queued_item_data.item.get('uuid')
|
||||
|
||||
# RACE CONDITION FIX: Check if this UUID is already being processed by another worker
|
||||
from changedetectionio import worker_handler
|
||||
from changedetectionio.queuedWatchMetaData import PrioritizedItem
|
||||
if worker_handler.is_watch_running_by_another_worker(uuid, worker_id):
|
||||
logger.trace(f"Worker {worker_id} detected UUID {uuid} already being processed by another worker - deferring")
|
||||
# Sleep to avoid tight loop and give the other worker time to finish
|
||||
await asyncio.sleep(10.0)
|
||||
await asyncio.sleep(DEFER_SLEEP_TIME_ALREADY_QUEUED)
|
||||
|
||||
# Re-queue with lower priority so it gets checked again after current processing finishes
|
||||
deferred_priority = max(1000, queued_item_data.priority * 10)
|
||||
@@ -129,8 +132,14 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
|
||||
# All fetchers are now async, so call directly
|
||||
await update_handler.call_browser()
|
||||
|
||||
# Run change detection (this is synchronous)
|
||||
changed_detected, update_obj, contents = update_handler.run_changedetection(watch=watch)
|
||||
# Run change detection in executor to avoid blocking event loop
|
||||
# This includes CPU-intensive operations like HTML parsing (lxml/inscriptis)
|
||||
# which can take 2-10ms and cause GIL contention across workers
|
||||
loop = asyncio.get_event_loop()
|
||||
changed_detected, update_obj, contents = await loop.run_in_executor(
|
||||
executor,
|
||||
lambda: update_handler.run_changedetection(watch=watch)
|
||||
)
|
||||
|
||||
except PermissionError as e:
|
||||
logger.critical(f"File permission error updating file, watch: {uuid}")
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
from flask import Blueprint, request, redirect, url_for, flash, render_template
|
||||
from loguru import logger
|
||||
|
||||
from changedetectionio.store import ChangeDetectionStore
|
||||
from changedetectionio.auth_decorator import login_optionally_required
|
||||
from changedetectionio import worker_handler
|
||||
from changedetectionio.blueprint.imports.importer import (
|
||||
import_url_list,
|
||||
import_distill_io_json,
|
||||
import_xlsx_wachete,
|
||||
import_xlsx_custom
|
||||
)
|
||||
|
||||
def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMetaData):
|
||||
import_blueprint = Blueprint('imports', __name__, template_folder="templates")
|
||||
@@ -17,15 +12,26 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
|
||||
def import_page():
|
||||
remaining_urls = []
|
||||
from changedetectionio import forms
|
||||
|
||||
#
|
||||
if request.method == 'POST':
|
||||
# from changedetectionio import worker_handler
|
||||
|
||||
from changedetectionio.blueprint.imports.importer import (
|
||||
import_url_list,
|
||||
import_distill_io_json,
|
||||
import_xlsx_wachete,
|
||||
import_xlsx_custom
|
||||
)
|
||||
|
||||
# URL List import
|
||||
if request.values.get('urls') and len(request.values.get('urls').strip()):
|
||||
# Import and push into the queue for immediate update check
|
||||
importer_handler = import_url_list()
|
||||
importer_handler.run(data=request.values.get('urls'), flash=flash, datastore=datastore, processor=request.values.get('processor', 'text_json_diff'))
|
||||
for uuid in importer_handler.new_uuids:
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
logger.debug(f"Imported {len(importer_handler.new_uuids)} new UUIDs")
|
||||
# Dont' add to queue because scheduler can see that they haven't been checked and will add them to the queue
|
||||
# for uuid in importer_handler.new_uuids:
|
||||
# worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
|
||||
if len(importer_handler.remaining_data) == 0:
|
||||
return redirect(url_for('watchlist.index'))
|
||||
@@ -37,8 +43,10 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
|
||||
# Import and push into the queue for immediate update check
|
||||
d_importer = import_distill_io_json()
|
||||
d_importer.run(data=request.values.get('distill-io'), flash=flash, datastore=datastore)
|
||||
for uuid in d_importer.new_uuids:
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
# Dont' add to queue because scheduler can see that they haven't been checked and will add them to the queue
|
||||
# for uuid in importer_handler.new_uuids:
|
||||
# worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
|
||||
|
||||
# XLSX importer
|
||||
if request.files and request.files.get('xlsx_file'):
|
||||
@@ -60,8 +68,10 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
|
||||
w_importer.import_profile = map
|
||||
w_importer.run(data=file, flash=flash, datastore=datastore)
|
||||
|
||||
for uuid in w_importer.new_uuids:
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
# Dont' add to queue because scheduler can see that they haven't been checked and will add them to the queue
|
||||
# for uuid in importer_handler.new_uuids:
|
||||
# worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
|
||||
|
||||
# Could be some remaining, or we could be on GET
|
||||
form = forms.importForm(formdata=request.form if request.method == 'POST' else None)
|
||||
|
||||
@@ -78,14 +78,20 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
# Handle dynamic worker count adjustment
|
||||
old_worker_count = datastore.data['settings']['requests'].get('workers', 1)
|
||||
new_worker_count = form.data['requests'].get('workers', 1)
|
||||
|
||||
|
||||
datastore.data['settings']['requests'].update(form.data['requests'])
|
||||
|
||||
|
||||
# Adjust worker count if it changed
|
||||
if new_worker_count != old_worker_count:
|
||||
from changedetectionio import worker_handler
|
||||
from changedetectionio.flask_app import update_q, notification_q, app, datastore as ds
|
||||
|
||||
|
||||
# Check CPU core availability and warn if worker count is high
|
||||
cpu_count = os.cpu_count()
|
||||
if cpu_count and new_worker_count >= (cpu_count * 0.9):
|
||||
flash(gettext("Warning: Worker count ({}) is close to or exceeds available CPU cores ({})").format(
|
||||
new_worker_count, cpu_count), 'warning')
|
||||
|
||||
result = worker_handler.adjust_async_worker_count(
|
||||
new_count=new_worker_count,
|
||||
update_q=update_q,
|
||||
@@ -93,7 +99,7 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
app=app,
|
||||
datastore=ds
|
||||
)
|
||||
|
||||
|
||||
if result['status'] == 'success':
|
||||
flash(gettext("Worker count adjusted: {}").format(result['message']), 'notice')
|
||||
elif result['status'] == 'not_supported':
|
||||
@@ -187,4 +193,32 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
logs=notification_debug_log if len(notification_debug_log) else ["Notification logs are empty - no notifications sent yet."])
|
||||
return output
|
||||
|
||||
@settings_blueprint.route("/toggle-all-paused", methods=['GET'])
|
||||
@login_optionally_required
|
||||
def toggle_all_paused():
|
||||
current_state = datastore.data['settings']['application'].get('all_paused', False)
|
||||
datastore.data['settings']['application']['all_paused'] = not current_state
|
||||
datastore.needs_write_urgent = True
|
||||
|
||||
if datastore.data['settings']['application']['all_paused']:
|
||||
flash(gettext("Automatic scheduling paused - checks will not be queued."), 'notice')
|
||||
else:
|
||||
flash(gettext("Automatic scheduling resumed - checks will be queued normally."), 'notice')
|
||||
|
||||
return redirect(url_for('watchlist.index'))
|
||||
|
||||
@settings_blueprint.route("/toggle-all-muted", methods=['GET'])
|
||||
@login_optionally_required
|
||||
def toggle_all_muted():
|
||||
current_state = datastore.data['settings']['application'].get('all_muted', False)
|
||||
datastore.data['settings']['application']['all_muted'] = not current_state
|
||||
datastore.needs_write_urgent = True
|
||||
|
||||
if datastore.data['settings']['application']['all_muted']:
|
||||
flash(gettext("All notifications muted."), 'notice')
|
||||
else:
|
||||
flash(gettext("All notifications unmuted."), 'notice')
|
||||
|
||||
return redirect(url_for('watchlist.index'))
|
||||
|
||||
return settings_blueprint
|
||||
@@ -25,6 +25,9 @@
|
||||
<li class="tab"><a href="#ui-options">{{ _('UI Options') }}</a></li>
|
||||
<li class="tab"><a href="#api">{{ _('API') }}</a></li>
|
||||
<li class="tab"><a href="#rss">{{ _('RSS') }}</a></li>
|
||||
<li class="pure-menu-item menu-collapsible {% if request.endpoint.startswith('backups.') %}active{% endif %}">
|
||||
<a href="{{ url_for('backups.index') }}" class="pure-menu-link">{{ _('Backups') }}</a>
|
||||
</li>
|
||||
<li class="tab"><a href="#timedate">{{ _('Time & Date') }}</a></li>
|
||||
<li class="tab"><a href="#proxies">{{ _('CAPTCHA & Proxies') }}</a></li>
|
||||
{% if plugin_tabs %}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import threading
|
||||
from flask import Blueprint, request, render_template, flash, url_for, redirect
|
||||
from flask_babel import gettext
|
||||
from loguru import logger
|
||||
|
||||
from changedetectionio.store import ChangeDetectionStore
|
||||
from changedetectionio.flask_app import login_optionally_required
|
||||
@@ -62,39 +64,73 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
@tags_blueprint.route("/delete/<string:uuid>", methods=['GET'])
|
||||
@login_optionally_required
|
||||
def delete(uuid):
|
||||
removed = 0
|
||||
# Delete the tag, and any tag reference
|
||||
# Delete the tag from settings immediately
|
||||
if datastore.data['settings']['application']['tags'].get(uuid):
|
||||
del datastore.data['settings']['application']['tags'][uuid]
|
||||
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if watch.get('tags') and uuid in watch['tags']:
|
||||
removed += 1
|
||||
watch['tags'].remove(uuid)
|
||||
# Remove tag from all watches in background thread to avoid blocking
|
||||
def remove_tag_background(tag_uuid):
|
||||
"""Background thread to remove tag from watches - discarded after completion."""
|
||||
removed_count = 0
|
||||
try:
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if watch.get('tags') and tag_uuid in watch['tags']:
|
||||
watch['tags'].remove(tag_uuid)
|
||||
removed_count += 1
|
||||
logger.info(f"Background: Tag {tag_uuid} removed from {removed_count} watches")
|
||||
except Exception as e:
|
||||
logger.error(f"Error removing tag from watches: {e}")
|
||||
|
||||
flash(gettext("Tag deleted and removed from {} watches").format(removed))
|
||||
# Start daemon thread
|
||||
threading.Thread(target=remove_tag_background, args=(uuid,), daemon=True).start()
|
||||
|
||||
flash(gettext("Tag deleted, removing from watches in background"))
|
||||
return redirect(url_for('tags.tags_overview_page'))
|
||||
|
||||
@tags_blueprint.route("/unlink/<string:uuid>", methods=['GET'])
|
||||
@login_optionally_required
|
||||
def unlink(uuid):
|
||||
unlinked = 0
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if watch.get('tags') and uuid in watch['tags']:
|
||||
unlinked += 1
|
||||
watch['tags'].remove(uuid)
|
||||
# Unlink tag from all watches in background thread to avoid blocking
|
||||
def unlink_tag_background(tag_uuid):
|
||||
"""Background thread to unlink tag from watches - discarded after completion."""
|
||||
unlinked_count = 0
|
||||
try:
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if watch.get('tags') and tag_uuid in watch['tags']:
|
||||
watch['tags'].remove(tag_uuid)
|
||||
unlinked_count += 1
|
||||
logger.info(f"Background: Tag {tag_uuid} unlinked from {unlinked_count} watches")
|
||||
except Exception as e:
|
||||
logger.error(f"Error unlinking tag from watches: {e}")
|
||||
|
||||
flash(gettext("Tag unlinked removed from {} watches").format(unlinked))
|
||||
# Start daemon thread
|
||||
threading.Thread(target=unlink_tag_background, args=(uuid,), daemon=True).start()
|
||||
|
||||
flash(gettext("Unlinking tag from watches in background"))
|
||||
return redirect(url_for('tags.tags_overview_page'))
|
||||
|
||||
@tags_blueprint.route("/delete_all", methods=['GET'])
|
||||
@login_optionally_required
|
||||
def delete_all():
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
watch['tags'] = []
|
||||
# Clear all tags from settings immediately
|
||||
datastore.data['settings']['application']['tags'] = {}
|
||||
|
||||
flash(gettext("All tags deleted"))
|
||||
# Clear tags from all watches in background thread to avoid blocking
|
||||
def clear_all_tags_background():
|
||||
"""Background thread to clear tags from all watches - discarded after completion."""
|
||||
cleared_count = 0
|
||||
try:
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
watch['tags'] = []
|
||||
cleared_count += 1
|
||||
logger.info(f"Background: Cleared tags from {cleared_count} watches")
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing tags from watches: {e}")
|
||||
|
||||
# Start daemon thread
|
||||
threading.Thread(target=clear_all_tags_background, daemon=True).start()
|
||||
|
||||
flash(gettext("All tags deleted, clearing from watches in background"))
|
||||
return redirect(url_for('tags.tags_overview_page'))
|
||||
|
||||
@tags_blueprint.route("/edit/<string:uuid>", methods=['GET'])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import time
|
||||
import threading
|
||||
from flask import Blueprint, request, redirect, url_for, flash, render_template, session
|
||||
from flask_babel import gettext
|
||||
from loguru import logger
|
||||
@@ -151,9 +152,24 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handle
|
||||
confirmtext = request.form.get('confirmtext')
|
||||
|
||||
if confirmtext == 'clear':
|
||||
for uuid in datastore.data['watching'].keys():
|
||||
datastore.clear_watch_history(uuid)
|
||||
flash(gettext("Cleared snapshot history for all watches"))
|
||||
# Run in background thread to avoid blocking
|
||||
def clear_history_background():
|
||||
# Capture UUIDs first to avoid race conditions
|
||||
watch_uuids = list(datastore.data['watching'].keys())
|
||||
logger.info(f"Background: Clearing history for {len(watch_uuids)} watches")
|
||||
|
||||
for uuid in watch_uuids:
|
||||
try:
|
||||
datastore.clear_watch_history(uuid)
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing history for watch {uuid}: {e}")
|
||||
|
||||
logger.info("Background: Completed clearing history")
|
||||
|
||||
# Start daemon thread
|
||||
threading.Thread(target=clear_history_background, daemon=True).start()
|
||||
|
||||
flash(gettext("History clearing started in background"))
|
||||
else:
|
||||
flash(gettext('Incorrect confirmation text.'), 'error')
|
||||
|
||||
@@ -169,18 +185,32 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handle
|
||||
# Save the current newest history as the most recently viewed
|
||||
with_errors = request.args.get('with_errors') == "1"
|
||||
tag_limit = request.args.get('tag')
|
||||
logger.debug(f"Limiting to tag {tag_limit}")
|
||||
now = int(time.time())
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if with_errors and not watch.get('last_error'):
|
||||
continue
|
||||
|
||||
if tag_limit and ( not watch.get('tags') or tag_limit not in watch['tags'] ):
|
||||
logger.debug(f"Skipping watch {watch_uuid}")
|
||||
continue
|
||||
# Mark watches as viewed in background thread to avoid blocking
|
||||
def mark_viewed_background():
|
||||
"""Background thread to mark watches as viewed - discarded after completion."""
|
||||
marked_count = 0
|
||||
try:
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if with_errors and not watch.get('last_error'):
|
||||
continue
|
||||
|
||||
datastore.set_last_viewed(watch_uuid, now)
|
||||
if tag_limit and (not watch.get('tags') or tag_limit not in watch['tags']):
|
||||
continue
|
||||
|
||||
datastore.set_last_viewed(watch_uuid, now)
|
||||
marked_count += 1
|
||||
|
||||
logger.info(f"Background marking complete: {marked_count} watches marked as viewed")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in background mark as viewed: {e}")
|
||||
|
||||
# Start background thread and return immediately
|
||||
thread = threading.Thread(target=mark_viewed_background, daemon=True)
|
||||
thread.start()
|
||||
|
||||
flash(gettext("Marking watches as viewed in background..."))
|
||||
return redirect(url_for('watchlist.index', tag=tag_limit))
|
||||
|
||||
@ui_blueprint.route("/delete", methods=['GET'])
|
||||
@@ -225,38 +255,49 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handle
|
||||
uuid = request.args.get('uuid')
|
||||
with_errors = request.args.get('with_errors') == "1"
|
||||
|
||||
i = 0
|
||||
|
||||
running_uuids = worker_handler.get_running_uuids()
|
||||
|
||||
if uuid:
|
||||
if uuid not in running_uuids:
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
i += 1
|
||||
|
||||
# Single watch - queue immediately
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
|
||||
flash(gettext("Queued 1 watch for rechecking."))
|
||||
else:
|
||||
# Recheck all, including muted
|
||||
# Get most overdue first
|
||||
# Multiple watches - first count how many need to be queued
|
||||
watches_to_queue = []
|
||||
for k in sorted(datastore.data['watching'].items(), key=lambda item: item[1].get('last_checked', 0)):
|
||||
watch_uuid = k[0]
|
||||
watch = k[1]
|
||||
if not watch['paused']:
|
||||
if watch_uuid not in running_uuids:
|
||||
if with_errors and not watch.get('last_error'):
|
||||
continue
|
||||
if not watch['paused'] and watch_uuid:
|
||||
if with_errors and not watch.get('last_error'):
|
||||
continue
|
||||
if tag != None and tag not in watch['tags']:
|
||||
continue
|
||||
watches_to_queue.append(watch_uuid)
|
||||
|
||||
if tag != None and tag not in watch['tags']:
|
||||
continue
|
||||
# If less than 20 watches, queue synchronously for immediate feedback
|
||||
if len(watches_to_queue) < 20:
|
||||
for watch_uuid in watches_to_queue:
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': watch_uuid}))
|
||||
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': watch_uuid}))
|
||||
i += 1
|
||||
if len(watches_to_queue) == 1:
|
||||
flash(gettext("Queued 1 watch for rechecking."))
|
||||
else:
|
||||
flash(gettext("Queued {} watches for rechecking.").format(len(watches_to_queue)))
|
||||
else:
|
||||
# 20+ watches - queue in background thread to avoid blocking HTTP response
|
||||
def queue_watches_background():
|
||||
"""Background thread to queue watches - discarded after completion."""
|
||||
try:
|
||||
for watch_uuid in watches_to_queue:
|
||||
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': watch_uuid}))
|
||||
logger.info(f"Background queueing complete: {len(watches_to_queue)} watches queued")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in background queueing: {e}")
|
||||
|
||||
if i == 1:
|
||||
flash(gettext("Queued 1 watch for rechecking."))
|
||||
if i > 1:
|
||||
flash(gettext("Queued {} watches for rechecking.").format(i))
|
||||
if i == 0:
|
||||
flash(gettext("No watches available to recheck."))
|
||||
# Start background thread and return immediately
|
||||
thread = threading.Thread(target=queue_watches_background, daemon=True, name="QueueWatches-Background")
|
||||
thread.start()
|
||||
|
||||
# Return immediately with approximate message
|
||||
flash(gettext("Queueing watches for rechecking in background..."))
|
||||
|
||||
return redirect(url_for('watchlist.index', **({'tag': tag} if tag else {})))
|
||||
|
||||
|
||||
@@ -52,7 +52,13 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
|
||||
redirect(url_for('ui_edit.edit_page', uuid=uuid))
|
||||
|
||||
# be sure we update with a copy instead of accidently editing the live object by reference
|
||||
default = deepcopy(datastore.data['watching'][uuid])
|
||||
default = None
|
||||
while not default:
|
||||
try:
|
||||
default = deepcopy(datastore.data['watching'][uuid])
|
||||
except RuntimeError as e:
|
||||
# Dictionary changed
|
||||
continue
|
||||
|
||||
# Defaults for proxy choice
|
||||
if datastore.proxy_list is not None: # When enabled
|
||||
|
||||
@@ -84,7 +84,7 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
|
||||
app_rss_token=datastore.data['settings']['application'].get('rss_access_token'),
|
||||
datastore=datastore,
|
||||
errored_count=errored_count,
|
||||
extra_classes='has-queue' if len(update_q.queue) else '',
|
||||
extra_classes='has-queue' if not update_q.empty() else '',
|
||||
form=form,
|
||||
generate_tag_colors=processors.generate_processor_badge_colors,
|
||||
guid=datastore.data['app_guid'],
|
||||
@@ -95,8 +95,8 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
|
||||
processor_badge_css=processors.get_processor_badge_css(),
|
||||
processor_badge_texts=processors.get_processor_badge_texts(),
|
||||
processor_descriptions=processors.get_processor_descriptions(),
|
||||
queue_size=len(update_q.queue),
|
||||
queued_uuids=[q_uuid.item['uuid'] for q_uuid in update_q.queue],
|
||||
queue_size=update_q.qsize(),
|
||||
queued_uuids=update_q.get_queued_uuids(),
|
||||
search_q=request.args.get('q', '').strip(),
|
||||
sort_attribute=request.args.get('sort') if request.args.get('sort') else request.cookies.get('sort'),
|
||||
sort_order=request.args.get('order') if request.args.get('order') else request.cookies.get('order'),
|
||||
|
||||
@@ -159,7 +159,7 @@ html[data-darkmode="true"] .watch-tag-list.tag-{{ class_name }} {
|
||||
<tbody>
|
||||
{%- if not watches|length -%}
|
||||
<tr>
|
||||
<td colspan="{{ cols_required }}" style="text-wrap: wrap;">{{ _('No website watches configured, please add a URL in the box above, or') }} <a href="{{ url_for('imports.import_page')}}" >{{ _('import a list') }}</a>.</td>
|
||||
<td colspan="{{ cols_required }}" style="text-wrap: wrap;">{{ _('No web page change detection watches configured, please add a URL in the box above, or') }} <a href="{{ url_for('imports.import_page')}}" >{{ _('import a list') }}</a>.</td>
|
||||
</tr>
|
||||
{%- endif -%}
|
||||
|
||||
|
||||
@@ -157,7 +157,17 @@ class fetcher(Fetcher):
|
||||
import io
|
||||
img = Image.open(io.BytesIO(screenshot_png))
|
||||
# Convert to RGB if needed (JPEG doesn't support transparency)
|
||||
if img.mode != 'RGB':
|
||||
# Always convert non-RGB modes to RGB to ensure JPEG compatibility
|
||||
if img.mode in ('RGBA', 'LA', 'P', 'PA'):
|
||||
# Handle transparency by compositing onto white background
|
||||
if img.mode == 'P':
|
||||
img = img.convert('RGBA')
|
||||
background = Image.new('RGB', img.size, (255, 255, 255))
|
||||
if img.mode in ('RGBA', 'LA', 'PA'):
|
||||
background.paste(img, mask=img.split()[-1]) # Use alpha channel as mask
|
||||
img = background
|
||||
elif img.mode != 'RGB':
|
||||
# For other modes, direct conversion
|
||||
img = img.convert('RGB')
|
||||
jpeg_buffer = io.BytesIO()
|
||||
img.save(jpeg_buffer, format='JPEG', quality=int(os.getenv("SCREENSHOT_QUALITY", 72)))
|
||||
|
||||
@@ -27,9 +27,7 @@ from flask import (
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from urllib.parse import urlparse
|
||||
from flask_compress import Compress as FlaskCompress
|
||||
from flask_login import current_user
|
||||
from flask_restful import abort, Api
|
||||
from flask_cors import CORS
|
||||
|
||||
@@ -46,6 +44,7 @@ from changedetectionio.api import Watch, WatchHistory, WatchSingleHistory, Watch
|
||||
from changedetectionio.api.Search import Search
|
||||
from .time_handler import is_within_schedule
|
||||
from changedetectionio.languages import get_available_languages, get_language_codes, get_flag_for_locale, get_timeago_locale
|
||||
IN_PYTEST = "pytest" in sys.modules or "PYTEST_CURRENT_TEST" in os.environ
|
||||
|
||||
datastore = None
|
||||
|
||||
@@ -56,7 +55,7 @@ extra_stylesheets = []
|
||||
# Use bulletproof janus-based queues for sync/async reliability
|
||||
update_q = RecheckPriorityQueue()
|
||||
notification_q = NotificationQueue()
|
||||
MAX_QUEUE_SIZE = 2000
|
||||
MAX_QUEUE_SIZE = 5000
|
||||
|
||||
app = Flask(__name__,
|
||||
static_url_path="",
|
||||
@@ -375,6 +374,9 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
global datastore, socketio_server
|
||||
datastore = datastore_o
|
||||
|
||||
# Set datastore reference in notification queue for all_muted checking
|
||||
notification_q.set_datastore(datastore)
|
||||
|
||||
# Import and create a wrapper for is_safe_url that has access to app
|
||||
from changedetectionio.is_safe_url import is_safe_url as _is_safe_url
|
||||
|
||||
@@ -979,6 +981,10 @@ def ticker_thread_check_time_launch_checks():
|
||||
logger.debug(f"System env MINIMUM_SECONDS_RECHECK_TIME {recheck_time_minimum_seconds}")
|
||||
|
||||
# Workers are now started during app initialization, not here
|
||||
WAIT_TIME_BETWEEN_LOOP = 1.0 if not IN_PYTEST else 0.01
|
||||
if IN_PYTEST:
|
||||
# The time between loops should be less than the first .sleep/wait in def wait_for_all_checks() of tests/util.py
|
||||
logger.warning(f"Looks like we're in PYTEST! Setting time between searching for items to add to the queue to {WAIT_TIME_BETWEEN_LOOP}s")
|
||||
|
||||
while not app.config.exit.is_set():
|
||||
|
||||
@@ -996,12 +1002,20 @@ def ticker_thread_check_time_launch_checks():
|
||||
|
||||
if health_result['status'] != 'healthy':
|
||||
logger.warning(f"Worker health check: {health_result['message']}")
|
||||
|
||||
|
||||
last_health_check = now
|
||||
|
||||
# Check if all checks are paused
|
||||
if datastore.data['settings']['application'].get('all_paused', False):
|
||||
app.config.exit.wait(1)
|
||||
continue
|
||||
|
||||
# Get a list of watches by UUID that are currently fetching data
|
||||
running_uuids = worker_handler.get_running_uuids()
|
||||
|
||||
# Build set of queued UUIDs once for O(1) lookup instead of O(n) per watch
|
||||
queued_uuids = {q_item.item['uuid'] for q_item in update_q.queue}
|
||||
|
||||
# Re #232 - Deepcopy the data incase it changes while we're iterating through it all
|
||||
watch_uuid_list = []
|
||||
while True:
|
||||
@@ -1018,16 +1032,17 @@ def ticker_thread_check_time_launch_checks():
|
||||
else:
|
||||
break
|
||||
|
||||
# Re #438 - Don't place more watches in the queue to be checked if the queue is already large
|
||||
while update_q.qsize() >= 2000:
|
||||
logger.warning(f"Recheck watches queue size limit reached ({MAX_QUEUE_SIZE}), skipping adding more items")
|
||||
app.config.exit.wait(10.0)
|
||||
|
||||
|
||||
recheck_time_system_seconds = int(datastore.threshold_seconds)
|
||||
|
||||
# Check for watches outside of the time threshold to put in the thread queue.
|
||||
for uuid in watch_uuid_list:
|
||||
for watch_index, uuid in enumerate(watch_uuid_list):
|
||||
# Re #438 - Check queue size every 100 watches for CPU efficiency (not every watch)
|
||||
if watch_index % 100 == 0:
|
||||
current_queue_size = update_q.qsize()
|
||||
if current_queue_size >= MAX_QUEUE_SIZE:
|
||||
logger.debug(f"Queue size limit reached ({current_queue_size}/{MAX_QUEUE_SIZE}), stopping scheduler this iteration.")
|
||||
break
|
||||
|
||||
now = time.time()
|
||||
watch = datastore.data['watching'].get(uuid)
|
||||
if not watch:
|
||||
@@ -1077,7 +1092,7 @@ def ticker_thread_check_time_launch_checks():
|
||||
seconds_since_last_recheck = now - watch['last_checked']
|
||||
|
||||
if seconds_since_last_recheck >= (threshold + watch.jitter_seconds) and seconds_since_last_recheck >= recheck_time_minimum_seconds:
|
||||
if not uuid in running_uuids and uuid not in [q_uuid.item['uuid'] for q_uuid in update_q.queue]:
|
||||
if not uuid in running_uuids and uuid not in queued_uuids:
|
||||
|
||||
# Proxies can be set to have a limit on seconds between which they can be called
|
||||
watch_proxy = datastore.get_preferred_proxy_for_watch(uuid=uuid)
|
||||
@@ -1120,4 +1135,4 @@ def ticker_thread_check_time_launch_checks():
|
||||
watch.jitter_seconds = 0
|
||||
|
||||
# Should be low so we can break this out in testing
|
||||
app.config.exit.wait(1)
|
||||
app.config.exit.wait(WAIT_TIME_BETWEEN_LOOP)
|
||||
|
||||
@@ -539,6 +539,18 @@ def cdata_in_document_to_text(html_content: str, render_anchor_tag_content=False
|
||||
|
||||
|
||||
def html_to_text(html_content: str, render_anchor_tag_content=False, is_rss=False, timeout=10) -> str:
|
||||
"""
|
||||
Convert HTML content to plain text using inscriptis.
|
||||
|
||||
Thread-Safety: This function uses inscriptis.get_text() which internally calls
|
||||
lxml.html.fromstring() with the default parser. Testing with 50 concurrent threads
|
||||
confirms this approach is thread-safe and produces deterministic output.
|
||||
|
||||
Alternative Approach Rejected: An explicit HTMLParser instance (thread-local or fresh)
|
||||
would also be thread-safe, but was found to break change detection logic in subtle ways
|
||||
(test_check_basic_change_detection_functionality). The default parser provides correct
|
||||
and reliable behavior.
|
||||
"""
|
||||
from inscriptis import get_text
|
||||
from inscriptis.model.config import ParserConfig
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ class model(dict):
|
||||
},
|
||||
'application': {
|
||||
# Custom notification content
|
||||
'all_paused': False,
|
||||
'all_muted': False,
|
||||
'api_access_token_enabled': True,
|
||||
'base_url' : None,
|
||||
'empty_pages_are_a_change': False,
|
||||
|
||||
@@ -130,7 +130,7 @@ def get_asset(asset_name, watch, datastore, request):
|
||||
except Exception as e:
|
||||
exception_container[0] = e
|
||||
|
||||
thread = threading.Thread(target=thread_target)
|
||||
thread = threading.Thread(target=thread_target, daemon=True, name="ImageDiff-Asset")
|
||||
thread.start()
|
||||
thread.join(timeout=60)
|
||||
|
||||
@@ -284,7 +284,7 @@ def _draw_bounding_box_if_configured(img_bytes, watch, datastore):
|
||||
except Exception as e:
|
||||
exception_container[0] = e
|
||||
|
||||
thread = threading.Thread(target=thread_target)
|
||||
thread = threading.Thread(target=thread_target, daemon=True, name="ImageDiff-BoundingBox")
|
||||
thread.start()
|
||||
thread.join(timeout=15)
|
||||
|
||||
@@ -393,7 +393,7 @@ def render(watch, datastore, request, url_for, render_template, flash, redirect)
|
||||
except Exception as e:
|
||||
exception_container[0] = e
|
||||
|
||||
thread = threading.Thread(target=thread_target)
|
||||
thread = threading.Thread(target=thread_target, daemon=True, name="ImageDiff-ChangePercentage")
|
||||
thread.start()
|
||||
thread.join(timeout=60)
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ class perform_site_check(difference_detection_processor):
|
||||
except Exception as e:
|
||||
exception_container[0] = e
|
||||
|
||||
thread = threading.Thread(target=thread_target)
|
||||
thread = threading.Thread(target=thread_target, daemon=True, name="ImageDiff-Processor")
|
||||
thread.start()
|
||||
thread.join(timeout=60)
|
||||
|
||||
|
||||
@@ -176,7 +176,16 @@ class RecheckPriorityQueue:
|
||||
def empty(self) -> bool:
|
||||
"""Check if queue is empty"""
|
||||
return self.qsize() == 0
|
||||
|
||||
|
||||
def get_queued_uuids(self) -> list:
|
||||
"""Get list of all queued UUIDs efficiently with single lock"""
|
||||
try:
|
||||
with self._lock:
|
||||
return [item.item['uuid'] for item in self._priority_items if hasattr(item, 'item') and 'uuid' in item.item]
|
||||
except Exception as e:
|
||||
logger.critical(f"CRITICAL: Failed to get queued UUIDs: {str(e)}")
|
||||
return []
|
||||
|
||||
def close(self):
|
||||
"""Close the janus queue"""
|
||||
try:
|
||||
@@ -352,21 +361,31 @@ class NotificationQueue:
|
||||
Simple wrapper around janus with bulletproof error handling.
|
||||
"""
|
||||
|
||||
def __init__(self, maxsize: int = 0):
|
||||
def __init__(self, maxsize: int = 0, datastore=None):
|
||||
try:
|
||||
self._janus_queue = janus.Queue(maxsize=maxsize)
|
||||
# BOTH interfaces required - see class docstring for why
|
||||
self.sync_q = self._janus_queue.sync_q # Flask routes, threads
|
||||
self.async_q = self._janus_queue.async_q # Async workers
|
||||
self.notification_event_signal = signal('notification_event')
|
||||
self.datastore = datastore # For checking all_muted setting
|
||||
logger.debug("NotificationQueue initialized successfully")
|
||||
except Exception as e:
|
||||
logger.critical(f"CRITICAL: Failed to initialize NotificationQueue: {str(e)}")
|
||||
raise
|
||||
|
||||
def set_datastore(self, datastore):
|
||||
"""Set datastore reference after initialization (for circular dependency handling)"""
|
||||
self.datastore = datastore
|
||||
|
||||
def put(self, item: Dict[str, Any], block: bool = True, timeout: Optional[float] = None):
|
||||
"""Thread-safe sync put with signal emission"""
|
||||
try:
|
||||
# Check if all notifications are muted
|
||||
if self.datastore and self.datastore.data['settings']['application'].get('all_muted', False):
|
||||
logger.debug(f"Notification blocked - all notifications are muted: {item.get('uuid', 'unknown')}")
|
||||
return False
|
||||
|
||||
self.sync_q.put(item, block=block, timeout=timeout)
|
||||
self._emit_notification_signal(item)
|
||||
logger.debug(f"Successfully queued notification: {item.get('uuid', 'unknown')}")
|
||||
@@ -378,6 +397,11 @@ class NotificationQueue:
|
||||
async def async_put(self, item: Dict[str, Any]):
|
||||
"""Pure async put with signal emission"""
|
||||
try:
|
||||
# Check if all notifications are muted
|
||||
if self.datastore and self.datastore.data['settings']['application'].get('all_muted', False):
|
||||
logger.debug(f"Notification blocked - all notifications are muted: {item.get('uuid', 'unknown')}")
|
||||
return False
|
||||
|
||||
await self.async_q.put(item)
|
||||
self._emit_notification_signal(item)
|
||||
logger.debug(f"Successfully async queued notification: {item.get('uuid', 'unknown')}")
|
||||
|
||||
@@ -150,11 +150,8 @@ def handle_watch_update(socketio, **kwargs):
|
||||
# Get list of watches that are currently running
|
||||
running_uuids = worker_handler.get_running_uuids()
|
||||
|
||||
# Get list of watches in the queue
|
||||
queue_list = []
|
||||
for q_item in update_q.queue:
|
||||
if hasattr(q_item, 'item') and 'uuid' in q_item.item:
|
||||
queue_list.append(q_item.item['uuid'])
|
||||
# Get list of watches in the queue (efficient single-lock method)
|
||||
queue_list = update_q.get_queued_uuids()
|
||||
|
||||
# Get the error texts from the watch
|
||||
error_texts = watch.compile_error_texts()
|
||||
@@ -254,21 +251,32 @@ def init_socketio(app, datastore):
|
||||
from changedetectionio import queuedWatchMetaData
|
||||
from changedetectionio import worker_handler
|
||||
from changedetectionio.flask_app import update_q, watch_check_update
|
||||
import threading
|
||||
|
||||
logger.trace(f"Got checkbox operations event: {data}")
|
||||
|
||||
datastore = socketio.datastore
|
||||
|
||||
_handle_operations(
|
||||
op=data.get('op'),
|
||||
uuids=data.get('uuids'),
|
||||
datastore=datastore,
|
||||
extra_data=data.get('extra_data'),
|
||||
worker_handler=worker_handler,
|
||||
update_q=update_q,
|
||||
queuedWatchMetaData=queuedWatchMetaData,
|
||||
watch_check_update=watch_check_update,
|
||||
emit_flash=False
|
||||
)
|
||||
def run_operation():
|
||||
"""Run the operation in a background thread to avoid blocking the socket.io event loop"""
|
||||
try:
|
||||
_handle_operations(
|
||||
op=data.get('op'),
|
||||
uuids=data.get('uuids'),
|
||||
datastore=datastore,
|
||||
extra_data=data.get('extra_data'),
|
||||
worker_handler=worker_handler,
|
||||
update_q=update_q,
|
||||
queuedWatchMetaData=queuedWatchMetaData,
|
||||
watch_check_update=watch_check_update,
|
||||
emit_flash=False
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in checkbox operation thread: {e}")
|
||||
|
||||
# Start operation in a disposable daemon thread
|
||||
thread = threading.Thread(target=run_operation, daemon=True, name=f"checkbox-op-{data.get('op')}")
|
||||
thread.start()
|
||||
|
||||
@socketio.on('connect')
|
||||
def handle_connect():
|
||||
|
||||
@@ -82,20 +82,14 @@ echo "RUNNING WITH BASE_URL SET"
|
||||
# Re #65 - Ability to include a link back to the installation, in the notification.
|
||||
export BASE_URL="https://really-unique-domain.io"
|
||||
|
||||
REMOVE_REQUESTS_OLD_SCREENSHOTS=false pytest -vv -s --maxfail=1 tests/test_notification.py
|
||||
|
||||
|
||||
# Re-run with HIDE_REFERER set - could affect login
|
||||
export HIDE_REFERER=True
|
||||
pytest -vv -s --maxfail=1 tests/test_access_control.py
|
||||
REMOVE_REQUESTS_OLD_SCREENSHOTS=false pytest -vv -s --maxfail=1 tests/test_notification.py tests/test_access_control.py
|
||||
|
||||
|
||||
# Re-run a few tests that will trigger brotli based storage
|
||||
export SNAPSHOT_BROTLI_COMPRESSION_THRESHOLD=5
|
||||
pytest -vv -s --maxfail=1 tests/test_access_control.py
|
||||
REMOVE_REQUESTS_OLD_SCREENSHOTS=false pytest tests/test_notification.py
|
||||
pytest -vv -s --maxfail=1 tests/test_backend.py
|
||||
pytest -vv -s --maxfail=1 tests/test_rss.py
|
||||
pytest -vv -s --maxfail=1 tests/test_unique_lines.py
|
||||
# And again with brotli+screenshot attachment
|
||||
SNAPSHOT_BROTLI_COMPRESSION_THRESHOLD=5 REMOVE_REQUESTS_OLD_SCREENSHOTS=false pytest -vv -s --maxfail=1 --dist=load tests/test_backend.py tests/test_rss.py tests/test_unique_lines.py tests/test_notification.py tests/test_access_control.py
|
||||
|
||||
# Try high concurrency
|
||||
FETCH_WORKERS=50 pytest tests/test_history_consistency.py -vv -l -s
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
background: var(--color-background-menu-link-hover);
|
||||
}
|
||||
}
|
||||
&#menu-pause, &#menu-mute {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
padding: 0.5rem 1em;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
#menu-mute, #menu-pause {
|
||||
padding-left: 0.3rem;
|
||||
padding-right: 0.3rem;
|
||||
img {
|
||||
height: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pure-menu-item {
|
||||
svg {
|
||||
|
||||
@@ -184,24 +184,24 @@ html[data-darkmode=true] {
|
||||
// Mobile adjustments
|
||||
@media only screen and (max-width: 768px) {
|
||||
.toast-container {
|
||||
left: 10px !important;
|
||||
right: 10px !important;
|
||||
top: 10px !important;
|
||||
transform: none !important;
|
||||
align-items: stretch;
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
top: 80px !important;
|
||||
transform: translateX(-50%) !important;
|
||||
align-items: center;
|
||||
|
||||
&.toast-bottom-right,
|
||||
&.toast-bottom-center,
|
||||
&.toast-bottom-left {
|
||||
top: auto !important;
|
||||
bottom: 10px !important;
|
||||
bottom: 80px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.toast {
|
||||
min-width: auto;
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
width: 80vw;
|
||||
transform: translateY(-100px);
|
||||
|
||||
&.toast-show {
|
||||
|
||||
@@ -125,6 +125,11 @@ $grid-gap: 0.5rem;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
// Empty state message - span full width on mobile
|
||||
> td[colspan] {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
> td.title-col {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
|
||||
@@ -33,6 +33,31 @@
|
||||
@use "parts/login_form";
|
||||
@use "parts/tabs";
|
||||
|
||||
// Smooth transitions for theme switching
|
||||
body,
|
||||
.pure-table,
|
||||
.pure-table thead,
|
||||
.pure-table td,
|
||||
.pure-table th,
|
||||
.pure-form input,
|
||||
.pure-form textarea,
|
||||
.pure-form select,
|
||||
.edit-form .inner,
|
||||
.pure-menu-horizontal,
|
||||
footer,
|
||||
.sticky-tab,
|
||||
#diff-jump,
|
||||
.button-tag,
|
||||
#new-watch-form,
|
||||
#new-watch-form input:not(.pure-button),
|
||||
code,
|
||||
.messages li,
|
||||
#checkbox-operations,
|
||||
.inline-warning,
|
||||
a,
|
||||
.watch-controls img {
|
||||
transition: color 0.4s ease, background-color 0.4s ease, background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--color-text);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,16 +8,15 @@
|
||||
<span class="pure-form-message-inline">
|
||||
Body for all notifications ‐ You can use <a target="newwindow" href="https://jinja.palletsprojects.com/en/3.0.x/templates/">Jinja2</a> templating in the notification title, body and URL, and tokens from below.
|
||||
</span><br>
|
||||
<div data-target="#notification-tokens-info{{ suffix }}" class="toggle-show pure-button button-tag button-xsmall">Show
|
||||
token/placeholders
|
||||
<div data-target="#notification-tokens-info{{ suffix }}" class="toggle-show pure-button button-tag button-xsmall">{{ _('Show token/placeholders') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-controls" style="display: none;" id="notification-tokens-info{{ suffix }}">
|
||||
<table class="pure-table" id="token-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Token</th>
|
||||
<th>Description</th>
|
||||
<th>{{ _('Token') }}</th>
|
||||
<th>{{ _('Description') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -126,7 +125,7 @@
|
||||
<p>
|
||||
<strong>Tip:</strong> Use <a target="newwindow" href="https://github.com/caronc/apprise">AppRise Notification URLs</a> for notification to just about any service! <i><a target="newwindow" href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes">Please read the notification services wiki here for important configuration notes</a></i>.<br>
|
||||
</p>
|
||||
<div data-target="#advanced-help-notifications" class="toggle-show pure-button button-tag button-xsmall">Show advanced help and tips</div>
|
||||
<div data-target="#advanced-help-notifications" class="toggle-show pure-button button-tag button-xsmall">{{ _('Show advanced help and tips') }}</div>
|
||||
<ul style="display: none" id="advanced-help-notifications">
|
||||
<li><code><a target="newwindow" href="https://github.com/caronc/apprise/wiki/Notify_discord">discord://</a></code> (or <code>https://discord.com/api/webhooks...</code>)) only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li>
|
||||
<li><code><a target="newwindow" href="https://github.com/caronc/apprise/wiki/Notify_telegram">tgram://</a></code> bots can't send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
||||
@@ -136,20 +135,20 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="notifications-wrapper">
|
||||
<a id="send-test-notification" class="pure-button button-secondary button-xsmall" >Send test notification</a> <div class="spinner" style="display: none;"></div>
|
||||
<a id="send-test-notification" class="pure-button button-secondary button-xsmall" >{{ _('Send test notification') }}</a> <div class="spinner" style="display: none;"></div>
|
||||
{% if emailprefix %}
|
||||
<a id="add-email-helper" class="pure-button button-secondary button-xsmall" >Add email <img style="height: 1em; display: inline-block" src="{{url_for('static_content', group='images', filename='email.svg')}}" alt="Add an email address"> </a>
|
||||
<a id="add-email-helper" class="pure-button button-secondary button-xsmall" >{{ _('Add email') }} <img style="height: 1em; display: inline-block" src="{{url_for('static_content', group='images', filename='email.svg')}}" alt="{{ _('Add an email address') }}"> </a>
|
||||
{% endif %}
|
||||
<a href="{{url_for('settings.notification_logs')}}" class="pure-button button-secondary button-xsmall" >Notification debug logs</a>
|
||||
<a href="{{url_for('settings.notification_logs')}}" class="pure-button button-secondary button-xsmall" >{{ _('Notification debug logs') }}</a>
|
||||
<br>
|
||||
<div id="notification-test-log" style="display: none;"><span class="pure-form-message-inline">Processing..</span></div>
|
||||
<div id="notification-test-log" style="display: none;"><span class="pure-form-message-inline">{{ _('Processing..') }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group grey-form-border">
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.notification_title, class="m-d notification-title", placeholder=settings_application['notification_title']) }}
|
||||
<span class="pure-form-message-inline">Title for all notifications</span>
|
||||
<span class="pure-form-message-inline">{{ _('Title for all notifications') }}</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.notification_body , rows=5, class="notification-body", placeholder=settings_application['notification_body']) }}
|
||||
@@ -174,7 +173,7 @@
|
||||
</div>
|
||||
<div class="">
|
||||
{{ render_field(form.notification_format , class="notification-format") }}
|
||||
<span class="pure-form-message-inline">Format for all notifications</span>
|
||||
<span class="pure-form-message-inline">{{ _('Format for all notifications') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -267,7 +267,7 @@
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pure-form-message-inline">
|
||||
<a href="https://changedetection.io/tutorials">{{ _('More help and examples about using the scheduler') }}</a>
|
||||
<a href="https://changedetection.io/tutorial/checking-web-pages-changes-according-schedule">{{ _('More help and examples about using the scheduler') }}</a>
|
||||
</span>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>Text to wait for before triggering a change/notification, all text and regex are tested <i>case-insensitive</i>.</li>
|
||||
<li>Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this watch</li>
|
||||
<li>Each line is processed separately (think of each line as "OR")</li>
|
||||
<li>Note: Wrap in forward slash / to use regex example: <code>/foo\d/</code></li>
|
||||
<li>{{ _('Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive.') }}</li>
|
||||
<li>{{ _('Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor') }}</li>
|
||||
<li>{{ _('Each line is processed separately (think of each line as "OR")') }}</li>
|
||||
<li>{{ _('Note: Wrap in forward slash / to use regex example:') }} <code>/foo\d/</code></li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
@@ -20,10 +20,10 @@
|
||||
") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>Matching text will be <strong>ignored</strong> in the text snapshot (you can still see it but it wont trigger a change)</li>
|
||||
<li>Each line processed separately, any line matching will be ignored (removed before creating the checksum)</li>
|
||||
<li>Regular Expression support, wrap the entire line in forward slash <code>/regex/</code></li>
|
||||
<li>Changing this will affect the comparison checksum which may trigger an alert</li>
|
||||
<li>{{ _('Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)') }}</li>
|
||||
<li>{{ _('Each line processed separately, any line matching will be ignored (removed before creating the checksum)') }}</li>
|
||||
<li>{{ _('Regular Expression support, wrap the entire line in forward slash') }} <code>/regex/</code></li>
|
||||
<li>{{ _('Changing this will affect the comparison checksum which may trigger an alert') }}</li>
|
||||
</ul>
|
||||
</span>
|
||||
<br><br>
|
||||
@@ -40,10 +40,10 @@ Not in stock
|
||||
Unavailable") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>Block change-detection while this text is on the page, all text and regex are tested <i>case-insensitive</i>, good for waiting for when a product is available again</li>
|
||||
<li>Block text is processed from the result-text that comes out of any CSS/JSON Filters for this watch</li>
|
||||
<li>All lines here must not exist (think of each line as "OR")</li>
|
||||
<li>Note: Wrap in forward slash / to use regex example: <code>/foo\d/</code></li>
|
||||
<li>{{ _('Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again') }}</li>
|
||||
<li>{{ _('Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor') }}</li>
|
||||
<li>{{ _('All lines here must not exist (think of each line as "OR")') }}</li>
|
||||
<li>{{ _('Note: Wrap in forward slash / to use regex example:') }} <code>/foo\d/</code></li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
@@ -55,17 +55,17 @@ Unavailable") }}
|
||||
keyword") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>Extracts text in the final output (line by line) after other filters using regular expressions or string match;
|
||||
<li>{{ _('Extracts text in the final output (line by line) after other filters using regular expressions or string match:') }}
|
||||
<ul>
|
||||
<li>Regular expression ‐ example <code>/reports.+?2022/i</code></li>
|
||||
<li>Don't forget to consider the white-space at the start of a line <code>/.+?reports.+?2022/i</code></li>
|
||||
<li>Use <code>//(?aiLmsux))</code> type flags (more <a href="https://docs.python.org/3/library/re.html#index-15">information here</a>)<br></li>
|
||||
<li>Keyword example ‐ example <code>Out of stock</code></li>
|
||||
<li>Use groups to extract just that text ‐ example <code>/reports.+?(\d+)/i</code> returns a list of years only</li>
|
||||
<li>Example - match lines containing a keyword <code>/.*icecream.*/</code></li>
|
||||
<li>{{ _('Regular expression - example') }} <code>/reports.+?2022/i</code></li>
|
||||
<li>{{ _('Don\'t forget to consider the white-space at the start of a line') }} <code>/.+?reports.+?2022/i</code></li>
|
||||
<li>{{ _('Use') }} <code>//(?aiLmsux))</code> {{ _('type flags (more') }} <a href="https://docs.python.org/3/library/re.html#index-15">{{ _('information here') }}</a>)<br></li>
|
||||
<li>{{ _('Keyword example - example') }} <code>Out of stock</code></li>
|
||||
<li>{{ _('Use groups to extract just that text - example') }} <code>/reports.+?(\d+)/i</code> {{ _('returns a list of years only') }}</li>
|
||||
<li>{{ _('Example - match lines containing a keyword') }} <code>/.*icecream.*/</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>One line per regular-expression/string match</li>
|
||||
<li>{{ _('One line per regular-expression/string match') }}</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
<li class="pure-menu-item menu-collapsible {% if request.endpoint.startswith('imports.') %}active{% endif %}">
|
||||
<a href="{{ url_for('imports.import_page') }}" class="pure-menu-link">{{ _('IMPORT') }}</a>
|
||||
</li>
|
||||
<li class="pure-menu-item menu-collapsible {% if request.endpoint.startswith('backups.') %}active{% endif %}">
|
||||
<a href="{{ url_for('backups.index') }}" class="pure-menu-link">{{ _('BACKUPS') }}</a>
|
||||
<li class="pure-menu-item" id="menu-pause">
|
||||
<a href="{{ url_for('settings.toggle_all_paused') }}" ><img src="{{url_for('static_content', group='images', filename='pause.svg')}}" alt="{% if all_paused %}Resume automatic scheduling{% else %}Pause auto-queue scheduling of watches{% endif %}" title="{% if all_paused %}Scheduling is paused - click to resume{% else %}Pause auto-queue scheduling of watches{% endif %}" class="icon icon-pause"{% if not all_paused %} style="opacity: 0.3"{% endif %}></a>
|
||||
</li>
|
||||
<li class="pure-menu-item " id="menu-mute">
|
||||
<a href="{{ url_for('settings.toggle_all_muted') }}" ><img src="{{url_for('static_content', group='images', filename='bell-off.svg')}}" alt="{% if all_muted %}Unmute notifications{% else %}Mute notifications{% endif %}" title="{% if all_muted %}Notifications are muted - click to unmute{% else %}Mute notifications{% endif %}" class="icon icon-mute"{% if not all_muted %} style="opacity: 0.3"{% endif %}></a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="pure-menu-item menu-collapsible">
|
||||
|
||||
@@ -159,6 +159,14 @@ def prepare_test_function(live_server, datastore_path):
|
||||
# CRITICAL: Get datastore and stop it from writing stale data
|
||||
datastore = live_server.app.config.get('DATASTORE')
|
||||
|
||||
# Clear the queue before starting the test to prevent state leakage
|
||||
from changedetectionio.flask_app import update_q
|
||||
while not update_q.empty():
|
||||
try:
|
||||
update_q.get_nowait()
|
||||
except:
|
||||
break
|
||||
|
||||
# Prevent background thread from writing during cleanup/reload
|
||||
datastore.needs_write = False
|
||||
datastore.needs_write_urgent = False
|
||||
@@ -183,8 +191,17 @@ def prepare_test_function(live_server, datastore_path):
|
||||
|
||||
yield
|
||||
|
||||
# Cleanup: Clear watches again after test
|
||||
# Cleanup: Clear watches and queue after test
|
||||
try:
|
||||
from changedetectionio.flask_app import update_q
|
||||
|
||||
# Clear the queue to prevent leakage to next test
|
||||
while not update_q.empty():
|
||||
try:
|
||||
update_q.get_nowait()
|
||||
except:
|
||||
break
|
||||
|
||||
datastore.data['watching'] = {}
|
||||
datastore.needs_write = True
|
||||
except Exception as e:
|
||||
@@ -238,17 +255,20 @@ def app(request, datastore_path):
|
||||
app.config['TEST_DATASTORE_PATH'] = datastore_path
|
||||
|
||||
def teardown():
|
||||
import threading
|
||||
import time
|
||||
|
||||
# Stop all threads and services
|
||||
datastore.stop_thread = True
|
||||
app.config.exit.set()
|
||||
|
||||
|
||||
# Shutdown workers gracefully before loguru cleanup
|
||||
try:
|
||||
from changedetectionio import worker_handler
|
||||
worker_handler.shutdown_workers()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# Stop socket server threads
|
||||
try:
|
||||
from changedetectionio.flask_app import socketio_server
|
||||
@@ -256,14 +276,35 @@ def app(request, datastore_path):
|
||||
socketio_server.shutdown()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Give threads a moment to finish their shutdown
|
||||
import time
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
# Get all active threads before cleanup
|
||||
main_thread = threading.main_thread()
|
||||
active_threads = [t for t in threading.enumerate() if t != main_thread and t.is_alive()]
|
||||
|
||||
# Wait for non-daemon threads to finish (with timeout)
|
||||
timeout = 2.0 # 2 seconds max wait
|
||||
start_time = time.time()
|
||||
|
||||
for thread in active_threads:
|
||||
if not thread.daemon:
|
||||
remaining_time = timeout - (time.time() - start_time)
|
||||
if remaining_time > 0:
|
||||
logger.debug(f"Waiting for non-daemon thread to finish: {thread.name}")
|
||||
thread.join(timeout=remaining_time)
|
||||
if thread.is_alive():
|
||||
logger.warning(f"Thread {thread.name} did not finish in time")
|
||||
|
||||
# Give daemon threads a moment to finish their current work
|
||||
time.sleep(0.2)
|
||||
|
||||
# Log any threads still running
|
||||
remaining_threads = [t for t in threading.enumerate() if t != main_thread and t.is_alive()]
|
||||
if remaining_threads:
|
||||
logger.debug(f"Threads still running after teardown: {[t.name for t in remaining_threads]}")
|
||||
|
||||
# Remove all loguru handlers to prevent "closed file" errors
|
||||
logger.remove()
|
||||
|
||||
|
||||
# Cleanup files
|
||||
cleanup(app_config['datastore_path'])
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ def test_check_access_control(app, client, live_server, measure_memory_usage, da
|
||||
|
||||
# Menu should be available now
|
||||
assert b"SETTINGS" in res.data
|
||||
assert b"BACKUP" in res.data
|
||||
assert b"IMPORT" in res.data
|
||||
assert b"LOG OUT" in res.data
|
||||
assert b"time_between_check-minutes" in res.data
|
||||
|
||||
@@ -109,18 +109,17 @@ def test_api_simple(client, live_server, measure_memory_usage, datastore_path):
|
||||
headers={'x-api-key': api_key}
|
||||
)
|
||||
assert len(res.json) == 0
|
||||
time.sleep(1)
|
||||
time.sleep(2)
|
||||
wait_for_all_checks(client)
|
||||
|
||||
set_modified_response(datastore_path=datastore_path)
|
||||
# Trigger recheck of all ?recheck_all=1
|
||||
client.get(
|
||||
res = client.get(
|
||||
url_for("createwatch", recheck_all='1'),
|
||||
headers={'x-api-key': api_key},
|
||||
)
|
||||
wait_for_all_checks(client)
|
||||
|
||||
time.sleep(1)
|
||||
time.sleep(2)
|
||||
# Did the recheck fire?
|
||||
res = client.get(
|
||||
url_for("createwatch"),
|
||||
|
||||
@@ -125,10 +125,12 @@ def test_api_tags_listing(client, live_server, measure_memory_usage, datastore_p
|
||||
url_for("tag", uuid=new_tag_uuid) + "?recheck=true",
|
||||
headers={'x-api-key': api_key}
|
||||
)
|
||||
wait_for_all_checks()
|
||||
assert res.status_code == 200
|
||||
assert b'OK, 1 watches' in res.data
|
||||
|
||||
assert res.status_code == 200
|
||||
assert b'OK, queued 1 watches for rechecking' in res.data
|
||||
|
||||
|
||||
wait_for_all_checks()
|
||||
after_check_time = live_server.app.config['DATASTORE'].data['watching'][watch_uuid].get('last_checked')
|
||||
|
||||
assert before_check_time != after_check_time
|
||||
|
||||
@@ -38,7 +38,6 @@ def test_check_basic_change_detection_functionality(client, live_server, measure
|
||||
# Default no password set, this stuff should be always available.
|
||||
|
||||
assert b"SETTINGS" in res.data
|
||||
assert b"BACKUP" in res.data
|
||||
assert b"IMPORT" in res.data
|
||||
|
||||
#####################
|
||||
|
||||
@@ -45,7 +45,7 @@ def run_filter_test(client, live_server, content_filter, app_notification_format
|
||||
uuid = client.application.config.get('DATASTORE').add_watch(url=test_url)
|
||||
res = client.get(url_for("watchlist.index"))
|
||||
|
||||
assert b'No website watches configured' not in res.data
|
||||
assert b'No web page change detection watches configured' not in res.data
|
||||
|
||||
|
||||
client.get(url_for("ui.form_watch_checknow"), follow_redirects=True)
|
||||
|
||||
205
changedetectionio/tests/unit/test_html_to_text.py
Normal file
205
changedetectionio/tests/unit/test_html_to_text.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
|
||||
"""Unit tests for html_tools.html_to_text function."""
|
||||
|
||||
import hashlib
|
||||
import threading
|
||||
import unittest
|
||||
from queue import Queue
|
||||
|
||||
from changedetectionio.html_tools import html_to_text
|
||||
|
||||
|
||||
class TestHtmlToText(unittest.TestCase):
|
||||
"""Test html_to_text function for correctness and thread-safety."""
|
||||
|
||||
def test_basic_text_extraction(self):
|
||||
"""Test basic HTML to text conversion."""
|
||||
html = '<html><body><h1>Title</h1><p>Paragraph text.</p></body></html>'
|
||||
text = html_to_text(html)
|
||||
|
||||
assert 'Title' in text
|
||||
assert 'Paragraph text.' in text
|
||||
assert '<' not in text # HTML tags should be stripped
|
||||
assert '>' not in text
|
||||
|
||||
def test_empty_html(self):
|
||||
"""Test handling of empty HTML."""
|
||||
html = '<html><body></body></html>'
|
||||
text = html_to_text(html)
|
||||
|
||||
# Should return empty or whitespace only
|
||||
assert text.strip() == ''
|
||||
|
||||
def test_nested_elements(self):
|
||||
"""Test extraction from nested HTML elements."""
|
||||
html = '''
|
||||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Header</h1>
|
||||
<div>
|
||||
<p>First paragraph</p>
|
||||
<p>Second paragraph</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
text = html_to_text(html)
|
||||
|
||||
assert 'Header' in text
|
||||
assert 'First paragraph' in text
|
||||
assert 'Second paragraph' in text
|
||||
|
||||
def test_anchor_tag_rendering(self):
|
||||
"""Test anchor tag rendering option."""
|
||||
html = '<html><body><a href="https://example.com">Link text</a></body></html>'
|
||||
|
||||
# Without rendering anchors
|
||||
text_without = html_to_text(html, render_anchor_tag_content=False)
|
||||
assert 'Link text' in text_without
|
||||
assert 'https://example.com' not in text_without
|
||||
|
||||
# With rendering anchors
|
||||
text_with = html_to_text(html, render_anchor_tag_content=True)
|
||||
assert 'Link text' in text_with
|
||||
assert 'https://example.com' in text_with or '[Link text]' in text_with
|
||||
|
||||
def test_rss_mode(self):
|
||||
"""Test RSS mode converts title tags to h1."""
|
||||
html = '<item><title>RSS Title</title><description>Content</description></item>'
|
||||
|
||||
# is_rss=True should convert <title> to <h1>
|
||||
text = html_to_text(html, is_rss=True)
|
||||
|
||||
assert 'RSS Title' in text
|
||||
assert 'Content' in text
|
||||
|
||||
def test_special_characters(self):
|
||||
"""Test handling of special characters and entities."""
|
||||
html = '<html><body><p>Test & <special> characters</p></body></html>'
|
||||
text = html_to_text(html)
|
||||
|
||||
# Entities should be decoded
|
||||
assert 'Test &' in text or 'Test &' in text
|
||||
assert 'special' in text
|
||||
|
||||
def test_whitespace_handling(self):
|
||||
"""Test that whitespace is properly handled."""
|
||||
html = '<html><body><p>Line 1</p><p>Line 2</p></body></html>'
|
||||
text = html_to_text(html)
|
||||
|
||||
# Should have some separation between lines
|
||||
assert 'Line 1' in text
|
||||
assert 'Line 2' in text
|
||||
assert text.count('\n') >= 1 # At least one newline
|
||||
|
||||
def test_deterministic_output(self):
|
||||
"""Test that the same HTML always produces the same text."""
|
||||
html = '<html><body><h1>Test</h1><p>Content here</p></body></html>'
|
||||
|
||||
# Extract text multiple times
|
||||
results = [html_to_text(html) for _ in range(10)]
|
||||
|
||||
# All results should be identical
|
||||
assert len(set(results)) == 1, "html_to_text should be deterministic"
|
||||
|
||||
def test_thread_safety_determinism(self):
|
||||
"""
|
||||
Test that html_to_text produces deterministic output under high concurrency.
|
||||
|
||||
This verifies that lxml's default parser (used by inscriptis.get_text)
|
||||
is thread-safe and produces consistent results when called from multiple
|
||||
threads simultaneously.
|
||||
"""
|
||||
html = '''
|
||||
<html>
|
||||
<head><title>Test Page</title></head>
|
||||
<body>
|
||||
<h1>Main Heading</h1>
|
||||
<div class="content">
|
||||
<p>First paragraph with <b>bold text</b>.</p>
|
||||
<p>Second paragraph with <i>italic text</i>.</p>
|
||||
<ul>
|
||||
<li>Item 1</li>
|
||||
<li>Item 2</li>
|
||||
<li>Item 3</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
results_queue = Queue()
|
||||
|
||||
def worker(worker_id, iterations=10):
|
||||
"""Worker that converts HTML to text multiple times."""
|
||||
for i in range(iterations):
|
||||
text = html_to_text(html)
|
||||
md5 = hashlib.md5(text.encode('utf-8')).hexdigest()
|
||||
results_queue.put((worker_id, i, md5))
|
||||
|
||||
# Launch many threads simultaneously
|
||||
num_threads = 50
|
||||
threads = []
|
||||
|
||||
for i in range(num_threads):
|
||||
t = threading.Thread(target=worker, args=(i,))
|
||||
threads.append(t)
|
||||
t.start()
|
||||
|
||||
# Wait for all threads to complete
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
# Collect all MD5 results
|
||||
md5_values = []
|
||||
while not results_queue.empty():
|
||||
_, _, md5 = results_queue.get()
|
||||
md5_values.append(md5)
|
||||
|
||||
# All MD5s should be identical
|
||||
unique_md5s = set(md5_values)
|
||||
|
||||
assert len(unique_md5s) == 1, (
|
||||
f"Thread-safety issue detected! Found {len(unique_md5s)} different MD5 values: {unique_md5s}. "
|
||||
"The thread-local parser fix may not be working correctly."
|
||||
)
|
||||
|
||||
print(f"✓ Thread-safety test passed: {len(md5_values)} conversions, all identical")
|
||||
|
||||
def test_thread_safety_basic(self):
|
||||
"""Verify basic thread safety - multiple threads can call html_to_text simultaneously."""
|
||||
results = []
|
||||
errors = []
|
||||
|
||||
def worker():
|
||||
"""Worker that converts HTML to text."""
|
||||
try:
|
||||
html = '<html><body><h1>Test</h1><p>Content</p></body></html>'
|
||||
text = html_to_text(html)
|
||||
results.append(text)
|
||||
except Exception as e:
|
||||
errors.append(e)
|
||||
|
||||
# Launch 10 threads simultaneously
|
||||
threads = [threading.Thread(target=worker) for _ in range(10)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
# Should have no errors
|
||||
assert len(errors) == 0, f"Thread-safety errors occurred: {errors}"
|
||||
|
||||
# All results should be identical
|
||||
assert len(set(results)) == 1, "All threads should produce identical output"
|
||||
|
||||
print(f"✓ Basic thread-safety test passed: {len(results)} threads, no errors")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Can run this file directly for quick testing
|
||||
unittest.main()
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -5,7 +5,7 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: changedetection.io\n"
|
||||
"Project-Id-Version: changedetection.io\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/dgtlmoon/changedetection.io\n"
|
||||
"POT-Creation-Date: 2026-01-02 16:07+0100\n"
|
||||
"PO-Revision-Date: 2026-01-12 16:33+0100\n"
|
||||
@@ -16,7 +16,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.10.3\n"
|
||||
"Generated-By: Babel 2.17.0\n"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:241
|
||||
#: changedetectionio/flask_app.py:213 changedetectionio/flask_app.py:225
|
||||
@@ -38,15 +38,11 @@ msgid "Incorrect password"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:63 changedetectionio/forms.py:243
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:64
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified when not using global settings."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified when not using global settings."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:164
|
||||
@@ -589,9 +585,7 @@ msgid "A backup is running!"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:13
|
||||
msgid ""
|
||||
"Here you can download and request a new backup, when a backup is "
|
||||
"completed you will see it listed below."
|
||||
msgid "Here you can download and request a new backup, when a backup is completed you will see it listed below."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:19
|
||||
@@ -611,9 +605,7 @@ msgid "Remove backups"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:45
|
||||
msgid ""
|
||||
"Importing 5,000 of the first URLs from your list, the rest can be "
|
||||
"imported again."
|
||||
msgid "Importing 5,000 of the first URLs from your list, the rest can be imported again."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:78
|
||||
@@ -648,9 +640,7 @@ msgstr ""
|
||||
#: changedetectionio/blueprint/imports/importer.py:214
|
||||
#: changedetectionio/blueprint/imports/importer.py:297
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Error processing row number {}, check all cell data types are correct, "
|
||||
"row was skipped."
|
||||
msgid "Error processing row number {}, check all cell data types are correct, row was skipped."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:218
|
||||
@@ -676,9 +666,7 @@ msgid ".XLSX & Wachete"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:20
|
||||
msgid ""
|
||||
"Enter one URL per line, and optionally add tags for each URL after a "
|
||||
"space, delineated by comma (,):"
|
||||
msgid "Enter one URL per line, and optionally add tags for each URL after a space, delineated by comma (,):"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:22
|
||||
@@ -690,9 +678,7 @@ msgid "URLs which do not pass validation will stay in the textarea."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:44
|
||||
msgid ""
|
||||
"Copy and Paste your Distill.io watch 'export' file, this should be a JSON"
|
||||
" file."
|
||||
msgid "Copy and Paste your Distill.io watch 'export' file, this should be a JSON file."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:45
|
||||
@@ -771,12 +757,17 @@ msgstr ""
|
||||
msgid "Password protection removed."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:98
|
||||
#: changedetectionio/blueprint/settings/__init__.py:92
|
||||
#, python-brace-format
|
||||
msgid "Warning: Worker count ({}) is close to or exceeds available CPU cores ({})"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:104
|
||||
#, python-brace-format
|
||||
msgid "Worker count adjusted: {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:100
|
||||
#: changedetectionio/blueprint/settings/__init__.py:106
|
||||
msgid "Dynamic worker adjustment not supported for sync workers"
|
||||
msgstr ""
|
||||
|
||||
@@ -985,9 +976,7 @@ msgid "Watch group / tag"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:21
|
||||
msgid ""
|
||||
"Groups allows you to manage filters and notifications for multiple "
|
||||
"watches under a single organisational tag."
|
||||
msgid "Groups allows you to manage filters and notifications for multiple watches under a single organisational tag."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:31
|
||||
@@ -1013,9 +1002,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:59
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete group "
|
||||
"<strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:60
|
||||
@@ -1035,10 +1022,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:67
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to unlink all watches from group "
|
||||
"<strong>%(title)s</strong>?</p><p>The tag will be kept but watches will "
|
||||
"be removed from it.</p>"
|
||||
msgid "<p>Are you sure you want to unlink all watches from group <strong>%(title)s</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:68
|
||||
@@ -1144,7 +1128,7 @@ msgstr ""
|
||||
msgid "Queued 1 watch for rechecking."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:257
|
||||
#: changedetectionio/blueprint/ui/__init__.py:283
|
||||
#, python-brace-format
|
||||
msgid "Queued {} watches for rechecking."
|
||||
msgstr ""
|
||||
@@ -1155,9 +1139,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:330
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Could not share, something went wrong while communicating with the share "
|
||||
"server - {}"
|
||||
msgid "Could not share, something went wrong while communicating with the share server - {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:93
|
||||
@@ -1170,9 +1152,7 @@ msgid "No history found for the specified link, bad link?"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:98
|
||||
msgid ""
|
||||
"Not enough history (2 snapshots required) to show difference page for "
|
||||
"this watch."
|
||||
msgid "Not enough history (2 snapshots required) to show difference page for this watch."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/edit.py:35
|
||||
@@ -1220,9 +1200,7 @@ msgid "Watch added."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:12
|
||||
msgid ""
|
||||
"This will remove version history (snapshots) for ALL watches, but keep "
|
||||
"your list of URLs!"
|
||||
msgid "This will remove version history (snapshots) for ALL watches, but keep your list of URLs!"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:13
|
||||
@@ -1404,9 +1382,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:144
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:80
|
||||
msgid ""
|
||||
"For now, Differences are performed on text, not graphically, only the "
|
||||
"latest screenshot is available."
|
||||
msgid "For now, Differences are performed on text, not graphically, only the latest screenshot is available."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:149
|
||||
@@ -1468,9 +1444,7 @@ msgid "Organisational tag/group name used in the main listing page"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:85
|
||||
msgid ""
|
||||
"Automatically uses the page title if found, you can also use your own "
|
||||
"title/description here"
|
||||
msgid "Automatically uses the page title if found, you can also use your own title/description here"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:95
|
||||
@@ -1478,10 +1452,7 @@ msgid "The interval/amount of time between each check."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:110
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, "
|
||||
"good for knowing when the page changed and your filter will not work "
|
||||
"anymore."
|
||||
msgid "Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
@@ -1493,9 +1464,7 @@ msgid "Basic"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
msgid ""
|
||||
"method (default) where your watched site doesn't need Javascript to "
|
||||
"render."
|
||||
msgid "method (default) where your watched site doesn't need Javascript to render."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
@@ -1507,9 +1476,7 @@ msgid "Chrome/Javascript"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
msgid ""
|
||||
"method requires a network connection to a running WebDriver+Chrome "
|
||||
"server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgid "method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:125
|
||||
@@ -1525,9 +1492,7 @@ msgid "Choose a proxy for this watch"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:143
|
||||
msgid ""
|
||||
"If you're having trouble waiting for the page to be fully rendered (text "
|
||||
"missing etc), try increasing the 'wait' time here."
|
||||
msgid "If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:145
|
||||
@@ -1548,9 +1513,7 @@ msgid "Show advanced options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:157
|
||||
msgid ""
|
||||
"Run this code before performing change detection, handy for filling in "
|
||||
"fields and other actions"
|
||||
msgid "Run this code before performing change detection, handy for filling in fields and other actions"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:158
|
||||
@@ -1607,9 +1570,7 @@ msgid "Visual Selector data is not ready, watch needs to be checked atleast once
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:253
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgid "Sorry, this functionality only works with fetchers that support interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:254
|
||||
@@ -1627,9 +1588,7 @@ msgid "to one that supports interactive Javascript."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:297
|
||||
msgid ""
|
||||
"Use the verify (✓) button to test if a condition passes against the "
|
||||
"current snapshot."
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:298
|
||||
@@ -1657,9 +1616,7 @@ msgid "Limit trigger/ignore/block/extract to;"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
msgid ""
|
||||
"Note: Depending on the length and similarity of the text on each line, "
|
||||
"the algorithm may consider an"
|
||||
msgid "Note: Depending on the length and similarity of the text on each line, the algorithm may consider an"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
@@ -1700,16 +1657,11 @@ msgid "Only trigger when unique lines appear"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:332
|
||||
msgid ""
|
||||
"Good for websites that just move the content around, and you want to know"
|
||||
" when NEW content is added, compares new lines against all history for "
|
||||
"this watch."
|
||||
msgid "Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
msgid ""
|
||||
"Helps reduce changes detected caused by sites shuffling lines around, "
|
||||
"combine with"
|
||||
msgid "Helps reduce changes detected caused by sites shuffling lines around, combine with"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
@@ -1739,9 +1691,7 @@ msgid "text"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid ""
|
||||
"elements that will be used for the change detection. It automatically "
|
||||
"fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgid "elements that will be used for the change detection. It automatically fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
@@ -1781,9 +1731,7 @@ msgid "Currently:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:423
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"Javascript and screenshots (such as playwright etc)."
|
||||
msgid "Sorry, this functionality only works with fetchers that support Javascript and screenshots (such as playwright etc)."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:424
|
||||
@@ -1839,9 +1787,7 @@ msgid "Are you sure you want to clear all history for:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:496
|
||||
msgid ""
|
||||
"This will remove all snapshots and previous versions. This action cannot "
|
||||
"be undone."
|
||||
msgid "This will remove all snapshots and previous versions. This action cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:497
|
||||
@@ -1865,9 +1811,7 @@ msgid "Current erroring screenshot from most recent request"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:91
|
||||
msgid ""
|
||||
"Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc "
|
||||
") that supports screenshots."
|
||||
msgid "Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc ) that supports screenshots."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:31
|
||||
@@ -1936,9 +1880,7 @@ msgid "Clear Histories"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:66
|
||||
msgid ""
|
||||
"<p>Are you sure you want to clear history for the selected "
|
||||
"items?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to clear history for the selected items?</p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:67
|
||||
@@ -1954,9 +1896,7 @@ msgid "Delete Watches?"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:72
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete the selected "
|
||||
"watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete the selected watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:78
|
||||
@@ -1989,7 +1929,7 @@ msgid "Changed"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
msgid "No website watches configured, please add a URL in the box above, or"
|
||||
msgid "No web page change detection watches configured, please add a URL in the box above, or"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
@@ -2213,7 +2153,7 @@ msgid "IMPORT"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:86
|
||||
msgid "BACKUPS"
|
||||
msgid "Backups"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:90
|
||||
@@ -2253,9 +2193,7 @@ msgid "Select Language"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid ""
|
||||
"Language support is in beta, please help us improve by opening a PR on "
|
||||
"GitHub with any updates."
|
||||
msgid "Language support is in beta, please help us improve by opening a PR on GitHub with any updates."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/login.html:10
|
||||
@@ -2266,3 +2204,150 @@ msgstr ""
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:18
|
||||
#: changedetectionio/widgets/ternary_boolean.py:72
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:19
|
||||
#: changedetectionio/widgets/ternary_boolean.py:73
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:20
|
||||
#: changedetectionio/widgets/ternary_boolean.py:74
|
||||
msgid "Main settings"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:11
|
||||
msgid "Show token/placeholders"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:18
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:19
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:128
|
||||
msgid "Show advanced help and tips"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:138
|
||||
msgid "Send test notification"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add email"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add an email address"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:142
|
||||
msgid "Notification debug logs"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:144
|
||||
msgid "Processing.."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:151
|
||||
msgid "Title for all notifications"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:176
|
||||
msgid "Format for all notifications"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:9
|
||||
msgid "Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:10
|
||||
msgid "Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:11
|
||||
msgid "Each line is processed separately (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:12
|
||||
msgid "Note: Wrap in forward slash / to use regex example:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:23
|
||||
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:24
|
||||
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:25
|
||||
msgid "Regular Expression support, wrap the entire line in forward slash"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:26
|
||||
msgid "Changing this will affect the comparison checksum which may trigger an alert"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:43
|
||||
msgid "Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:44
|
||||
msgid "Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:45
|
||||
msgid "All lines here must not exist (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:58
|
||||
msgid "Extracts text in the final output (line by line) after other filters using regular expressions or string match:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:60
|
||||
msgid "Regular expression - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:61
|
||||
msgid "Don't forget to consider the white-space at the start of a line"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "Use"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "type flags (more"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "information here"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:63
|
||||
msgid "Keyword example - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "Use groups to extract just that text - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "returns a list of years only"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:65
|
||||
msgid "Example - match lines containing a keyword"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:68
|
||||
msgid "One line per regular-expression/string match"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
@@ -16,7 +16,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.10.3\n"
|
||||
"Generated-By: Babel 2.17.0\n"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:241
|
||||
#: changedetectionio/flask_app.py:213 changedetectionio/flask_app.py:225
|
||||
@@ -38,15 +38,11 @@ msgid "Incorrect password"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:63 changedetectionio/forms.py:243
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:64
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified when not using global settings."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified when not using global settings."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:164
|
||||
@@ -589,9 +585,7 @@ msgid "A backup is running!"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:13
|
||||
msgid ""
|
||||
"Here you can download and request a new backup, when a backup is "
|
||||
"completed you will see it listed below."
|
||||
msgid "Here you can download and request a new backup, when a backup is completed you will see it listed below."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:19
|
||||
@@ -611,9 +605,7 @@ msgid "Remove backups"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:45
|
||||
msgid ""
|
||||
"Importing 5,000 of the first URLs from your list, the rest can be "
|
||||
"imported again."
|
||||
msgid "Importing 5,000 of the first URLs from your list, the rest can be imported again."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:78
|
||||
@@ -648,9 +640,7 @@ msgstr ""
|
||||
#: changedetectionio/blueprint/imports/importer.py:214
|
||||
#: changedetectionio/blueprint/imports/importer.py:297
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Error processing row number {}, check all cell data types are correct, "
|
||||
"row was skipped."
|
||||
msgid "Error processing row number {}, check all cell data types are correct, row was skipped."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:218
|
||||
@@ -676,9 +666,7 @@ msgid ".XLSX & Wachete"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:20
|
||||
msgid ""
|
||||
"Enter one URL per line, and optionally add tags for each URL after a "
|
||||
"space, delineated by comma (,):"
|
||||
msgid "Enter one URL per line, and optionally add tags for each URL after a space, delineated by comma (,):"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:22
|
||||
@@ -690,9 +678,7 @@ msgid "URLs which do not pass validation will stay in the textarea."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:44
|
||||
msgid ""
|
||||
"Copy and Paste your Distill.io watch 'export' file, this should be a JSON"
|
||||
" file."
|
||||
msgid "Copy and Paste your Distill.io watch 'export' file, this should be a JSON file."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:45
|
||||
@@ -771,12 +757,17 @@ msgstr ""
|
||||
msgid "Password protection removed."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:98
|
||||
#: changedetectionio/blueprint/settings/__init__.py:92
|
||||
#, python-brace-format
|
||||
msgid "Warning: Worker count ({}) is close to or exceeds available CPU cores ({})"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:104
|
||||
#, python-brace-format
|
||||
msgid "Worker count adjusted: {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:100
|
||||
#: changedetectionio/blueprint/settings/__init__.py:106
|
||||
msgid "Dynamic worker adjustment not supported for sync workers"
|
||||
msgstr ""
|
||||
|
||||
@@ -985,9 +976,7 @@ msgid "Watch group / tag"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:21
|
||||
msgid ""
|
||||
"Groups allows you to manage filters and notifications for multiple "
|
||||
"watches under a single organisational tag."
|
||||
msgid "Groups allows you to manage filters and notifications for multiple watches under a single organisational tag."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:31
|
||||
@@ -1013,9 +1002,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:59
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete group "
|
||||
"<strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:60
|
||||
@@ -1035,10 +1022,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:67
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to unlink all watches from group "
|
||||
"<strong>%(title)s</strong>?</p><p>The tag will be kept but watches will "
|
||||
"be removed from it.</p>"
|
||||
msgid "<p>Are you sure you want to unlink all watches from group <strong>%(title)s</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:68
|
||||
@@ -1144,7 +1128,7 @@ msgstr ""
|
||||
msgid "Queued 1 watch for rechecking."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:257
|
||||
#: changedetectionio/blueprint/ui/__init__.py:283
|
||||
#, python-brace-format
|
||||
msgid "Queued {} watches for rechecking."
|
||||
msgstr ""
|
||||
@@ -1155,9 +1139,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:330
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Could not share, something went wrong while communicating with the share "
|
||||
"server - {}"
|
||||
msgid "Could not share, something went wrong while communicating with the share server - {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:93
|
||||
@@ -1170,9 +1152,7 @@ msgid "No history found for the specified link, bad link?"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:98
|
||||
msgid ""
|
||||
"Not enough history (2 snapshots required) to show difference page for "
|
||||
"this watch."
|
||||
msgid "Not enough history (2 snapshots required) to show difference page for this watch."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/edit.py:35
|
||||
@@ -1220,9 +1200,7 @@ msgid "Watch added."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:12
|
||||
msgid ""
|
||||
"This will remove version history (snapshots) for ALL watches, but keep "
|
||||
"your list of URLs!"
|
||||
msgid "This will remove version history (snapshots) for ALL watches, but keep your list of URLs!"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:13
|
||||
@@ -1404,9 +1382,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:144
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:80
|
||||
msgid ""
|
||||
"For now, Differences are performed on text, not graphically, only the "
|
||||
"latest screenshot is available."
|
||||
msgid "For now, Differences are performed on text, not graphically, only the latest screenshot is available."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:149
|
||||
@@ -1468,9 +1444,7 @@ msgid "Organisational tag/group name used in the main listing page"
|
||||
msgstr "organizational tag/group name used in the main listing page"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:85
|
||||
msgid ""
|
||||
"Automatically uses the page title if found, you can also use your own "
|
||||
"title/description here"
|
||||
msgid "Automatically uses the page title if found, you can also use your own title/description here"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:95
|
||||
@@ -1478,10 +1452,7 @@ msgid "The interval/amount of time between each check."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:110
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, "
|
||||
"good for knowing when the page changed and your filter will not work "
|
||||
"anymore."
|
||||
msgid "Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
@@ -1493,9 +1464,7 @@ msgid "Basic"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
msgid ""
|
||||
"method (default) where your watched site doesn't need Javascript to "
|
||||
"render."
|
||||
msgid "method (default) where your watched site doesn't need Javascript to render."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
@@ -1507,9 +1476,7 @@ msgid "Chrome/Javascript"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
msgid ""
|
||||
"method requires a network connection to a running WebDriver+Chrome "
|
||||
"server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgid "method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:125
|
||||
@@ -1525,9 +1492,7 @@ msgid "Choose a proxy for this watch"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:143
|
||||
msgid ""
|
||||
"If you're having trouble waiting for the page to be fully rendered (text "
|
||||
"missing etc), try increasing the 'wait' time here."
|
||||
msgid "If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:145
|
||||
@@ -1548,9 +1513,7 @@ msgid "Show advanced options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:157
|
||||
msgid ""
|
||||
"Run this code before performing change detection, handy for filling in "
|
||||
"fields and other actions"
|
||||
msgid "Run this code before performing change detection, handy for filling in fields and other actions"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:158
|
||||
@@ -1607,9 +1570,7 @@ msgid "Visual Selector data is not ready, watch needs to be checked atleast once
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:253
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgid "Sorry, this functionality only works with fetchers that support interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:254
|
||||
@@ -1627,9 +1588,7 @@ msgid "to one that supports interactive Javascript."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:297
|
||||
msgid ""
|
||||
"Use the verify (✓) button to test if a condition passes against the "
|
||||
"current snapshot."
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:298
|
||||
@@ -1657,9 +1616,7 @@ msgid "Limit trigger/ignore/block/extract to;"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
msgid ""
|
||||
"Note: Depending on the length and similarity of the text on each line, "
|
||||
"the algorithm may consider an"
|
||||
msgid "Note: Depending on the length and similarity of the text on each line, the algorithm may consider an"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
@@ -1700,16 +1657,11 @@ msgid "Only trigger when unique lines appear"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:332
|
||||
msgid ""
|
||||
"Good for websites that just move the content around, and you want to know"
|
||||
" when NEW content is added, compares new lines against all history for "
|
||||
"this watch."
|
||||
msgid "Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
msgid ""
|
||||
"Helps reduce changes detected caused by sites shuffling lines around, "
|
||||
"combine with"
|
||||
msgid "Helps reduce changes detected caused by sites shuffling lines around, combine with"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
@@ -1739,9 +1691,7 @@ msgid "text"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid ""
|
||||
"elements that will be used for the change detection. It automatically "
|
||||
"fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgid "elements that will be used for the change detection. It automatically fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
@@ -1781,9 +1731,7 @@ msgid "Currently:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:423
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"Javascript and screenshots (such as playwright etc)."
|
||||
msgid "Sorry, this functionality only works with fetchers that support Javascript and screenshots (such as playwright etc)."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:424
|
||||
@@ -1839,9 +1787,7 @@ msgid "Are you sure you want to clear all history for:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:496
|
||||
msgid ""
|
||||
"This will remove all snapshots and previous versions. This action cannot "
|
||||
"be undone."
|
||||
msgid "This will remove all snapshots and previous versions. This action cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:497
|
||||
@@ -1865,9 +1811,7 @@ msgid "Current erroring screenshot from most recent request"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:91
|
||||
msgid ""
|
||||
"Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc "
|
||||
") that supports screenshots."
|
||||
msgid "Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc ) that supports screenshots."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:31
|
||||
@@ -1936,9 +1880,7 @@ msgid "Clear Histories"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:66
|
||||
msgid ""
|
||||
"<p>Are you sure you want to clear history for the selected "
|
||||
"items?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to clear history for the selected items?</p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:67
|
||||
@@ -1954,9 +1896,7 @@ msgid "Delete Watches?"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:72
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete the selected "
|
||||
"watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete the selected watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:78
|
||||
@@ -1989,7 +1929,7 @@ msgid "Changed"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
msgid "No website watches configured, please add a URL in the box above, or"
|
||||
msgid "No web page change detection watches configured, please add a URL in the box above, or"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
@@ -2213,7 +2153,7 @@ msgid "IMPORT"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:86
|
||||
msgid "BACKUPS"
|
||||
msgid "Backups"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:90
|
||||
@@ -2253,9 +2193,7 @@ msgid "Select Language"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid ""
|
||||
"Language support is in beta, please help us improve by opening a PR on "
|
||||
"GitHub with any updates."
|
||||
msgid "Language support is in beta, please help us improve by opening a PR on GitHub with any updates."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/login.html:10
|
||||
@@ -2266,3 +2204,150 @@ msgstr ""
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:18
|
||||
#: changedetectionio/widgets/ternary_boolean.py:72
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:19
|
||||
#: changedetectionio/widgets/ternary_boolean.py:73
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:20
|
||||
#: changedetectionio/widgets/ternary_boolean.py:74
|
||||
msgid "Main settings"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:11
|
||||
msgid "Show token/placeholders"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:18
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:19
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:128
|
||||
msgid "Show advanced help and tips"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:138
|
||||
msgid "Send test notification"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add email"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add an email address"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:142
|
||||
msgid "Notification debug logs"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:144
|
||||
msgid "Processing.."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:151
|
||||
msgid "Title for all notifications"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:176
|
||||
msgid "Format for all notifications"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:9
|
||||
msgid "Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:10
|
||||
msgid "Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:11
|
||||
msgid "Each line is processed separately (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:12
|
||||
msgid "Note: Wrap in forward slash / to use regex example:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:23
|
||||
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:24
|
||||
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:25
|
||||
msgid "Regular Expression support, wrap the entire line in forward slash"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:26
|
||||
msgid "Changing this will affect the comparison checksum which may trigger an alert"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:43
|
||||
msgid "Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:44
|
||||
msgid "Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:45
|
||||
msgid "All lines here must not exist (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:58
|
||||
msgid "Extracts text in the final output (line by line) after other filters using regular expressions or string match:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:60
|
||||
msgid "Regular expression - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:61
|
||||
msgid "Don't forget to consider the white-space at the start of a line"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "Use"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "type flags (more"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "information here"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:63
|
||||
msgid "Keyword example - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "Use groups to extract just that text - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "returns a list of years only"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:65
|
||||
msgid "Example - match lines containing a keyword"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:68
|
||||
msgid "One line per regular-expression/string match"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2026-01-03 14:31+0100\n"
|
||||
"POT-Creation-Date: 2026-01-02 16:07+0100\n"
|
||||
"PO-Revision-Date: 2026-01-02 11:40+0100\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: ko\n"
|
||||
@@ -19,35 +19,31 @@ msgstr ""
|
||||
"Generated-By: Babel 2.17.0\n"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:241
|
||||
#: changedetectionio/flask_app.py:214 changedetectionio/flask_app.py:226
|
||||
#: changedetectionio/flask_app.py:247
|
||||
#: changedetectionio/flask_app.py:213 changedetectionio/flask_app.py:225
|
||||
#: changedetectionio/flask_app.py:246
|
||||
#: changedetectionio/realtime/socket_server.py:171
|
||||
msgid "Not yet"
|
||||
msgstr "아직 아님"
|
||||
|
||||
#: changedetectionio/flask_app.py:534
|
||||
msgid "Already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/flask_app.py:536
|
||||
#: changedetectionio/flask_app.py:468
|
||||
msgid "You must be logged in, please log in."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/flask_app.py:551
|
||||
#: changedetectionio/flask_app.py:495
|
||||
msgid "Already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/flask_app.py:522
|
||||
#, fuzzy
|
||||
msgid "Incorrect password"
|
||||
msgstr "비밀번호"
|
||||
|
||||
#: changedetectionio/forms.py:63 changedetectionio/forms.py:243
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:64
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified when not using global settings."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified when not using global settings."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py:164
|
||||
@@ -380,9 +376,8 @@ msgid "Muted"
|
||||
msgstr "무음"
|
||||
|
||||
#: changedetectionio/forms.py:832
|
||||
#, fuzzy
|
||||
msgid "On"
|
||||
msgstr "없음"
|
||||
msgstr "켜짐"
|
||||
|
||||
#: changedetectionio/forms.py:833
|
||||
msgid "Attach screenshot to notification (where possible)"
|
||||
@@ -617,8 +612,6 @@ msgid "Backups were deleted."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:6
|
||||
#: changedetectionio/templates/base.html:282
|
||||
#: changedetectionio/templates/base.html:290
|
||||
msgid "Backups"
|
||||
msgstr "백업"
|
||||
|
||||
@@ -627,9 +620,7 @@ msgid "A backup is running!"
|
||||
msgstr "백업이 실행 중입니다!"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:13
|
||||
msgid ""
|
||||
"Here you can download and request a new backup, when a backup is "
|
||||
"completed you will see it listed below."
|
||||
msgid "Here you can download and request a new backup, when a backup is completed you will see it listed below."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:19
|
||||
@@ -649,9 +640,7 @@ msgid "Remove backups"
|
||||
msgstr "백업 삭제"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:45
|
||||
msgid ""
|
||||
"Importing 5,000 of the first URLs from your list, the rest can be "
|
||||
"imported again."
|
||||
msgid "Importing 5,000 of the first URLs from your list, the rest can be imported again."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:78
|
||||
@@ -686,9 +675,7 @@ msgstr ""
|
||||
#: changedetectionio/blueprint/imports/importer.py:214
|
||||
#: changedetectionio/blueprint/imports/importer.py:297
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Error processing row number {}, check all cell data types are correct, "
|
||||
"row was skipped."
|
||||
msgid "Error processing row number {}, check all cell data types are correct, row was skipped."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:218
|
||||
@@ -714,9 +701,7 @@ msgid ".XLSX & Wachete"
|
||||
msgstr ".XLSX 및 와체테"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:20
|
||||
msgid ""
|
||||
"Enter one URL per line, and optionally add tags for each URL after a "
|
||||
"space, delineated by comma (,):"
|
||||
msgid "Enter one URL per line, and optionally add tags for each URL after a space, delineated by comma (,):"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:22
|
||||
@@ -728,9 +713,7 @@ msgid "URLs which do not pass validation will stay in the textarea."
|
||||
msgstr "유효성 검사를 통과하지 못한 URL은 텍스트 영역에 유지됩니다."
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:44
|
||||
msgid ""
|
||||
"Copy and Paste your Distill.io watch 'export' file, this should be a JSON"
|
||||
" file."
|
||||
msgid "Copy and Paste your Distill.io watch 'export' file, this should be a JSON file."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:45
|
||||
@@ -809,12 +792,17 @@ msgstr "수입"
|
||||
msgid "Password protection removed."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:98
|
||||
#: changedetectionio/blueprint/settings/__init__.py:92
|
||||
#, python-brace-format
|
||||
msgid "Warning: Worker count ({}) is close to or exceeds available CPU cores ({})"
|
||||
msgstr "경고: 워커 수({})가 사용 가능한 CPU 코어 수({})에 근접하거나 초과합니다"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:104
|
||||
#, python-brace-format
|
||||
msgid "Worker count adjusted: {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:100
|
||||
#: changedetectionio/blueprint/settings/__init__.py:106
|
||||
msgid "Dynamic worker adjustment not supported for sync workers"
|
||||
msgstr ""
|
||||
|
||||
@@ -1026,9 +1014,7 @@ msgid "Watch group / tag"
|
||||
msgstr "감시 그룹/태그"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:21
|
||||
msgid ""
|
||||
"Groups allows you to manage filters and notifications for multiple "
|
||||
"watches under a single organisational tag."
|
||||
msgid "Groups allows you to manage filters and notifications for multiple watches under a single organisational tag."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:31
|
||||
@@ -1054,9 +1040,7 @@ msgstr "그룹을 삭제하시겠습니까?"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:59
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete group "
|
||||
"<strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:60
|
||||
@@ -1076,10 +1060,7 @@ msgstr "그룹을 연결 해제하시겠습니까?"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:67
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to unlink all watches from group "
|
||||
"<strong>%(title)s</strong>?</p><p>The tag will be kept but watches will "
|
||||
"be removed from it.</p>"
|
||||
msgid "<p>Are you sure you want to unlink all watches from group <strong>%(title)s</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:68
|
||||
@@ -1189,10 +1170,10 @@ msgstr ""
|
||||
msgid "Queued 1 watch for rechecking."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:257
|
||||
#: changedetectionio/blueprint/ui/__init__.py:283
|
||||
#, python-brace-format
|
||||
msgid "Queued {} watches for rechecking."
|
||||
msgstr ""
|
||||
msgstr "{}개의 감시 항목을 재확인 대기열에 추가했습니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:259
|
||||
msgid "No watches available to recheck."
|
||||
@@ -1200,9 +1181,7 @@ msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:330
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Could not share, something went wrong while communicating with the share "
|
||||
"server - {}"
|
||||
msgid "Could not share, something went wrong while communicating with the share server - {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:93
|
||||
@@ -1215,9 +1194,7 @@ msgid "No history found for the specified link, bad link?"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:98
|
||||
msgid ""
|
||||
"Not enough history (2 snapshots required) to show difference page for "
|
||||
"this watch."
|
||||
msgid "Not enough history (2 snapshots required) to show difference page for this watch."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/edit.py:35
|
||||
@@ -1266,9 +1243,7 @@ msgid "Watch added."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:12
|
||||
msgid ""
|
||||
"This will remove version history (snapshots) for ALL watches, but keep "
|
||||
"your list of URLs!"
|
||||
msgid "This will remove version history (snapshots) for ALL watches, but keep your list of URLs!"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:13
|
||||
@@ -1304,8 +1279,7 @@ msgid "Clear History!"
|
||||
msgstr "기록 지우기"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:39
|
||||
#: changedetectionio/templates/base.html:379
|
||||
#: changedetectionio/templates/base.html:399
|
||||
#: changedetectionio/templates/base.html:274
|
||||
msgid "Cancel"
|
||||
msgstr "취소"
|
||||
|
||||
@@ -1451,9 +1425,7 @@ msgstr "공유하거나 무시 목록에 추가할 텍스트를 강조 표시합
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:144
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:80
|
||||
msgid ""
|
||||
"For now, Differences are performed on text, not graphically, only the "
|
||||
"latest screenshot is available."
|
||||
msgid "For now, Differences are performed on text, not graphically, only the latest screenshot is available."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:149
|
||||
@@ -1515,9 +1487,7 @@ msgid "Organisational tag/group name used in the main listing page"
|
||||
msgstr "기본 목록 페이지에 사용되는 조직 태그/그룹 이름"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:85
|
||||
msgid ""
|
||||
"Automatically uses the page title if found, you can also use your own "
|
||||
"title/description here"
|
||||
msgid "Automatically uses the page title if found, you can also use your own title/description here"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:95
|
||||
@@ -1525,10 +1495,7 @@ msgid "The interval/amount of time between each check."
|
||||
msgstr "각 확인 사이의 간격/시간입니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:110
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, "
|
||||
"good for knowing when the page changed and your filter will not work "
|
||||
"anymore."
|
||||
msgid "Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
@@ -1540,9 +1507,7 @@ msgid "Basic"
|
||||
msgstr "기초적인"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
msgid ""
|
||||
"method (default) where your watched site doesn't need Javascript to "
|
||||
"render."
|
||||
msgid "method (default) where your watched site doesn't need Javascript to render."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
@@ -1554,9 +1519,7 @@ msgid "Chrome/Javascript"
|
||||
msgstr "크롬/자바스크립트"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
msgid ""
|
||||
"method requires a network connection to a running WebDriver+Chrome "
|
||||
"server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgid "method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:125
|
||||
@@ -1572,9 +1535,7 @@ msgid "Choose a proxy for this watch"
|
||||
msgstr "이 시계에 대한 RSS 피드"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:143
|
||||
msgid ""
|
||||
"If you're having trouble waiting for the page to be fully rendered (text "
|
||||
"missing etc), try increasing the 'wait' time here."
|
||||
msgid "If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:145
|
||||
@@ -1595,9 +1556,7 @@ msgid "Show advanced options"
|
||||
msgstr "고급 옵션 표시"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:157
|
||||
msgid ""
|
||||
"Run this code before performing change detection, handy for filling in "
|
||||
"fields and other actions"
|
||||
msgid "Run this code before performing change detection, handy for filling in fields and other actions"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:158
|
||||
@@ -1654,9 +1613,7 @@ msgid "Visual Selector data is not ready, watch needs to be checked atleast once
|
||||
msgstr "시각적 선택기 데이터가 준비되지 않았습니다. 시계를 한 번 이상 확인해야 합니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:253
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgid "Sorry, this functionality only works with fetchers that support interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:254
|
||||
@@ -1674,9 +1631,7 @@ msgid "to one that supports interactive Javascript."
|
||||
msgstr "대화형 Javascript를 지원하는 것입니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:297
|
||||
msgid ""
|
||||
"Use the verify (✓) button to test if a condition passes against the "
|
||||
"current snapshot."
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:298
|
||||
@@ -1704,9 +1659,7 @@ msgid "Limit trigger/ignore/block/extract to;"
|
||||
msgstr "트리거/무시/차단/추출을 다음으로 제한합니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
msgid ""
|
||||
"Note: Depending on the length and similarity of the text on each line, "
|
||||
"the algorithm may consider an"
|
||||
msgid "Note: Depending on the length and similarity of the text on each line, the algorithm may consider an"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
@@ -1747,16 +1700,11 @@ msgid "Only trigger when unique lines appear"
|
||||
msgstr "고유한 줄이 나타날 때만 트리거됩니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:332
|
||||
msgid ""
|
||||
"Good for websites that just move the content around, and you want to know"
|
||||
" when NEW content is added, compares new lines against all history for "
|
||||
"this watch."
|
||||
msgid "Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
msgid ""
|
||||
"Helps reduce changes detected caused by sites shuffling lines around, "
|
||||
"combine with"
|
||||
msgid "Helps reduce changes detected caused by sites shuffling lines around, combine with"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
@@ -1786,9 +1734,7 @@ msgid "text"
|
||||
msgstr "텍스트"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid ""
|
||||
"elements that will be used for the change detection. It automatically "
|
||||
"fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgid "elements that will be used for the change detection. It automatically fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
@@ -1828,9 +1774,7 @@ msgid "Currently:"
|
||||
msgstr "현재:"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:423
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"Javascript and screenshots (such as playwright etc)."
|
||||
msgid "Sorry, this functionality only works with fetchers that support Javascript and screenshots (such as playwright etc)."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:424
|
||||
@@ -1886,9 +1830,7 @@ msgid "Are you sure you want to clear all history for:"
|
||||
msgstr "정말로 다음의 기록을 모두 지우시겠습니까?"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:496
|
||||
msgid ""
|
||||
"This will remove all snapshots and previous versions. This action cannot "
|
||||
"be undone."
|
||||
msgid "This will remove all snapshots and previous versions. This action cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:497
|
||||
@@ -1912,9 +1854,7 @@ msgid "Current erroring screenshot from most recent request"
|
||||
msgstr "가장 최근 요청의 현재 오류 스크린샷"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:91
|
||||
msgid ""
|
||||
"Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc "
|
||||
") that supports screenshots."
|
||||
msgid "Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc ) that supports screenshots."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:31
|
||||
@@ -1983,9 +1923,7 @@ msgid "Clear Histories"
|
||||
msgstr "기록 지우기"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:66
|
||||
msgid ""
|
||||
"<p>Are you sure you want to clear history for the selected "
|
||||
"items?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to clear history for the selected items?</p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:67
|
||||
@@ -2001,9 +1939,7 @@ msgid "Delete Watches?"
|
||||
msgstr "시계를 삭제하시겠습니까?"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:72
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete the selected "
|
||||
"watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete the selected watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:78
|
||||
@@ -2036,7 +1972,7 @@ msgid "Changed"
|
||||
msgstr "변경됨"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
msgid "No website watches configured, please add a URL in the box above, or"
|
||||
msgid "No web page change detection watches configured, please add a URL in the box above, or"
|
||||
msgstr "구성된 웹사이트 시계가 없습니다. 위 상자에 URL을 추가하세요. 또는"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
@@ -2064,7 +2000,7 @@ msgid "No information"
|
||||
msgstr "정보 없음"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:234
|
||||
#: changedetectionio/templates/base.html:353
|
||||
#: changedetectionio/templates/base.html:248
|
||||
msgid "Checking now"
|
||||
msgstr "지금 확인 중"
|
||||
|
||||
@@ -2250,267 +2186,226 @@ msgstr "웹페이지 텍스트/HTML, JSON 및 PDF 변경"
|
||||
msgid "Detects all text changes where possible"
|
||||
msgstr "가능한 경우 모든 텍스트 변경 사항을 감지합니다."
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:25
|
||||
msgid "Entry"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:153
|
||||
#, fuzzy
|
||||
msgid "Actions"
|
||||
msgstr "정황"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:172
|
||||
msgid "Add a row/rule after"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:173
|
||||
msgid "Remove this row/rule"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:174
|
||||
msgid "Verify this rule against current snapshot"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid ""
|
||||
"Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but "
|
||||
"Chrome based fetching is not enabled."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid "Alternatively try our"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid ""
|
||||
"very affordable subscription based service which has all this setup for "
|
||||
"you"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
#, fuzzy
|
||||
msgid "You may need to"
|
||||
msgstr "당신은"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "Enable playwright environment variable"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "and uncomment the"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
#, fuzzy
|
||||
msgid "in the"
|
||||
msgstr "그만큼"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
#, fuzzy
|
||||
msgid "file"
|
||||
msgstr "제목"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:240
|
||||
msgid "Set a hourly/week day schedule"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:247
|
||||
#, fuzzy
|
||||
msgid "Schedule time limits"
|
||||
msgstr "재확인 시간(분)"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:248
|
||||
msgid "Business hours"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:249
|
||||
#, fuzzy
|
||||
msgid "Weekends"
|
||||
msgstr "주"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:250
|
||||
#, fuzzy
|
||||
msgid "Reset"
|
||||
msgstr "요구"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:259
|
||||
msgid ""
|
||||
"Warning, one or more of your 'days' has a duration that would extend into"
|
||||
" the next day."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:260
|
||||
msgid "This could have unintended consequences."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:270
|
||||
#, fuzzy
|
||||
msgid "More help and examples about using the scheduler"
|
||||
msgstr "여기에 더 많은 도움말과 예시가 있습니다."
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:275
|
||||
#, fuzzy
|
||||
msgid "Want to use a time schedule?"
|
||||
msgstr "시간 스케줄러 사용"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:275
|
||||
msgid "First confirm/save your Time Zone Settings"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:284
|
||||
msgid ""
|
||||
"Triggers a change if this text appears, AND something changed in the "
|
||||
"document."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:284
|
||||
#, fuzzy
|
||||
msgid "Triggered text"
|
||||
msgstr "오류 텍스트"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:285
|
||||
msgid "Ignored for calculating changes, but still shown."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:285
|
||||
#, fuzzy
|
||||
msgid "Ignored text"
|
||||
msgstr "오류 텍스트"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:286
|
||||
#, fuzzy
|
||||
msgid "No change-detection will occur because this text exists."
|
||||
msgstr "텍스트가 일치하는 동안 변경 감지 차단"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:286
|
||||
#, fuzzy
|
||||
msgid "Blocked text"
|
||||
msgstr "오류 텍스트"
|
||||
|
||||
#: changedetectionio/templates/base.html:78
|
||||
#: changedetectionio/templates/base.html:168
|
||||
#: changedetectionio/templates/base.html:77
|
||||
msgid "GROUPS"
|
||||
msgstr "여러 떼"
|
||||
|
||||
#: changedetectionio/templates/base.html:81
|
||||
#: changedetectionio/templates/base.html:169
|
||||
#: changedetectionio/templates/base.html:80
|
||||
msgid "SETTINGS"
|
||||
msgstr "설정"
|
||||
|
||||
#: changedetectionio/templates/base.html:84
|
||||
#: changedetectionio/templates/base.html:170
|
||||
#: changedetectionio/templates/base.html:83
|
||||
msgid "IMPORT"
|
||||
msgstr "가져오기"
|
||||
|
||||
#: changedetectionio/templates/base.html:87
|
||||
#: changedetectionio/templates/base.html:171
|
||||
msgid "BACKUPS"
|
||||
#: changedetectionio/templates/base.html:86
|
||||
msgid "Backups"
|
||||
msgstr "백업"
|
||||
|
||||
#: changedetectionio/templates/base.html:91
|
||||
#: changedetectionio/templates/base.html:173
|
||||
#: changedetectionio/templates/base.html:90
|
||||
msgid "EDIT"
|
||||
msgstr "편집하다"
|
||||
|
||||
#: changedetectionio/templates/base.html:101
|
||||
#: changedetectionio/templates/base.html:177
|
||||
#: changedetectionio/templates/base.html:100
|
||||
msgid "LOG OUT"
|
||||
msgstr "로그아웃"
|
||||
|
||||
#: changedetectionio/templates/base.html:108
|
||||
#: changedetectionio/templates/base.html:109
|
||||
msgid "Search, or Use Alt+S Key"
|
||||
msgstr "검색 또는 Alt+S 키 사용"
|
||||
|
||||
#: changedetectionio/templates/base.html:114
|
||||
#: changedetectionio/templates/base.html:116
|
||||
msgid "Toggle Light/Dark Mode"
|
||||
msgstr "밝은/어두운 모드 전환"
|
||||
|
||||
#: changedetectionio/templates/base.html:115
|
||||
#: changedetectionio/templates/base.html:117
|
||||
msgid "Toggle light/dark mode"
|
||||
msgstr "밝은/어두운 모드 전환"
|
||||
|
||||
#: changedetectionio/templates/base.html:125
|
||||
#: changedetectionio/templates/base.html:127
|
||||
msgid "Change Language"
|
||||
msgstr "언어 변경"
|
||||
|
||||
#: changedetectionio/templates/base.html:126
|
||||
#: changedetectionio/templates/base.html:128
|
||||
msgid "Change language"
|
||||
msgstr "언어 변경"
|
||||
|
||||
#: changedetectionio/templates/base.html:253
|
||||
#, fuzzy
|
||||
msgid "Watch List"
|
||||
msgstr "모니터 목록"
|
||||
|
||||
#: changedetectionio/templates/base.html:258
|
||||
#, fuzzy
|
||||
msgid "Watches"
|
||||
msgstr "모니터"
|
||||
|
||||
#: changedetectionio/templates/base.html:261
|
||||
msgid "Queue Status"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:270
|
||||
#, fuzzy
|
||||
msgid "Queue"
|
||||
msgstr "대기 중"
|
||||
|
||||
#: changedetectionio/templates/base.html:274
|
||||
#: changedetectionio/templates/base.html:279
|
||||
#, fuzzy
|
||||
msgid "Settings"
|
||||
msgstr "설정"
|
||||
|
||||
#: changedetectionio/templates/base.html:293
|
||||
msgid "Sitemap Crawler"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:318
|
||||
msgid "Sitemap"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:354
|
||||
#: changedetectionio/templates/base.html:249
|
||||
msgid "Real-time updates offline"
|
||||
msgstr "실시간 업데이트 오프라인"
|
||||
|
||||
#: changedetectionio/templates/base.html:364
|
||||
#: changedetectionio/templates/base.html:259
|
||||
msgid "Select Language"
|
||||
msgstr "언어 선택"
|
||||
|
||||
#: changedetectionio/templates/base.html:375
|
||||
msgid ""
|
||||
"Language support is in beta, please help us improve by opening a PR on "
|
||||
"GitHub with any updates."
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid "Language support is in beta, please help us improve by opening a PR on GitHub with any updates."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:387
|
||||
#: changedetectionio/templates/base.html:400
|
||||
#, fuzzy
|
||||
msgid "Search"
|
||||
msgstr "수색"
|
||||
|
||||
#: changedetectionio/templates/base.html:392
|
||||
msgid "URL or Title"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:392
|
||||
#, fuzzy
|
||||
msgid "in"
|
||||
msgstr "추가 정보"
|
||||
|
||||
#: changedetectionio/templates/base.html:393
|
||||
msgid "Enter search term..."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/login.html:11
|
||||
#: changedetectionio/templates/login.html:10
|
||||
msgid "Password"
|
||||
msgstr "비밀번호"
|
||||
|
||||
#: changedetectionio/templates/login.html:17
|
||||
#: changedetectionio/templates/login.html:16
|
||||
msgid "Login"
|
||||
msgstr "로그인"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:18
|
||||
#: changedetectionio/widgets/ternary_boolean.py:72
|
||||
msgid "Yes"
|
||||
msgstr "예"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:19
|
||||
#: changedetectionio/widgets/ternary_boolean.py:73
|
||||
msgid "No"
|
||||
msgstr "아니오"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:20
|
||||
#: changedetectionio/widgets/ternary_boolean.py:74
|
||||
msgid "Main settings"
|
||||
msgstr "기본 설정"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:11
|
||||
msgid "Show token/placeholders"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:18
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:19
|
||||
#, fuzzy
|
||||
msgid "Description"
|
||||
msgstr "덧셈"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:128
|
||||
#, fuzzy
|
||||
msgid "Show advanced help and tips"
|
||||
msgstr "고급 옵션 표시"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:138
|
||||
#, fuzzy
|
||||
msgid "Send test notification"
|
||||
msgstr "기본 알림 사용"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add email"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add an email address"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:142
|
||||
#, fuzzy
|
||||
msgid "Notification debug logs"
|
||||
msgstr "알림 디버그 로그"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:144
|
||||
#, fuzzy
|
||||
msgid "Processing.."
|
||||
msgstr "프로세서"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:151
|
||||
#, fuzzy
|
||||
msgid "Title for all notifications"
|
||||
msgstr "기본 알림 사용"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:176
|
||||
#, fuzzy
|
||||
msgid "Format for all notifications"
|
||||
msgstr "기본 알림 사용"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:9
|
||||
msgid "Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:10
|
||||
msgid "Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:11
|
||||
msgid "Each line is processed separately (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:12
|
||||
msgid "Note: Wrap in forward slash / to use regex example:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:23
|
||||
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:24
|
||||
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:25
|
||||
msgid "Regular Expression support, wrap the entire line in forward slash"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:26
|
||||
msgid "Changing this will affect the comparison checksum which may trigger an alert"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:43
|
||||
msgid "Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:44
|
||||
msgid "Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:45
|
||||
msgid "All lines here must not exist (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:58
|
||||
msgid "Extracts text in the final output (line by line) after other filters using regular expressions or string match:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:60
|
||||
msgid "Regular expression - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:61
|
||||
msgid "Don't forget to consider the white-space at the start of a line"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
#, fuzzy
|
||||
msgid "Use"
|
||||
msgstr "정지시키다"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "type flags (more"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
#, fuzzy
|
||||
msgid "information here"
|
||||
msgstr "정보 없음"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:63
|
||||
msgid "Keyword example - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "Use groups to extract just that text - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "returns a list of years only"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:65
|
||||
msgid "Example - match lines containing a keyword"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:68
|
||||
msgid "One line per regular-expression/string match"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Invalid time format. Use HH:MM."
|
||||
#~ msgstr "시간 형식이 잘못되었습니다. HH:MM을 사용하세요."
|
||||
|
||||
@@ -2892,9 +2787,7 @@ msgstr "로그인"
|
||||
#~ msgid "Visual / Image screenshot change detection"
|
||||
#~ msgstr "시각적/이미지 스크린샷 변경 감지"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Compares screenshots using fast OpenCV "
|
||||
#~ "algorithm, 10-100x faster than SSIM"
|
||||
#~ msgid "Compares screenshots using fast OpenCV algorithm, 10-100x faster than SSIM"
|
||||
#~ msgstr "SSIM보다 10~100배 빠른 빠른 OpenCV 알고리즘을 사용하여 스크린샷을 비교합니다."
|
||||
|
||||
#~ msgid "Visual"
|
||||
@@ -2915,3 +2808,120 @@ msgstr "로그인"
|
||||
#~ msgid "Detects all text changes where possible"
|
||||
#~ msgstr "가능한 경우 모든 텍스트 변경 사항을 감지합니다."
|
||||
|
||||
#~ msgid "Entry"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Actions"
|
||||
#~ msgstr "정황"
|
||||
|
||||
#~ msgid "Add a row/rule after"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Remove this row/rule"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Verify this rule against current snapshot"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Alternatively try our"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "very affordable subscription based service which has all this setup for you"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "You may need to"
|
||||
#~ msgstr "당신은"
|
||||
|
||||
#~ msgid "Enable playwright environment variable"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "and uncomment the"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "in the"
|
||||
#~ msgstr "그만큼"
|
||||
|
||||
#~ msgid "file"
|
||||
#~ msgstr "제목"
|
||||
|
||||
#~ msgid "Set a hourly/week day schedule"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Schedule time limits"
|
||||
#~ msgstr "재확인 시간(분)"
|
||||
|
||||
#~ msgid "Business hours"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Weekends"
|
||||
#~ msgstr "주"
|
||||
|
||||
#~ msgid "Reset"
|
||||
#~ msgstr "요구"
|
||||
|
||||
#~ msgid "Warning, one or more of your 'days' has a duration that would extend into the next day."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "This could have unintended consequences."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "More help and examples about using the scheduler"
|
||||
#~ msgstr "여기에 더 많은 도움말과 예시가 있습니다."
|
||||
|
||||
#~ msgid "Want to use a time schedule?"
|
||||
#~ msgstr "시간 스케줄러 사용"
|
||||
|
||||
#~ msgid "First confirm/save your Time Zone Settings"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Triggers a change if this text appears, AND something changed in the document."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Triggered text"
|
||||
#~ msgstr "오류 텍스트"
|
||||
|
||||
#~ msgid "Ignored for calculating changes, but still shown."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Ignored text"
|
||||
#~ msgstr "오류 텍스트"
|
||||
|
||||
#~ msgid "No change-detection will occur because this text exists."
|
||||
#~ msgstr "텍스트가 일치하는 동안 변경 감지 차단"
|
||||
|
||||
#~ msgid "Blocked text"
|
||||
#~ msgstr "오류 텍스트"
|
||||
|
||||
#~ msgid "Watch List"
|
||||
#~ msgstr "모니터 목록"
|
||||
|
||||
#~ msgid "Watches"
|
||||
#~ msgstr "모니터"
|
||||
|
||||
#~ msgid "Queue Status"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Queue"
|
||||
#~ msgstr "대기 중"
|
||||
|
||||
#~ msgid "Sitemap Crawler"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Sitemap"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Search"
|
||||
#~ msgstr "수색"
|
||||
|
||||
#~ msgid "URL or Title"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "in"
|
||||
#~ msgstr "추가 정보"
|
||||
|
||||
#~ msgid "Enter search term..."
|
||||
#~ msgstr ""
|
||||
|
||||
|
||||
@@ -770,12 +770,17 @@ msgstr ""
|
||||
msgid "Password protection removed."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:98
|
||||
#: changedetectionio/blueprint/settings/__init__.py:92
|
||||
#, python-brace-format
|
||||
msgid "Warning: Worker count ({}) is close to or exceeds available CPU cores ({})"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:104
|
||||
#, python-brace-format
|
||||
msgid "Worker count adjusted: {}"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:100
|
||||
#: changedetectionio/blueprint/settings/__init__.py:106
|
||||
msgid "Dynamic worker adjustment not supported for sync workers"
|
||||
msgstr ""
|
||||
|
||||
@@ -1143,7 +1148,7 @@ msgstr ""
|
||||
msgid "Queued 1 watch for rechecking."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:257
|
||||
#: changedetectionio/blueprint/ui/__init__.py:283
|
||||
#, python-brace-format
|
||||
msgid "Queued {} watches for rechecking."
|
||||
msgstr ""
|
||||
@@ -1988,7 +1993,7 @@ msgid "Changed"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
msgid "No website watches configured, please add a URL in the box above, or"
|
||||
msgid "No web page change detection watches configured, please add a URL in the box above, or"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
@@ -2211,10 +2216,6 @@ msgstr ""
|
||||
msgid "IMPORT"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:86
|
||||
msgid "BACKUPS"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/base.html:90
|
||||
msgid "EDIT"
|
||||
msgstr ""
|
||||
@@ -2265,3 +2266,150 @@ msgstr ""
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:18
|
||||
#: changedetectionio/widgets/ternary_boolean.py:72
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:19
|
||||
#: changedetectionio/widgets/ternary_boolean.py:73
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:20
|
||||
#: changedetectionio/widgets/ternary_boolean.py:74
|
||||
msgid "Main settings"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:11
|
||||
msgid "Show token/placeholders"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:18
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:19
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:128
|
||||
msgid "Show advanced help and tips"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:138
|
||||
msgid "Send test notification"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add email"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add an email address"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:142
|
||||
msgid "Notification debug logs"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:144
|
||||
msgid "Processing.."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:151
|
||||
msgid "Title for all notifications"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:176
|
||||
msgid "Format for all notifications"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:9
|
||||
msgid "Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:10
|
||||
msgid "Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:11
|
||||
msgid "Each line is processed separately (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:12
|
||||
msgid "Note: Wrap in forward slash / to use regex example:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:23
|
||||
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:24
|
||||
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:25
|
||||
msgid "Regular Expression support, wrap the entire line in forward slash"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:26
|
||||
msgid "Changing this will affect the comparison checksum which may trigger an alert"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:43
|
||||
msgid "Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:44
|
||||
msgid "Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:45
|
||||
msgid "All lines here must not exist (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:58
|
||||
msgid "Extracts text in the final output (line by line) after other filters using regular expressions or string match:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:60
|
||||
msgid "Regular expression - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:61
|
||||
msgid "Don't forget to consider the white-space at the start of a line"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "Use"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "type flags (more"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "information here"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:63
|
||||
msgid "Keyword example - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "Use groups to extract just that text - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "returns a list of years only"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:65
|
||||
msgid "Example - match lines containing a keyword"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:68
|
||||
msgid "One line per regular-expression/string match"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
@@ -7,46 +7,42 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2026-01-03 14:31+0100\n"
|
||||
"PO-Revision-Date: 2026-01-16 06:40+0800\n"
|
||||
"POT-Creation-Date: 2026-01-02 16:07+0100\n"
|
||||
"PO-Revision-Date: 2026-01-16 17:40+0800\n"
|
||||
"Last-Translator: 吾爱分享 <admin@wuaishare.cn>\n"
|
||||
"Language-Team: zh <LL@li.org>\n"
|
||||
"Language: zh\n"
|
||||
"Language-Team: zh <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.17.0\n"
|
||||
"X-Generator: Poedit 3.8\n"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:241
|
||||
#: changedetectionio/flask_app.py:214 changedetectionio/flask_app.py:226
|
||||
#: changedetectionio/flask_app.py:247 changedetectionio/realtime/socket_server.py:171
|
||||
#: changedetectionio/flask_app.py:213 changedetectionio/flask_app.py:225
|
||||
#: changedetectionio/flask_app.py:246
|
||||
#: changedetectionio/realtime/socket_server.py:171
|
||||
msgid "Not yet"
|
||||
msgstr "尚未"
|
||||
|
||||
#: changedetectionio/flask_app.py:534
|
||||
msgid "Already logged in"
|
||||
msgstr "已登录"
|
||||
|
||||
#: changedetectionio/flask_app.py:536
|
||||
#: changedetectionio/flask_app.py:468
|
||||
msgid "You must be logged in, please log in."
|
||||
msgstr "需要登录,请先登录。"
|
||||
|
||||
#: changedetectionio/flask_app.py:551
|
||||
#: changedetectionio/flask_app.py:495
|
||||
msgid "Already logged in"
|
||||
msgstr "已登录"
|
||||
|
||||
#: changedetectionio/flask_app.py:522
|
||||
msgid "Incorrect password"
|
||||
msgstr "密码错误"
|
||||
|
||||
#: changedetectionio/forms.py:63 changedetectionio/forms.py:243
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must be "
|
||||
"specified."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified."
|
||||
msgstr "必须指定至少一个时间间隔(周、天、小时、分钟或秒)。"
|
||||
|
||||
#: changedetectionio/forms.py:64
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must be "
|
||||
"specified when not using global settings."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified when not using global settings."
|
||||
msgstr "未使用全局设置时,必须指定至少一个时间间隔(周、天、小时、分钟或秒)。"
|
||||
|
||||
#: changedetectionio/forms.py:164
|
||||
@@ -218,7 +214,7 @@ msgstr "应为 1 秒或以上"
|
||||
|
||||
#: changedetectionio/forms.py:767
|
||||
msgid "URLs"
|
||||
msgstr "URL"
|
||||
msgstr "URL 列表"
|
||||
|
||||
#: changedetectionio/forms.py:768
|
||||
msgid "Upload .xlsx file"
|
||||
@@ -521,7 +517,7 @@ msgstr "移除密码"
|
||||
|
||||
#: changedetectionio/forms.py:1021
|
||||
msgid "Render anchor tag content"
|
||||
msgstr "渲染 a 标签内容"
|
||||
msgstr "渲染 <a> 标签内容"
|
||||
|
||||
#: changedetectionio/forms.py:1022
|
||||
msgid "Allow anonymous access to watch history page when password is enabled"
|
||||
@@ -581,8 +577,6 @@ msgid "Backups were deleted."
|
||||
msgstr "备份已删除。"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:6
|
||||
#: changedetectionio/templates/base.html:282
|
||||
#: changedetectionio/templates/base.html:290
|
||||
msgid "Backups"
|
||||
msgstr "备份"
|
||||
|
||||
@@ -591,9 +585,7 @@ msgid "A backup is running!"
|
||||
msgstr "备份正在运行!"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:13
|
||||
msgid ""
|
||||
"Here you can download and request a new backup, when a backup is completed you "
|
||||
"will see it listed below."
|
||||
msgid "Here you can download and request a new backup, when a backup is completed you will see it listed below."
|
||||
msgstr "在此可下载并请求新的备份,备份完成后会在下方列表中显示。"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:19
|
||||
@@ -613,8 +605,7 @@ msgid "Remove backups"
|
||||
msgstr "删除备份"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:45
|
||||
msgid ""
|
||||
"Importing 5,000 of the first URLs from your list, the rest can be imported again."
|
||||
msgid "Importing 5,000 of the first URLs from your list, the rest can be imported again."
|
||||
msgstr "仅导入列表前 5,000 个 URL,其余可稍后继续导入。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:78
|
||||
@@ -649,9 +640,7 @@ msgstr "处理第 {} 行时出错,URL 值不正确,已跳过该行。"
|
||||
#: changedetectionio/blueprint/imports/importer.py:214
|
||||
#: changedetectionio/blueprint/imports/importer.py:297
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Error processing row number {}, check all cell data types are correct, row was "
|
||||
"skipped."
|
||||
msgid "Error processing row number {}, check all cell data types are correct, row was skipped."
|
||||
msgstr "处理第 {} 行时出错,请检查单元格数据类型是否正确,已跳过该行。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:218
|
||||
@@ -677,9 +666,7 @@ msgid ".XLSX & Wachete"
|
||||
msgstr ".XLSX 与 Wachete"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:20
|
||||
msgid ""
|
||||
"Enter one URL per line, and optionally add tags for each URL after a space, "
|
||||
"delineated by comma (,):"
|
||||
msgid "Enter one URL per line, and optionally add tags for each URL after a space, delineated by comma (,):"
|
||||
msgstr "每行输入一个 URL,可在 URL 后用空格追加标签,标签以逗号 (,) 分隔:"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:22
|
||||
@@ -691,8 +678,7 @@ msgid "URLs which do not pass validation will stay in the textarea."
|
||||
msgstr "未通过验证的 URL 会保留在文本框中。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:44
|
||||
msgid ""
|
||||
"Copy and Paste your Distill.io watch 'export' file, this should be a JSON file."
|
||||
msgid "Copy and Paste your Distill.io watch 'export' file, this should be a JSON file."
|
||||
msgstr "复制并粘贴 Distill.io 监控的“导出”文件(JSON)。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:45
|
||||
@@ -771,12 +757,17 @@ msgstr "导入"
|
||||
msgid "Password protection removed."
|
||||
msgstr "已移除密码保护。"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:98
|
||||
#: changedetectionio/blueprint/settings/__init__.py:92
|
||||
#, python-brace-format
|
||||
msgid "Warning: Worker count ({}) is close to or exceeds available CPU cores ({})"
|
||||
msgstr "警告:工作线程数({})接近或超过可用CPU核心数({})"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:104
|
||||
#, python-brace-format
|
||||
msgid "Worker count adjusted: {}"
|
||||
msgstr "工作线程数已调整:{}"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:100
|
||||
#: changedetectionio/blueprint/settings/__init__.py:106
|
||||
msgid "Dynamic worker adjustment not supported for sync workers"
|
||||
msgstr "同步工作线程不支持动态调整"
|
||||
|
||||
@@ -985,9 +976,7 @@ msgid "Watch group / tag"
|
||||
msgstr "监控分组/标签"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:21
|
||||
msgid ""
|
||||
"Groups allows you to manage filters and notifications for multiple watches under a "
|
||||
"single organisational tag."
|
||||
msgid "Groups allows you to manage filters and notifications for multiple watches under a single organisational tag."
|
||||
msgstr "分组可让您在一个组织标签下管理多个监控项的过滤器与通知。"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:31
|
||||
@@ -1013,9 +1002,7 @@ msgstr "删除分组?"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:59
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This "
|
||||
"action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgstr "<p>确定要删除分组 <strong>%(title)s</strong> 吗?</p><p>此操作不可撤销。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:60
|
||||
@@ -1035,12 +1022,8 @@ msgstr "取消分组关联?"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:67
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to unlink all watches from group <strong>%(title)s</"
|
||||
"strong>?</p><p>The tag will be kept but watches will be removed from it.</p>"
|
||||
msgstr ""
|
||||
"<p>确定要将分组 <strong>%(title)s</strong> 与所有监控项解绑吗?</p><p>标签会保留,"
|
||||
"但监控项将从该分组移除。</p>"
|
||||
msgid "<p>Are you sure you want to unlink all watches from group <strong>%(title)s</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>"
|
||||
msgstr "<p>确定要将分组 <strong>%(title)s</strong> 与所有监控项解绑吗?</p><p>标签会保留,但监控项将从该分组移除。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:68
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:69
|
||||
@@ -1145,7 +1128,7 @@ msgstr "已克隆,正在编辑新的监控项。"
|
||||
msgid "Queued 1 watch for rechecking."
|
||||
msgstr "已将 1 个监控项加入重新检查队列。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:257
|
||||
#: changedetectionio/blueprint/ui/__init__.py:283
|
||||
#, python-brace-format
|
||||
msgid "Queued {} watches for rechecking."
|
||||
msgstr "已将 {} 个监控项加入重新检查队列。"
|
||||
@@ -1156,9 +1139,7 @@ msgstr "没有可重新检查的监控项。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:330
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Could not share, something went wrong while communicating with the share server - "
|
||||
"{}"
|
||||
msgid "Could not share, something went wrong while communicating with the share server - {}"
|
||||
msgstr "无法分享,与分享服务器通信时出错 - {}"
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:93
|
||||
@@ -1171,8 +1152,7 @@ msgid "No history found for the specified link, bad link?"
|
||||
msgstr "未找到该链接的历史记录,链接是否有误?"
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:98
|
||||
msgid ""
|
||||
"Not enough history (2 snapshots required) to show difference page for this watch."
|
||||
msgid "Not enough history (2 snapshots required) to show difference page for this watch."
|
||||
msgstr "历史记录不足(需要 2 个快照),无法显示该监控项的差异页。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/edit.py:35
|
||||
@@ -1220,9 +1200,7 @@ msgid "Watch added."
|
||||
msgstr "监控项已添加。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:12
|
||||
msgid ""
|
||||
"This will remove version history (snapshots) for ALL watches, but keep your list "
|
||||
"of URLs!"
|
||||
msgid "This will remove version history (snapshots) for ALL watches, but keep your list of URLs!"
|
||||
msgstr "这将删除所有监控项的版本历史(快照),但会保留 URL 列表!"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:13
|
||||
@@ -1243,7 +1221,7 @@ msgstr "确认文本"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:27
|
||||
msgid "Type in the word"
|
||||
msgstr "请输入"
|
||||
msgstr "请输入单词"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:27
|
||||
msgid "clear"
|
||||
@@ -1258,8 +1236,7 @@ msgid "Clear History!"
|
||||
msgstr "清除历史记录!"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:39
|
||||
#: changedetectionio/templates/base.html:379
|
||||
#: changedetectionio/templates/base.html:399
|
||||
#: changedetectionio/templates/base.html:274
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
@@ -1405,9 +1382,7 @@ msgstr "选中文本以分享或加入忽略列表。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:144
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:80
|
||||
msgid ""
|
||||
"For now, Differences are performed on text, not graphically, only the latest "
|
||||
"screenshot is available."
|
||||
msgid "For now, Differences are performed on text, not graphically, only the latest screenshot is available."
|
||||
msgstr "目前差异仅按文本比较,非图形对比,只提供最新截图。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:149
|
||||
@@ -1469,9 +1444,7 @@ msgid "Organisational tag/group name used in the main listing page"
|
||||
msgstr "主列表中使用的组织标签/分组名称"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:85
|
||||
msgid ""
|
||||
"Automatically uses the page title if found, you can also use your own title/"
|
||||
"description here"
|
||||
msgid "Automatically uses the page title if found, you can also use your own title/description here"
|
||||
msgstr "若检测到页面标题将自动使用,你也可以在此自定义标题/描述"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:95
|
||||
@@ -1479,9 +1452,7 @@ msgid "The interval/amount of time between each check."
|
||||
msgstr "每次检查之间的时间间隔。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:110
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for "
|
||||
"knowing when the page changed and your filter will not work anymore."
|
||||
msgid "Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore."
|
||||
msgstr "当页面上找不到该过滤器时发送通知,便于知晓页面已变化且过滤器不再适用。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
@@ -1505,11 +1476,8 @@ msgid "Chrome/Javascript"
|
||||
msgstr "Chrome/JavaScript"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
msgid ""
|
||||
"method requires a network connection to a running WebDriver+Chrome server, set by "
|
||||
"the ENV var 'WEBDRIVER_URL'."
|
||||
msgstr ""
|
||||
"方式需要连接正在运行的 WebDriver+Chrome 服务器,通过环境变量 'WEBDRIVER_URL' 设置。"
|
||||
msgid "method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgstr "方式需要连接正在运行的 WebDriver+Chrome 服务器,通过环境变量 'WEBDRIVER_URL' 设置。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:125
|
||||
msgid "Connect using Bright Data and Oxylabs Proxies, find out more here."
|
||||
@@ -1524,9 +1492,7 @@ msgid "Choose a proxy for this watch"
|
||||
msgstr "为此监控项选择代理"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:143
|
||||
msgid ""
|
||||
"If you're having trouble waiting for the page to be fully rendered (text missing "
|
||||
"etc), try increasing the 'wait' time here."
|
||||
msgid "If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here."
|
||||
msgstr "如果页面渲染未完成(缺文本等),可尝试增加这里的等待时间。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:145
|
||||
@@ -1547,9 +1513,7 @@ msgid "Show advanced options"
|
||||
msgstr "显示高级选项"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:157
|
||||
msgid ""
|
||||
"Run this code before performing change detection, handy for filling in fields and "
|
||||
"other actions"
|
||||
msgid "Run this code before performing change detection, handy for filling in fields and other actions"
|
||||
msgstr "在执行变更检测前运行此代码,便于填写表单等操作"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:158
|
||||
@@ -1606,9 +1570,7 @@ msgid "Visual Selector data is not ready, watch needs to be checked atleast once
|
||||
msgstr "可视化选择器数据尚未就绪,监控项至少需要检查一次。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:253
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support interactive "
|
||||
"Javascript (so far only Playwright based fetchers)"
|
||||
msgid "Sorry, this functionality only works with fetchers that support interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgstr "抱歉,此功能仅适用于支持交互式 JavaScript 的抓取器(目前仅 Playwright)。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:254
|
||||
@@ -1626,9 +1588,7 @@ msgid "to one that supports interactive Javascript."
|
||||
msgstr "为支持交互式 JavaScript 的方式。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:297
|
||||
msgid ""
|
||||
"Use the verify (✓) button to test if a condition passes against the current "
|
||||
"snapshot."
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "使用验证(✓)按钮测试条件是否符合当前快照。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:298
|
||||
@@ -1656,9 +1616,7 @@ msgid "Limit trigger/ignore/block/extract to;"
|
||||
msgstr "将触发/忽略/阻止/提取限定为;"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
msgid ""
|
||||
"Note: Depending on the length and similarity of the text on each line, the "
|
||||
"algorithm may consider an"
|
||||
msgid "Note: Depending on the length and similarity of the text on each line, the algorithm may consider an"
|
||||
msgstr "注意:根据每行文本长度与相似度,算法可能把"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
@@ -1699,15 +1657,11 @@ msgid "Only trigger when unique lines appear"
|
||||
msgstr "仅当出现新的唯一行时触发"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:332
|
||||
msgid ""
|
||||
"Good for websites that just move the content around, and you want to know when NEW "
|
||||
"content is added, compares new lines against all history for this watch."
|
||||
msgstr ""
|
||||
"适合仅移动内容的网站,想知道新增内容时使用,会将新行与该监控项的全部历史进行比对。"
|
||||
msgid "Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch."
|
||||
msgstr "适合仅移动内容的网站,想知道新增内容时使用,会将新行与该监控项的全部历史进行比对。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
msgid ""
|
||||
"Helps reduce changes detected caused by sites shuffling lines around, combine with"
|
||||
msgid "Helps reduce changes detected caused by sites shuffling lines around, combine with"
|
||||
msgstr "有助于减少因行顺序变化导致的变更,可结合"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
@@ -1737,15 +1691,12 @@ msgid "text"
|
||||
msgstr "文本"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid ""
|
||||
"elements that will be used for the change detection. It automatically fills-in the "
|
||||
"filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgstr ""
|
||||
"用于变更检测的元素,并会自动填入“CSS/JSONPath/JQ/XPath 过滤器”选项卡中的过滤器"
|
||||
msgid "elements that will be used for the change detection. It automatically fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgstr "用于变更检测的元素,并会自动填入“CSS/JSONPath/JQ/XPath 过滤器”选项卡中的过滤器"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid "tab. Use"
|
||||
msgstr "使用"
|
||||
msgstr "选项卡中。使用"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid "Shift+Click"
|
||||
@@ -1780,9 +1731,7 @@ msgid "Currently:"
|
||||
msgstr "当前:"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:423
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support Javascript and "
|
||||
"screenshots (such as playwright etc)."
|
||||
msgid "Sorry, this functionality only works with fetchers that support Javascript and screenshots (such as playwright etc)."
|
||||
msgstr "抱歉,此功能仅适用于支持 JavaScript 和截图的抓取器(如 Playwright 等)。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:424
|
||||
@@ -1838,8 +1787,7 @@ msgid "Are you sure you want to clear all history for:"
|
||||
msgstr "确定要清除以下监控项的全部历史记录吗:"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:496
|
||||
msgid ""
|
||||
"This will remove all snapshots and previous versions. This action cannot be undone."
|
||||
msgid "This will remove all snapshots and previous versions. This action cannot be undone."
|
||||
msgstr "这将删除所有快照和历史版本。此操作不可撤销。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:497
|
||||
@@ -1863,9 +1811,7 @@ msgid "Current erroring screenshot from most recent request"
|
||||
msgstr "最近请求的错误截图"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:91
|
||||
msgid ""
|
||||
"Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc ) that "
|
||||
"supports screenshots."
|
||||
msgid "Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc ) that supports screenshots."
|
||||
msgstr "截图需要支持截图的内容抓取器(如 Sockpuppetbrowser、Selenium 等)。"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:31
|
||||
@@ -1934,9 +1880,7 @@ msgid "Clear Histories"
|
||||
msgstr "清除历史记录"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:66
|
||||
msgid ""
|
||||
"<p>Are you sure you want to clear history for the selected items?</p><p>This "
|
||||
"action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to clear history for the selected items?</p><p>This action cannot be undone.</p>"
|
||||
msgstr "<p>确定要清除所选项的历史记录吗?</p><p>此操作不可撤销。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:67
|
||||
@@ -1952,9 +1896,7 @@ msgid "Delete Watches?"
|
||||
msgstr "删除监控项?"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:72
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete the selected watches?</strong></p><p>This "
|
||||
"action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete the selected watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgstr "<p>确定要删除所选监控项吗?</p><p>此操作不可撤销。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:78
|
||||
@@ -1987,7 +1929,7 @@ msgid "Changed"
|
||||
msgstr "变更"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
msgid "No website watches configured, please add a URL in the box above, or"
|
||||
msgid "No web page change detection watches configured, please add a URL in the box above, or"
|
||||
msgstr "尚未配置网站监控项,请在上方输入 URL 或"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
@@ -2015,7 +1957,7 @@ msgid "No information"
|
||||
msgstr "暂无信息"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:234
|
||||
#: changedetectionio/templates/base.html:353
|
||||
#: changedetectionio/templates/base.html:248
|
||||
msgid "Checking now"
|
||||
msgstr "正在检查"
|
||||
|
||||
@@ -2198,238 +2140,340 @@ msgstr "网页文本/HTML、JSON 和 PDF 变更"
|
||||
msgid "Detects all text changes where possible"
|
||||
msgstr "尽可能检测所有文本变更"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:25
|
||||
msgid "Entry"
|
||||
msgstr "条目"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:153
|
||||
msgid "Actions"
|
||||
msgstr "操作"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:172
|
||||
msgid "Add a row/rule after"
|
||||
msgstr "在后面添加一行/规则"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:173
|
||||
msgid "Remove this row/rule"
|
||||
msgstr "删除此行/规则"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:174
|
||||
msgid "Verify this rule against current snapshot"
|
||||
msgstr "用当前快照验证此规则"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid ""
|
||||
"Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome "
|
||||
"based fetching is not enabled."
|
||||
msgstr ""
|
||||
"错误 - 此监控项需要 Chrome(playwright/sockpuppetbrowser),但未启用基于 Chrome 的"
|
||||
"抓取。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid "Alternatively try our"
|
||||
msgstr "也可以试试我们的"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid "very affordable subscription based service which has all this setup for you"
|
||||
msgstr "价格实惠的订阅服务,已为你完成全部配置"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "You may need to"
|
||||
msgstr "你可能需要"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "Enable playwright environment variable"
|
||||
msgstr "启用 Playwright 环境变量"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "and uncomment the"
|
||||
msgstr "并取消注释"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "in the"
|
||||
msgstr "在"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "file"
|
||||
msgstr "文件"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:240
|
||||
msgid "Set a hourly/week day schedule"
|
||||
msgstr "设置小时/工作日计划"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:247
|
||||
msgid "Schedule time limits"
|
||||
msgstr "计划时间限制"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:248
|
||||
msgid "Business hours"
|
||||
msgstr "工作时间"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:249
|
||||
msgid "Weekends"
|
||||
msgstr "周末"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:250
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:259
|
||||
msgid ""
|
||||
"Warning, one or more of your 'days' has a duration that would extend into the next "
|
||||
"day."
|
||||
msgstr "警告:一个或多个“天”的时长会延续到次日。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:260
|
||||
msgid "This could have unintended consequences."
|
||||
msgstr "这可能导致意外结果。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:270
|
||||
msgid "More help and examples about using the scheduler"
|
||||
msgstr "更多关于调度器的帮助与示例"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:275
|
||||
msgid "Want to use a time schedule?"
|
||||
msgstr "想使用时间计划吗?"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:275
|
||||
msgid "First confirm/save your Time Zone Settings"
|
||||
msgstr "请先确认/保存你的时区设置"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:284
|
||||
msgid ""
|
||||
"Triggers a change if this text appears, AND something changed in the document."
|
||||
msgstr "当出现该文本且文档发生变化时触发。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:284
|
||||
msgid "Triggered text"
|
||||
msgstr "触发文本"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:285
|
||||
msgid "Ignored for calculating changes, but still shown."
|
||||
msgstr "计算变更时忽略,但仍会显示。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:285
|
||||
msgid "Ignored text"
|
||||
msgstr "忽略文本"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:286
|
||||
msgid "No change-detection will occur because this text exists."
|
||||
msgstr "由于存在该文本,将不会进行变更检测。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:286
|
||||
msgid "Blocked text"
|
||||
msgstr "阻止文本"
|
||||
|
||||
#: changedetectionio/templates/base.html:78 changedetectionio/templates/base.html:168
|
||||
#: changedetectionio/templates/base.html:77
|
||||
msgid "GROUPS"
|
||||
msgstr "分组"
|
||||
|
||||
#: changedetectionio/templates/base.html:81 changedetectionio/templates/base.html:169
|
||||
#: changedetectionio/templates/base.html:80
|
||||
msgid "SETTINGS"
|
||||
msgstr "设置"
|
||||
|
||||
#: changedetectionio/templates/base.html:84 changedetectionio/templates/base.html:170
|
||||
#: changedetectionio/templates/base.html:83
|
||||
msgid "IMPORT"
|
||||
msgstr "导入"
|
||||
|
||||
#: changedetectionio/templates/base.html:87 changedetectionio/templates/base.html:171
|
||||
msgid "BACKUPS"
|
||||
#: changedetectionio/templates/base.html:86
|
||||
msgid "Backups"
|
||||
msgstr "备份"
|
||||
|
||||
#: changedetectionio/templates/base.html:91 changedetectionio/templates/base.html:173
|
||||
#: changedetectionio/templates/base.html:90
|
||||
msgid "EDIT"
|
||||
msgstr "编辑"
|
||||
|
||||
#: changedetectionio/templates/base.html:101
|
||||
#: changedetectionio/templates/base.html:177
|
||||
#: changedetectionio/templates/base.html:100
|
||||
msgid "LOG OUT"
|
||||
msgstr "退出登录"
|
||||
|
||||
#: changedetectionio/templates/base.html:108
|
||||
#: changedetectionio/templates/base.html:109
|
||||
msgid "Search, or Use Alt+S Key"
|
||||
msgstr "搜索,或使用 Alt+S 快捷键"
|
||||
|
||||
#: changedetectionio/templates/base.html:114
|
||||
#: changedetectionio/templates/base.html:116
|
||||
msgid "Toggle Light/Dark Mode"
|
||||
msgstr "切换亮/暗模式"
|
||||
|
||||
#: changedetectionio/templates/base.html:115
|
||||
#: changedetectionio/templates/base.html:117
|
||||
msgid "Toggle light/dark mode"
|
||||
msgstr "切换亮/暗模式"
|
||||
|
||||
#: changedetectionio/templates/base.html:125
|
||||
#: changedetectionio/templates/base.html:127
|
||||
msgid "Change Language"
|
||||
msgstr "切换语言"
|
||||
|
||||
#: changedetectionio/templates/base.html:126
|
||||
#: changedetectionio/templates/base.html:128
|
||||
msgid "Change language"
|
||||
msgstr "切换语言"
|
||||
|
||||
#: changedetectionio/templates/base.html:253
|
||||
msgid "Watch List"
|
||||
msgstr "监控列表"
|
||||
|
||||
#: changedetectionio/templates/base.html:258
|
||||
msgid "Watches"
|
||||
msgstr "监控项"
|
||||
|
||||
#: changedetectionio/templates/base.html:261
|
||||
msgid "Queue Status"
|
||||
msgstr "队列状态"
|
||||
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid "Queue"
|
||||
msgstr "队列"
|
||||
|
||||
#: changedetectionio/templates/base.html:274
|
||||
#: changedetectionio/templates/base.html:279
|
||||
msgid "Settings"
|
||||
msgstr "设置"
|
||||
|
||||
#: changedetectionio/templates/base.html:293
|
||||
msgid "Sitemap Crawler"
|
||||
msgstr "站点地图爬虫"
|
||||
|
||||
#: changedetectionio/templates/base.html:318
|
||||
msgid "Sitemap"
|
||||
msgstr "站点地图"
|
||||
|
||||
#: changedetectionio/templates/base.html:354
|
||||
#: changedetectionio/templates/base.html:249
|
||||
msgid "Real-time updates offline"
|
||||
msgstr "实时更新离线"
|
||||
|
||||
#: changedetectionio/templates/base.html:364
|
||||
#: changedetectionio/templates/base.html:259
|
||||
msgid "Select Language"
|
||||
msgstr "选择语言"
|
||||
|
||||
#: changedetectionio/templates/base.html:375
|
||||
msgid ""
|
||||
"Language support is in beta, please help us improve by opening a PR on GitHub with "
|
||||
"any updates."
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid "Language support is in beta, please help us improve by opening a PR on GitHub with any updates."
|
||||
msgstr "语言支持仍在测试阶段,欢迎在 GitHub 提交 PR 帮助改进。"
|
||||
|
||||
#: changedetectionio/templates/base.html:387
|
||||
#: changedetectionio/templates/base.html:400
|
||||
msgid "Search"
|
||||
msgstr "搜索"
|
||||
|
||||
#: changedetectionio/templates/base.html:392
|
||||
msgid "URL or Title"
|
||||
msgstr "URL 或标题"
|
||||
|
||||
#: changedetectionio/templates/base.html:392
|
||||
msgid "in"
|
||||
msgstr "在"
|
||||
|
||||
#: changedetectionio/templates/base.html:393
|
||||
msgid "Enter search term..."
|
||||
msgstr "输入搜索关键词..."
|
||||
|
||||
#: changedetectionio/templates/login.html:11
|
||||
#: changedetectionio/templates/login.html:10
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
#: changedetectionio/templates/login.html:17
|
||||
#: changedetectionio/templates/login.html:16
|
||||
msgid "Login"
|
||||
msgstr "登录"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:18
|
||||
#: changedetectionio/widgets/ternary_boolean.py:72
|
||||
msgid "Yes"
|
||||
msgstr "是"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:19
|
||||
#: changedetectionio/widgets/ternary_boolean.py:73
|
||||
msgid "No"
|
||||
msgstr "否"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:20
|
||||
#: changedetectionio/widgets/ternary_boolean.py:74
|
||||
msgid "Main settings"
|
||||
msgstr "主设置"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:11
|
||||
msgid "Show token/placeholders"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:18
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:19
|
||||
#, fuzzy
|
||||
msgid "Description"
|
||||
msgstr "新增"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:128
|
||||
#, fuzzy
|
||||
msgid "Show advanced help and tips"
|
||||
msgstr "显示高级选项"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:138
|
||||
#, fuzzy
|
||||
msgid "Send test notification"
|
||||
msgstr "使用默认通知"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add email"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add an email address"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:142
|
||||
#, fuzzy
|
||||
msgid "Notification debug logs"
|
||||
msgstr "通知调试日志"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:144
|
||||
#, fuzzy
|
||||
msgid "Processing.."
|
||||
msgstr "处理器"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:151
|
||||
#, fuzzy
|
||||
msgid "Title for all notifications"
|
||||
msgstr "使用默认通知"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:176
|
||||
#, fuzzy
|
||||
msgid "Format for all notifications"
|
||||
msgstr "使用默认通知"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:9
|
||||
msgid "Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:10
|
||||
msgid "Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:11
|
||||
msgid "Each line is processed separately (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:12
|
||||
msgid "Note: Wrap in forward slash / to use regex example:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:23
|
||||
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:24
|
||||
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:25
|
||||
msgid "Regular Expression support, wrap the entire line in forward slash"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:26
|
||||
msgid "Changing this will affect the comparison checksum which may trigger an alert"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:43
|
||||
msgid "Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:44
|
||||
msgid "Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:45
|
||||
msgid "All lines here must not exist (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:58
|
||||
msgid "Extracts text in the final output (line by line) after other filters using regular expressions or string match:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:60
|
||||
msgid "Regular expression - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:61
|
||||
msgid "Don't forget to consider the white-space at the start of a line"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
#, fuzzy
|
||||
msgid "Use"
|
||||
msgstr "暂停"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "type flags (more"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
#, fuzzy
|
||||
msgid "information here"
|
||||
msgstr "暂无信息"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:63
|
||||
msgid "Keyword example - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "Use groups to extract just that text - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "returns a list of years only"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:65
|
||||
msgid "Example - match lines containing a keyword"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:68
|
||||
msgid "One line per regular-expression/string match"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Entry"
|
||||
#~ msgstr "条目"
|
||||
|
||||
#~ msgid "Actions"
|
||||
#~ msgstr "操作"
|
||||
|
||||
#~ msgid "Add a row/rule after"
|
||||
#~ msgstr "在后面添加一行/规则"
|
||||
|
||||
#~ msgid "Remove this row/rule"
|
||||
#~ msgstr "删除此行/规则"
|
||||
|
||||
#~ msgid "Verify this rule against current snapshot"
|
||||
#~ msgstr "用当前快照验证此规则"
|
||||
|
||||
#~ msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
|
||||
#~ msgstr "错误 - 此监控项需要 Chrome(playwright/sockpuppetbrowser),但未启用基于 Chrome 的抓取。"
|
||||
|
||||
#~ msgid "Alternatively try our"
|
||||
#~ msgstr "也可以试试我们的"
|
||||
|
||||
#~ msgid "very affordable subscription based service which has all this setup for you"
|
||||
#~ msgstr "价格实惠的订阅服务,已为你完成全部配置"
|
||||
|
||||
#~ msgid "You may need to"
|
||||
#~ msgstr "你可能需要"
|
||||
|
||||
#~ msgid "Enable playwright environment variable"
|
||||
#~ msgstr "启用 Playwright 环境变量"
|
||||
|
||||
#~ msgid "and uncomment the"
|
||||
#~ msgstr "并取消注释"
|
||||
|
||||
#~ msgid "in the"
|
||||
#~ msgstr "在"
|
||||
|
||||
#~ msgid "file"
|
||||
#~ msgstr "文件"
|
||||
|
||||
#~ msgid "Set a hourly/week day schedule"
|
||||
#~ msgstr "设置小时/工作日计划"
|
||||
|
||||
#~ msgid "Schedule time limits"
|
||||
#~ msgstr "计划时间限制"
|
||||
|
||||
#~ msgid "Business hours"
|
||||
#~ msgstr "工作时间"
|
||||
|
||||
#~ msgid "Weekends"
|
||||
#~ msgstr "周末"
|
||||
|
||||
#~ msgid "Reset"
|
||||
#~ msgstr "重置"
|
||||
|
||||
#~ msgid "Warning, one or more of your 'days' has a duration that would extend into the next day."
|
||||
#~ msgstr "警告:一个或多个“天”的时长会延续到次日。"
|
||||
|
||||
#~ msgid "This could have unintended consequences."
|
||||
#~ msgstr "这可能导致意外结果。"
|
||||
|
||||
#~ msgid "More help and examples about using the scheduler"
|
||||
#~ msgstr "更多关于调度器的帮助与示例"
|
||||
|
||||
#~ msgid "Want to use a time schedule?"
|
||||
#~ msgstr "想使用时间计划吗?"
|
||||
|
||||
#~ msgid "First confirm/save your Time Zone Settings"
|
||||
#~ msgstr "请先确认/保存你的时区设置"
|
||||
|
||||
#~ msgid "Triggers a change if this text appears, AND something changed in the document."
|
||||
#~ msgstr "当出现该文本且文档发生变化时触发。"
|
||||
|
||||
#~ msgid "Triggered text"
|
||||
#~ msgstr "触发文本"
|
||||
|
||||
#~ msgid "Ignored for calculating changes, but still shown."
|
||||
#~ msgstr "计算变更时忽略,但仍会显示。"
|
||||
|
||||
#~ msgid "Ignored text"
|
||||
#~ msgstr "忽略文本"
|
||||
|
||||
#~ msgid "No change-detection will occur because this text exists."
|
||||
#~ msgstr "由于存在该文本,将不会进行变更检测。"
|
||||
|
||||
#~ msgid "Blocked text"
|
||||
#~ msgstr "阻止文本"
|
||||
|
||||
#~ msgid "Watch List"
|
||||
#~ msgstr "监控列表"
|
||||
|
||||
#~ msgid "Watches"
|
||||
#~ msgstr "监控项"
|
||||
|
||||
#~ msgid "Queue Status"
|
||||
#~ msgstr "队列状态"
|
||||
|
||||
#~ msgid "Queue"
|
||||
#~ msgstr "队列"
|
||||
|
||||
#~ msgid "Sitemap Crawler"
|
||||
#~ msgstr "站点地图爬虫"
|
||||
|
||||
#~ msgid "Sitemap"
|
||||
#~ msgstr "站点地图"
|
||||
|
||||
#~ msgid "Search"
|
||||
#~ msgstr "搜索"
|
||||
|
||||
#~ msgid "URL or Title"
|
||||
#~ msgstr "URL 或标题"
|
||||
|
||||
#~ msgid "in"
|
||||
#~ msgstr "在"
|
||||
|
||||
#~ msgid "Enter search term..."
|
||||
#~ msgstr "输入搜索关键词..."
|
||||
|
||||
|
||||
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2026-01-03 14:31+0100\n"
|
||||
"POT-Creation-Date: 2026-01-02 16:07+0100\n"
|
||||
"PO-Revision-Date: 2026-01-15 12:00+0800\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: zh_Hant_TW\n"
|
||||
@@ -19,34 +19,30 @@ msgstr ""
|
||||
"Generated-By: Babel 2.17.0\n"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:241
|
||||
#: changedetectionio/flask_app.py:214 changedetectionio/flask_app.py:226
|
||||
#: changedetectionio/flask_app.py:247
|
||||
#: changedetectionio/flask_app.py:213 changedetectionio/flask_app.py:225
|
||||
#: changedetectionio/flask_app.py:246
|
||||
#: changedetectionio/realtime/socket_server.py:171
|
||||
msgid "Not yet"
|
||||
msgstr "還沒有"
|
||||
|
||||
#: changedetectionio/flask_app.py:534
|
||||
msgid "Already logged in"
|
||||
msgstr "已經登入"
|
||||
|
||||
#: changedetectionio/flask_app.py:536
|
||||
#: changedetectionio/flask_app.py:468
|
||||
msgid "You must be logged in, please log in."
|
||||
msgstr "您必須先登入,請登入。"
|
||||
|
||||
#: changedetectionio/flask_app.py:551
|
||||
#: changedetectionio/flask_app.py:495
|
||||
msgid "Already logged in"
|
||||
msgstr "已經登入"
|
||||
|
||||
#: changedetectionio/flask_app.py:522
|
||||
msgid "Incorrect password"
|
||||
msgstr "密碼錯誤"
|
||||
|
||||
#: changedetectionio/forms.py:63 changedetectionio/forms.py:243
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified."
|
||||
msgstr "必須指定至少一個時間間隔(週、天、小時、分鐘或秒)。"
|
||||
|
||||
#: changedetectionio/forms.py:64
|
||||
msgid ""
|
||||
"At least one time interval (weeks, days, hours, minutes, or seconds) must"
|
||||
" be specified when not using global settings."
|
||||
msgid "At least one time interval (weeks, days, hours, minutes, or seconds) must be specified when not using global settings."
|
||||
msgstr "當不使用全域設定時,必須指定至少一個時間間隔(週、天、小時、分鐘或秒)。"
|
||||
|
||||
#: changedetectionio/forms.py:164
|
||||
@@ -581,8 +577,6 @@ msgid "Backups were deleted."
|
||||
msgstr "備份已刪除。"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:6
|
||||
#: changedetectionio/templates/base.html:282
|
||||
#: changedetectionio/templates/base.html:290
|
||||
msgid "Backups"
|
||||
msgstr "備份"
|
||||
|
||||
@@ -591,9 +585,7 @@ msgid "A backup is running!"
|
||||
msgstr "備份正在執行中!"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:13
|
||||
msgid ""
|
||||
"Here you can download and request a new backup, when a backup is "
|
||||
"completed you will see it listed below."
|
||||
msgid "Here you can download and request a new backup, when a backup is completed you will see it listed below."
|
||||
msgstr "您可以在此下載並請求建立新備份,備份完成後將顯示於下方。"
|
||||
|
||||
#: changedetectionio/blueprint/backups/templates/overview.html:19
|
||||
@@ -613,9 +605,7 @@ msgid "Remove backups"
|
||||
msgstr "移除備份"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:45
|
||||
msgid ""
|
||||
"Importing 5,000 of the first URLs from your list, the rest can be "
|
||||
"imported again."
|
||||
msgid "Importing 5,000 of the first URLs from your list, the rest can be imported again."
|
||||
msgstr "正在匯入清單中的前 5,000 個 URL,其餘的可以再次匯入。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:78
|
||||
@@ -650,9 +640,7 @@ msgstr "處理第 {} 行時發生錯誤,URL 數值不正確,已跳過該行
|
||||
#: changedetectionio/blueprint/imports/importer.py:214
|
||||
#: changedetectionio/blueprint/imports/importer.py:297
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Error processing row number {}, check all cell data types are correct, "
|
||||
"row was skipped."
|
||||
msgid "Error processing row number {}, check all cell data types are correct, row was skipped."
|
||||
msgstr "處理第 {} 行時發生錯誤,請檢查所有儲存格資料類型是否正確,已跳過該行。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/importer.py:218
|
||||
@@ -678,9 +666,7 @@ msgid ".XLSX & Wachete"
|
||||
msgstr ".XLSX 和 Wachete"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:20
|
||||
msgid ""
|
||||
"Enter one URL per line, and optionally add tags for each URL after a "
|
||||
"space, delineated by comma (,):"
|
||||
msgid "Enter one URL per line, and optionally add tags for each URL after a space, delineated by comma (,):"
|
||||
msgstr "每行輸入一個 URL,可選用空格分隔後為每個 URL 新增標籤,標籤間用逗號 (,) 分隔:"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:22
|
||||
@@ -692,9 +678,7 @@ msgid "URLs which do not pass validation will stay in the textarea."
|
||||
msgstr "未通過驗證的 URL 將保留在文字區塊中。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:44
|
||||
msgid ""
|
||||
"Copy and Paste your Distill.io watch 'export' file, this should be a JSON"
|
||||
" file."
|
||||
msgid "Copy and Paste your Distill.io watch 'export' file, this should be a JSON file."
|
||||
msgstr "複製並貼上您的 Distill.io 監測任務「匯出」檔案,這應該是一個 JSON 檔案。"
|
||||
|
||||
#: changedetectionio/blueprint/imports/templates/import.html:45
|
||||
@@ -773,12 +757,17 @@ msgstr "匯入"
|
||||
msgid "Password protection removed."
|
||||
msgstr "密碼保護已移除。"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:98
|
||||
#: changedetectionio/blueprint/settings/__init__.py:92
|
||||
#, python-brace-format
|
||||
msgid "Warning: Worker count ({}) is close to or exceeds available CPU cores ({})"
|
||||
msgstr "警告:工作程式數量({})接近或超過可用CPU核心數({})"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:104
|
||||
#, python-brace-format
|
||||
msgid "Worker count adjusted: {}"
|
||||
msgstr "工作程序數量已調整:{}"
|
||||
|
||||
#: changedetectionio/blueprint/settings/__init__.py:100
|
||||
#: changedetectionio/blueprint/settings/__init__.py:106
|
||||
msgid "Dynamic worker adjustment not supported for sync workers"
|
||||
msgstr "同步工作程序不支援動態調整"
|
||||
|
||||
@@ -987,9 +976,7 @@ msgid "Watch group / tag"
|
||||
msgstr "監測群組 / 標籤"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:21
|
||||
msgid ""
|
||||
"Groups allows you to manage filters and notifications for multiple "
|
||||
"watches under a single organisational tag."
|
||||
msgid "Groups allows you to manage filters and notifications for multiple watches under a single organisational tag."
|
||||
msgstr "群組功能讓您能在單一組織標籤下,管理多個監測任務的過濾器與通知設定。"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:31
|
||||
@@ -1015,9 +1002,7 @@ msgstr "刪除群組?"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:59
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete group "
|
||||
"<strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete group <strong>%(title)s</strong>?</p><p>This action cannot be undone.</p>"
|
||||
msgstr "<p>您確定要刪除群組 <strong>%(title)s</strong> 嗎?</p><p>此動作無法復原。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:60
|
||||
@@ -1037,10 +1022,7 @@ msgstr "解除群組連結?"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:67
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<p>Are you sure you want to unlink all watches from group "
|
||||
"<strong>%(title)s</strong>?</p><p>The tag will be kept but watches will "
|
||||
"be removed from it.</p>"
|
||||
msgid "<p>Are you sure you want to unlink all watches from group <strong>%(title)s</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>"
|
||||
msgstr "<p>您確定要將所有監測任務從群組 <strong>%(title)s</strong> 解除連結嗎?</p><p>標籤將被保留,但監測任務將從中移除。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/tags/templates/groups-overview.html:68
|
||||
@@ -1146,7 +1128,7 @@ msgstr "已複製,您正在編輯新的監測任務。"
|
||||
msgid "Queued 1 watch for rechecking."
|
||||
msgstr "已將 1 個監測任務排入複查佇列。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:257
|
||||
#: changedetectionio/blueprint/ui/__init__.py:283
|
||||
#, python-brace-format
|
||||
msgid "Queued {} watches for rechecking."
|
||||
msgstr "已將 {} 個監測任務排入複查佇列。"
|
||||
@@ -1157,9 +1139,7 @@ msgstr "沒有可複查的監測任務。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/__init__.py:330
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Could not share, something went wrong while communicating with the share "
|
||||
"server - {}"
|
||||
msgid "Could not share, something went wrong while communicating with the share server - {}"
|
||||
msgstr "無法分享,與分享伺服器通訊時發生錯誤 - {}"
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:93
|
||||
@@ -1172,9 +1152,7 @@ msgid "No history found for the specified link, bad link?"
|
||||
msgstr "找不到指定連結的歷史記錄,連結無效?"
|
||||
|
||||
#: changedetectionio/blueprint/ui/diff.py:98
|
||||
msgid ""
|
||||
"Not enough history (2 snapshots required) to show difference page for "
|
||||
"this watch."
|
||||
msgid "Not enough history (2 snapshots required) to show difference page for this watch."
|
||||
msgstr "歷史記錄不足(需要 2 個快照)以顯示此監測任務的差異頁面。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/edit.py:35
|
||||
@@ -1222,9 +1200,7 @@ msgid "Watch added."
|
||||
msgstr "監測任務已新增。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:12
|
||||
msgid ""
|
||||
"This will remove version history (snapshots) for ALL watches, but keep "
|
||||
"your list of URLs!"
|
||||
msgid "This will remove version history (snapshots) for ALL watches, but keep your list of URLs!"
|
||||
msgstr "這將移除「所有」監測任務的版本歷史記錄(快照),但保留您的 URL 列表!"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:13
|
||||
@@ -1260,8 +1236,7 @@ msgid "Clear History!"
|
||||
msgstr "清除歷史記錄!"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/clear_all_history.html:39
|
||||
#: changedetectionio/templates/base.html:379
|
||||
#: changedetectionio/templates/base.html:399
|
||||
#: changedetectionio/templates/base.html:274
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
@@ -1407,9 +1382,7 @@ msgstr "反白文字以分享或新增至忽略列表。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:144
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:80
|
||||
msgid ""
|
||||
"For now, Differences are performed on text, not graphically, only the "
|
||||
"latest screenshot is available."
|
||||
msgid "For now, Differences are performed on text, not graphically, only the latest screenshot is available."
|
||||
msgstr "目前,差異比對是針對文字執行,而非圖形,僅提供最新的截圖。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/diff.html:149
|
||||
@@ -1471,9 +1444,7 @@ msgid "Organisational tag/group name used in the main listing page"
|
||||
msgstr "主列表頁面中使用的組織標籤 / 群組名稱"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:85
|
||||
msgid ""
|
||||
"Automatically uses the page title if found, you can also use your own "
|
||||
"title/description here"
|
||||
msgid "Automatically uses the page title if found, you can also use your own title/description here"
|
||||
msgstr "如果找到頁面標題將自動使用,您也可以在此使用您自己的標題 / 描述"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:95
|
||||
@@ -1481,10 +1452,7 @@ msgid "The interval/amount of time between each check."
|
||||
msgstr "每次檢查之間的間隔 / 時間量。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:110
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, "
|
||||
"good for knowing when the page changed and your filter will not work "
|
||||
"anymore."
|
||||
msgid "Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore."
|
||||
msgstr "當頁面上找不到過濾器時發送通知,這有助於了解頁面何時變更導致您的過濾器失效。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
@@ -1496,9 +1464,7 @@ msgid "Basic"
|
||||
msgstr "基本"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:123
|
||||
msgid ""
|
||||
"method (default) where your watched site doesn't need Javascript to "
|
||||
"render."
|
||||
msgid "method (default) where your watched site doesn't need Javascript to render."
|
||||
msgstr "方法(預設),適用於您監測的網站不需要 Javascript 渲染的情況。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
@@ -1510,9 +1476,7 @@ msgid "Chrome/Javascript"
|
||||
msgstr "Chrome / Javascript"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:124
|
||||
msgid ""
|
||||
"method requires a network connection to a running WebDriver+Chrome "
|
||||
"server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgid "method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'."
|
||||
msgstr "方法需要連線到執行中的 WebDriver + Chrome 伺服器,由環境變數 'WEBDRIVER_URL' 設定。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:125
|
||||
@@ -1528,9 +1492,7 @@ msgid "Choose a proxy for this watch"
|
||||
msgstr "為此監測任務選擇代理伺服器"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:143
|
||||
msgid ""
|
||||
"If you're having trouble waiting for the page to be fully rendered (text "
|
||||
"missing etc), try increasing the 'wait' time here."
|
||||
msgid "If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here."
|
||||
msgstr "如果您在等待頁面完全渲染時遇到問題(文字遺失等),請嘗試在此增加「等待」時間。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:145
|
||||
@@ -1551,9 +1513,7 @@ msgid "Show advanced options"
|
||||
msgstr "顯示進階選項"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:157
|
||||
msgid ""
|
||||
"Run this code before performing change detection, handy for filling in "
|
||||
"fields and other actions"
|
||||
msgid "Run this code before performing change detection, handy for filling in fields and other actions"
|
||||
msgstr "在執行變更檢測之前執行此程式碼,方便填寫欄位和其他動作"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:158
|
||||
@@ -1610,9 +1570,7 @@ msgid "Visual Selector data is not ready, watch needs to be checked atleast once
|
||||
msgstr "視覺選擇器資料尚未準備好,監測任務至少需要檢查一次。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:253
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgid "Sorry, this functionality only works with fetchers that support interactive Javascript (so far only Playwright based fetchers)"
|
||||
msgstr "抱歉,此功能僅適用於支援互動式 Javascript 的抓取器(目前僅限基於 Playwright 的抓取器)"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:254
|
||||
@@ -1630,9 +1588,7 @@ msgid "to one that supports interactive Javascript."
|
||||
msgstr "為支援互動式 Javascript 的方式。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:297
|
||||
msgid ""
|
||||
"Use the verify (✓) button to test if a condition passes against the "
|
||||
"current snapshot."
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "使用驗證 (✓) 按鈕測試條件是否符合目前的快照。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:298
|
||||
@@ -1660,9 +1616,7 @@ msgid "Limit trigger/ignore/block/extract to;"
|
||||
msgstr "將觸發 / 忽略 / 阻擋 / 提取限制為;"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
msgid ""
|
||||
"Note: Depending on the length and similarity of the text on each line, "
|
||||
"the algorithm may consider an"
|
||||
msgid "Note: Depending on the length and similarity of the text on each line, the algorithm may consider an"
|
||||
msgstr "注意:根據每行文字的長度和相似度,演算法可能會將"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:326
|
||||
@@ -1703,16 +1657,11 @@ msgid "Only trigger when unique lines appear"
|
||||
msgstr "僅當出現獨特行時觸發"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:332
|
||||
msgid ""
|
||||
"Good for websites that just move the content around, and you want to know"
|
||||
" when NEW content is added, compares new lines against all history for "
|
||||
"this watch."
|
||||
msgid "Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch."
|
||||
msgstr "適用於內容僅會移動的網站,且您想知道何時新增了「新」內容,此功能會將新行與此監測任務的所有歷史記錄進行比較。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
msgid ""
|
||||
"Helps reduce changes detected caused by sites shuffling lines around, "
|
||||
"combine with"
|
||||
msgid "Helps reduce changes detected caused by sites shuffling lines around, combine with"
|
||||
msgstr "有助於減少因網站重新排列行而檢測到的變更,結合"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:340
|
||||
@@ -1742,9 +1691,7 @@ msgid "text"
|
||||
msgstr "文字"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
msgid ""
|
||||
"elements that will be used for the change detection. It automatically "
|
||||
"fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgid "elements that will be used for the change detection. It automatically fills-in the filters in the \"CSS/JSONPath/JQ/XPath Filters\" box of the"
|
||||
msgstr "將用於變更檢測的元素。它會自動填入"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:386
|
||||
@@ -1784,9 +1731,7 @@ msgid "Currently:"
|
||||
msgstr "目前:"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:423
|
||||
msgid ""
|
||||
"Sorry, this functionality only works with fetchers that support "
|
||||
"Javascript and screenshots (such as playwright etc)."
|
||||
msgid "Sorry, this functionality only works with fetchers that support Javascript and screenshots (such as playwright etc)."
|
||||
msgstr "抱歉,此功能僅適用於支援 Javascript 和截圖的抓取器(如 playwright 等)。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:424
|
||||
@@ -1842,9 +1787,7 @@ msgid "Are you sure you want to clear all history for:"
|
||||
msgstr "您確定要清除以下項目的所有歷史記錄:"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:496
|
||||
msgid ""
|
||||
"This will remove all snapshots and previous versions. This action cannot "
|
||||
"be undone."
|
||||
msgid "This will remove all snapshots and previous versions. This action cannot be undone."
|
||||
msgstr "這將移除所有快照和先前版本。此動作無法復原。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html:497
|
||||
@@ -1868,9 +1811,7 @@ msgid "Current erroring screenshot from most recent request"
|
||||
msgstr "最近請求的目前錯誤截圖"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/preview.html:91
|
||||
msgid ""
|
||||
"Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc "
|
||||
") that supports screenshots."
|
||||
msgid "Screenshot requires a Content Fetcher ( Sockpuppetbrowser, selenium, etc ) that supports screenshots."
|
||||
msgstr "截圖需要支援截圖的內容抓取器 (Sockpuppetbrowser, selenium 等)。"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:31
|
||||
@@ -1939,9 +1880,7 @@ msgid "Clear Histories"
|
||||
msgstr "清除歷史記錄"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:66
|
||||
msgid ""
|
||||
"<p>Are you sure you want to clear history for the selected "
|
||||
"items?</p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to clear history for the selected items?</p><p>This action cannot be undone.</p>"
|
||||
msgstr "<p>您確定要清除所選項目的歷史記錄嗎?</p><p>此動作無法復原。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:67
|
||||
@@ -1957,9 +1896,7 @@ msgid "Delete Watches?"
|
||||
msgstr "刪除監測任務?"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:72
|
||||
msgid ""
|
||||
"<p>Are you sure you want to delete the selected "
|
||||
"watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgid "<p>Are you sure you want to delete the selected watches?</strong></p><p>This action cannot be undone.</p>"
|
||||
msgstr "<p>您確定要刪除所選的監測任務嗎?</strong></p><p>此動作無法復原。</p>"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:78
|
||||
@@ -1992,7 +1929,7 @@ msgid "Changed"
|
||||
msgstr "變更"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
msgid "No website watches configured, please add a URL in the box above, or"
|
||||
msgid "No web page change detection watches configured, please add a URL in the box above, or"
|
||||
msgstr "未設定網站監測任務,請在上方欄位新增 URL,或"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:130
|
||||
@@ -2020,7 +1957,7 @@ msgid "No information"
|
||||
msgstr "無資訊"
|
||||
|
||||
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html:234
|
||||
#: changedetectionio/templates/base.html:353
|
||||
#: changedetectionio/templates/base.html:248
|
||||
msgid "Checking now"
|
||||
msgstr "正在檢查"
|
||||
|
||||
@@ -2203,244 +2140,340 @@ msgstr "網頁文字 / HTML、JSON 和 PDF 變更"
|
||||
msgid "Detects all text changes where possible"
|
||||
msgstr "盡可能檢測所有文字變更"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:25
|
||||
msgid "Entry"
|
||||
msgstr "項目"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:153
|
||||
msgid "Actions"
|
||||
msgstr "動作"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:172
|
||||
msgid "Add a row/rule after"
|
||||
msgstr "在後方新增一行 / 規則"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:173
|
||||
msgid "Remove this row/rule"
|
||||
msgstr "移除此行 / 規則"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:174
|
||||
msgid "Verify this rule against current snapshot"
|
||||
msgstr "針對目前快照驗證此規則"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid ""
|
||||
"Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but "
|
||||
"Chrome based fetching is not enabled."
|
||||
msgstr "錯誤 - 此監測任務需要 Chrome(搭配 playwright / sockpuppetbrowser),但未啟用基於 Chrome 的抓取功能。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid "Alternatively try our"
|
||||
msgstr "或者嘗試我們"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:184
|
||||
msgid ""
|
||||
"very affordable subscription based service which has all this setup for "
|
||||
"you"
|
||||
msgstr "非常實惠的訂閱服務,為您準備好所有設定"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "You may need to"
|
||||
msgstr "您可能需要"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "Enable playwright environment variable"
|
||||
msgstr "啟用 playwright 環境變數"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "and uncomment the"
|
||||
msgstr "並取消註解"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "in the"
|
||||
msgstr "於"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:185
|
||||
msgid "file"
|
||||
msgstr "檔案"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:240
|
||||
msgid "Set a hourly/week day schedule"
|
||||
msgstr "設定每小時 / 平日排程"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:247
|
||||
msgid "Schedule time limits"
|
||||
msgstr "排程時間限制"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:248
|
||||
msgid "Business hours"
|
||||
msgstr "營業時間"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:249
|
||||
msgid "Weekends"
|
||||
msgstr "週末"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:250
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:259
|
||||
msgid ""
|
||||
"Warning, one or more of your 'days' has a duration that would extend into"
|
||||
" the next day."
|
||||
msgstr "警告,您設定的一個或多個「天」持續時間將延伸至隔天。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:260
|
||||
msgid "This could have unintended consequences."
|
||||
msgstr "這可能會產生非預期的後果。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:270
|
||||
msgid "More help and examples about using the scheduler"
|
||||
msgstr "關於使用排程器的更多幫助和範例"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:275
|
||||
msgid "Want to use a time schedule?"
|
||||
msgstr "想要使用時間排程嗎?"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:275
|
||||
msgid "First confirm/save your Time Zone Settings"
|
||||
msgstr "請先確認 / 儲存您的時區設定"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:284
|
||||
msgid ""
|
||||
"Triggers a change if this text appears, AND something changed in the "
|
||||
"document."
|
||||
msgstr "如果出現此文字,且文件中有些內容變更,則觸發變更。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:284
|
||||
msgid "Triggered text"
|
||||
msgstr "觸發的文字"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:285
|
||||
msgid "Ignored for calculating changes, but still shown."
|
||||
msgstr "計算變更時忽略,但仍會顯示。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:285
|
||||
msgid "Ignored text"
|
||||
msgstr "忽略的文字"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:286
|
||||
msgid "No change-detection will occur because this text exists."
|
||||
msgstr "因為存在此文字,將不會進行變更檢測。"
|
||||
|
||||
#: changedetectionio/templates/_helpers.html:286
|
||||
msgid "Blocked text"
|
||||
msgstr "被阻擋的文字"
|
||||
|
||||
#: changedetectionio/templates/base.html:78
|
||||
#: changedetectionio/templates/base.html:168
|
||||
#: changedetectionio/templates/base.html:77
|
||||
msgid "GROUPS"
|
||||
msgstr "群組"
|
||||
|
||||
#: changedetectionio/templates/base.html:81
|
||||
#: changedetectionio/templates/base.html:169
|
||||
#: changedetectionio/templates/base.html:80
|
||||
msgid "SETTINGS"
|
||||
msgstr "設定"
|
||||
|
||||
#: changedetectionio/templates/base.html:84
|
||||
#: changedetectionio/templates/base.html:170
|
||||
#: changedetectionio/templates/base.html:83
|
||||
msgid "IMPORT"
|
||||
msgstr "匯入"
|
||||
|
||||
#: changedetectionio/templates/base.html:87
|
||||
#: changedetectionio/templates/base.html:171
|
||||
msgid "BACKUPS"
|
||||
#: changedetectionio/templates/base.html:86
|
||||
msgid "Backups"
|
||||
msgstr "備份"
|
||||
|
||||
#: changedetectionio/templates/base.html:91
|
||||
#: changedetectionio/templates/base.html:173
|
||||
#: changedetectionio/templates/base.html:90
|
||||
msgid "EDIT"
|
||||
msgstr "編輯"
|
||||
|
||||
#: changedetectionio/templates/base.html:101
|
||||
#: changedetectionio/templates/base.html:177
|
||||
#: changedetectionio/templates/base.html:100
|
||||
msgid "LOG OUT"
|
||||
msgstr "登出"
|
||||
|
||||
#: changedetectionio/templates/base.html:108
|
||||
#: changedetectionio/templates/base.html:109
|
||||
msgid "Search, or Use Alt+S Key"
|
||||
msgstr "搜尋,或使用 Alt+S 鍵"
|
||||
|
||||
#: changedetectionio/templates/base.html:114
|
||||
#: changedetectionio/templates/base.html:116
|
||||
msgid "Toggle Light/Dark Mode"
|
||||
msgstr "切換亮 / 暗模式"
|
||||
|
||||
#: changedetectionio/templates/base.html:115
|
||||
#: changedetectionio/templates/base.html:117
|
||||
msgid "Toggle light/dark mode"
|
||||
msgstr "切換亮 / 暗模式"
|
||||
|
||||
#: changedetectionio/templates/base.html:125
|
||||
#: changedetectionio/templates/base.html:127
|
||||
msgid "Change Language"
|
||||
msgstr "更改語言"
|
||||
|
||||
#: changedetectionio/templates/base.html:126
|
||||
#: changedetectionio/templates/base.html:128
|
||||
msgid "Change language"
|
||||
msgstr "更改語言"
|
||||
|
||||
#: changedetectionio/templates/base.html:253
|
||||
msgid "Watch List"
|
||||
msgstr "監測列表"
|
||||
|
||||
#: changedetectionio/templates/base.html:258
|
||||
msgid "Watches"
|
||||
msgstr "監測任務"
|
||||
|
||||
#: changedetectionio/templates/base.html:261
|
||||
msgid "Queue Status"
|
||||
msgstr "佇列狀態"
|
||||
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid "Queue"
|
||||
msgstr "佇列"
|
||||
|
||||
#: changedetectionio/templates/base.html:274
|
||||
#: changedetectionio/templates/base.html:279
|
||||
msgid "Settings"
|
||||
msgstr "設定"
|
||||
|
||||
#: changedetectionio/templates/base.html:293
|
||||
msgid "Sitemap Crawler"
|
||||
msgstr "Sitemap 爬蟲"
|
||||
|
||||
#: changedetectionio/templates/base.html:318
|
||||
msgid "Sitemap"
|
||||
msgstr "Sitemap"
|
||||
|
||||
#: changedetectionio/templates/base.html:354
|
||||
#: changedetectionio/templates/base.html:249
|
||||
msgid "Real-time updates offline"
|
||||
msgstr "離線即時更新"
|
||||
|
||||
#: changedetectionio/templates/base.html:364
|
||||
#: changedetectionio/templates/base.html:259
|
||||
msgid "Select Language"
|
||||
msgstr "選擇語言"
|
||||
|
||||
#: changedetectionio/templates/base.html:375
|
||||
msgid ""
|
||||
"Language support is in beta, please help us improve by opening a PR on "
|
||||
"GitHub with any updates."
|
||||
#: changedetectionio/templates/base.html:270
|
||||
msgid "Language support is in beta, please help us improve by opening a PR on GitHub with any updates."
|
||||
msgstr "語言支援尚在 Beta 階段,請在 GitHub 上提交 PR 以協助我們改進。"
|
||||
|
||||
#: changedetectionio/templates/base.html:387
|
||||
#: changedetectionio/templates/base.html:400
|
||||
msgid "Search"
|
||||
msgstr "搜尋"
|
||||
|
||||
#: changedetectionio/templates/base.html:392
|
||||
msgid "URL or Title"
|
||||
msgstr "URL 或標題"
|
||||
|
||||
#: changedetectionio/templates/base.html:392
|
||||
msgid "in"
|
||||
msgstr "於"
|
||||
|
||||
#: changedetectionio/templates/base.html:393
|
||||
msgid "Enter search term..."
|
||||
msgstr "輸入搜尋關鍵字 ..."
|
||||
|
||||
#: changedetectionio/templates/login.html:11
|
||||
#: changedetectionio/templates/login.html:10
|
||||
msgid "Password"
|
||||
msgstr "密碼"
|
||||
|
||||
#: changedetectionio/templates/login.html:17
|
||||
#: changedetectionio/templates/login.html:16
|
||||
msgid "Login"
|
||||
msgstr "登入"
|
||||
msgstr "登入"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:18
|
||||
#: changedetectionio/widgets/ternary_boolean.py:72
|
||||
msgid "Yes"
|
||||
msgstr "是"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:19
|
||||
#: changedetectionio/widgets/ternary_boolean.py:73
|
||||
msgid "No"
|
||||
msgstr "否"
|
||||
|
||||
#: changedetectionio/widgets/ternary_boolean.py:20
|
||||
#: changedetectionio/widgets/ternary_boolean.py:74
|
||||
msgid "Main settings"
|
||||
msgstr "主設定"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:11
|
||||
msgid "Show token/placeholders"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:18
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:19
|
||||
#, fuzzy
|
||||
msgid "Description"
|
||||
msgstr "新增"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:128
|
||||
#, fuzzy
|
||||
msgid "Show advanced help and tips"
|
||||
msgstr "顯示進階選項"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:138
|
||||
#, fuzzy
|
||||
msgid "Send test notification"
|
||||
msgstr "使用預設通知"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add email"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:140
|
||||
msgid "Add an email address"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:142
|
||||
#, fuzzy
|
||||
msgid "Notification debug logs"
|
||||
msgstr "通知除錯記錄"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:144
|
||||
#, fuzzy
|
||||
msgid "Processing.."
|
||||
msgstr "處理器"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:151
|
||||
#, fuzzy
|
||||
msgid "Title for all notifications"
|
||||
msgstr "使用預設通知"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html:176
|
||||
#, fuzzy
|
||||
msgid "Format for all notifications"
|
||||
msgstr "使用預設通知"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:9
|
||||
msgid "Text to wait for before triggering a change/notification, all text and regex are tested case-insensitive."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:10
|
||||
msgid "Trigger text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:11
|
||||
msgid "Each line is processed separately (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:12
|
||||
msgid "Note: Wrap in forward slash / to use regex example:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:23
|
||||
msgid "Matching text will be ignored in the text snapshot (you can still see it but it wont trigger a change)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:24
|
||||
msgid "Each line processed separately, any line matching will be ignored (removed before creating the checksum)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:25
|
||||
msgid "Regular Expression support, wrap the entire line in forward slash"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:26
|
||||
msgid "Changing this will affect the comparison checksum which may trigger an alert"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:43
|
||||
msgid "Block change-detection while this text is on the page, all text and regex are tested case-insensitive, good for waiting for when a product is available again"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:44
|
||||
msgid "Block text is processed from the result-text that comes out of any CSS/JSON Filters for this monitor"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:45
|
||||
msgid "All lines here must not exist (think of each line as \"OR\")"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:58
|
||||
msgid "Extracts text in the final output (line by line) after other filters using regular expressions or string match:"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:60
|
||||
msgid "Regular expression - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:61
|
||||
msgid "Don't forget to consider the white-space at the start of a line"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
#, fuzzy
|
||||
msgid "Use"
|
||||
msgstr "暫停"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
msgid "type flags (more"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:62
|
||||
#, fuzzy
|
||||
msgid "information here"
|
||||
msgstr "無資訊"
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:63
|
||||
msgid "Keyword example - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "Use groups to extract just that text - example"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:64
|
||||
msgid "returns a list of years only"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:65
|
||||
msgid "Example - match lines containing a keyword"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/edit/text-options.html:68
|
||||
msgid "One line per regular-expression/string match"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Entry"
|
||||
#~ msgstr "項目"
|
||||
|
||||
#~ msgid "Actions"
|
||||
#~ msgstr "動作"
|
||||
|
||||
#~ msgid "Add a row/rule after"
|
||||
#~ msgstr "在後方新增一行 / 規則"
|
||||
|
||||
#~ msgid "Remove this row/rule"
|
||||
#~ msgstr "移除此行 / 規則"
|
||||
|
||||
#~ msgid "Verify this rule against current snapshot"
|
||||
#~ msgstr "針對目前快照驗證此規則"
|
||||
|
||||
#~ msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
|
||||
#~ msgstr "錯誤 - 此監測任務需要 Chrome(搭配 playwright / sockpuppetbrowser),但未啟用基於 Chrome 的抓取功能。"
|
||||
|
||||
#~ msgid "Alternatively try our"
|
||||
#~ msgstr "或者嘗試我們"
|
||||
|
||||
#~ msgid "very affordable subscription based service which has all this setup for you"
|
||||
#~ msgstr "非常實惠的訂閱服務,為您準備好所有設定"
|
||||
|
||||
#~ msgid "You may need to"
|
||||
#~ msgstr "您可能需要"
|
||||
|
||||
#~ msgid "Enable playwright environment variable"
|
||||
#~ msgstr "啟用 playwright 環境變數"
|
||||
|
||||
#~ msgid "and uncomment the"
|
||||
#~ msgstr "並取消註解"
|
||||
|
||||
#~ msgid "in the"
|
||||
#~ msgstr "於"
|
||||
|
||||
#~ msgid "file"
|
||||
#~ msgstr "檔案"
|
||||
|
||||
#~ msgid "Set a hourly/week day schedule"
|
||||
#~ msgstr "設定每小時 / 平日排程"
|
||||
|
||||
#~ msgid "Schedule time limits"
|
||||
#~ msgstr "排程時間限制"
|
||||
|
||||
#~ msgid "Business hours"
|
||||
#~ msgstr "營業時間"
|
||||
|
||||
#~ msgid "Weekends"
|
||||
#~ msgstr "週末"
|
||||
|
||||
#~ msgid "Reset"
|
||||
#~ msgstr "重置"
|
||||
|
||||
#~ msgid "Warning, one or more of your 'days' has a duration that would extend into the next day."
|
||||
#~ msgstr "警告,您設定的一個或多個「天」持續時間將延伸至隔天。"
|
||||
|
||||
#~ msgid "This could have unintended consequences."
|
||||
#~ msgstr "這可能會產生非預期的後果。"
|
||||
|
||||
#~ msgid "More help and examples about using the scheduler"
|
||||
#~ msgstr "關於使用排程器的更多幫助和範例"
|
||||
|
||||
#~ msgid "Want to use a time schedule?"
|
||||
#~ msgstr "想要使用時間排程嗎?"
|
||||
|
||||
#~ msgid "First confirm/save your Time Zone Settings"
|
||||
#~ msgstr "請先確認 / 儲存您的時區設定"
|
||||
|
||||
#~ msgid "Triggers a change if this text appears, AND something changed in the document."
|
||||
#~ msgstr "如果出現此文字,且文件中有些內容變更,則觸發變更。"
|
||||
|
||||
#~ msgid "Triggered text"
|
||||
#~ msgstr "觸發的文字"
|
||||
|
||||
#~ msgid "Ignored for calculating changes, but still shown."
|
||||
#~ msgstr "計算變更時忽略,但仍會顯示。"
|
||||
|
||||
#~ msgid "Ignored text"
|
||||
#~ msgstr "忽略的文字"
|
||||
|
||||
#~ msgid "No change-detection will occur because this text exists."
|
||||
#~ msgstr "因為存在此文字,將不會進行變更檢測。"
|
||||
|
||||
#~ msgid "Blocked text"
|
||||
#~ msgstr "被阻擋的文字"
|
||||
|
||||
#~ msgid "Watch List"
|
||||
#~ msgstr "監測列表"
|
||||
|
||||
#~ msgid "Watches"
|
||||
#~ msgstr "監測任務"
|
||||
|
||||
#~ msgid "Queue Status"
|
||||
#~ msgstr "佇列狀態"
|
||||
|
||||
#~ msgid "Queue"
|
||||
#~ msgstr "佇列"
|
||||
|
||||
#~ msgid "Sitemap Crawler"
|
||||
#~ msgstr "Sitemap 爬蟲"
|
||||
|
||||
#~ msgid "Sitemap"
|
||||
#~ msgstr "Sitemap"
|
||||
|
||||
#~ msgid "Search"
|
||||
#~ msgstr "搜尋"
|
||||
|
||||
#~ msgid "URL or Title"
|
||||
#~ msgstr "URL 或標題"
|
||||
|
||||
#~ msgid "in"
|
||||
#~ msgstr "於"
|
||||
|
||||
#~ msgid "Enter search term..."
|
||||
#~ msgstr "輸入搜尋關鍵字 ..."
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from wtforms import Field
|
||||
from wtforms import widgets
|
||||
from markupsafe import Markup
|
||||
from flask_babel import lazy_gettext as _l
|
||||
|
||||
class TernaryNoneBooleanWidget:
|
||||
"""
|
||||
@@ -14,9 +14,9 @@ class TernaryNoneBooleanWidget:
|
||||
boolean_mode = getattr(field, 'boolean_mode', False)
|
||||
|
||||
# Get custom text or use defaults
|
||||
yes_text = getattr(field, 'yes_text', 'Yes')
|
||||
no_text = getattr(field, 'no_text', 'No')
|
||||
none_text = getattr(field, 'none_text', 'Main settings')
|
||||
yes_text = getattr(field, 'yes_text', _l('Yes'))
|
||||
no_text = getattr(field, 'no_text', _l('No'))
|
||||
none_text = getattr(field, 'none_text', _l('Main settings'))
|
||||
|
||||
# True option
|
||||
checked_true = ' checked' if field.data is True else ''
|
||||
@@ -63,14 +63,14 @@ class TernaryNoneBooleanField(Field):
|
||||
"""
|
||||
widget = TernaryNoneBooleanWidget()
|
||||
|
||||
def __init__(self, label=None, validators=None, false_values=None, boolean_mode=False,
|
||||
yes_text="Yes", no_text="No", none_text="Main settings", **kwargs):
|
||||
def __init__(self, label=None, validators=None, false_values=None, boolean_mode=False,
|
||||
yes_text=None, no_text=None, none_text=None, **kwargs):
|
||||
super(TernaryNoneBooleanField, self).__init__(label, validators, **kwargs)
|
||||
|
||||
self.boolean_mode = boolean_mode
|
||||
self.yes_text = yes_text
|
||||
self.no_text = no_text
|
||||
self.none_text = none_text
|
||||
self.yes_text = yes_text if yes_text is not None else _l('Yes')
|
||||
self.no_text = no_text if no_text is not None else _l('No')
|
||||
self.none_text = none_text if none_text is not None else _l('Main settings')
|
||||
|
||||
if false_values is None:
|
||||
self.false_values = {'false', ''}
|
||||
|
||||
Reference in New Issue
Block a user