Compare commits

...

14 Commits

Author SHA1 Message Date
dgtlmoon eae9521924 Recompile languages 2026-04-28 15:23:01 +10:00
dgtlmoon 1dbbbd6819 0.55.2 2026-04-28 15:20:38 +10:00
redphx d07b57b816 typo: {{diff_url}} token mentioned twice (#4094) 2026-04-28 05:52:58 +02:00
skkzsh 22ef98d58e i18n: UI - Align desktop "Last Checked" / "Last Changed" with mobile (#4090) 2026-04-28 02:46:40 +02:00
dgtlmoon 9f6e4ea0ad UI - AI/LLM - OpenRouter config UI was missing the correct fields. #4091 2026-04-28 10:44:28 +10:00
skkzsh 7d2803e179 Freeze POT-Creation-Date at sentinel to stop per-locale churn (#4092)
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-04-28 00:37:14 +10:00
dgtlmoon f93dc7746d i18n - Recompile languages
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-04-27 17:11:06 +10:00
dgtlmoon d427dbc2b2 0.55.1 2026-04-27 17:03:18 +10:00
dgtlmoon 52b189fc7c Security - Hardening XML parser against XXE 2026-04-27 17:00:42 +10:00
dgtlmoon 866b442576 Security - Stored XSS via Tag Name in Modal Dialog 2026-04-27 16:36:28 +10:00
dgtlmoon ba20f66cee Security - Arbitrary Local File Read via crafted backup restore 2026-04-27 16:35:07 +10:00
Junhan Koo e064bcea13 i18n - Update Korean language (#4084)
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-04-27 05:01:37 +02:00
dgtlmoon 74a7eb1b11 [i18n] "Usage" tab label in AI / LLM settings is ambiguous across contexts #4086 (#4088)
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
2026-04-26 15:25:22 +02:00
dgtlmoon 79d75f7926 Translations - Playwright macro unused, add extra linting for translations, add TRANSLATORS.md (#4087)
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
ChangeDetection.io App Test / lint-template-i18n (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v7 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v8 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (main) (push) Has been cancelled
2026-04-26 12:36:50 +02:00
48 changed files with 1413 additions and 1356 deletions
+55 -5
View File
@@ -44,10 +44,60 @@ jobs:
exit 1
fi
lint-template-i18n:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check for fragmented gettext calls in templates
run: |
python3 << 'PYEOF'
import re, sys
from pathlib import Path
# Detects adjacent {{ _(...) }} calls on the same line separated only by HTML
# tags, whitespace, or non-translating Jinja2 variables — the anti-pattern of
# splitting a single sentence across multiple msgids.
# See https://github.com/dgtlmoon/changedetection.io/issues/4074 for background.
#
# The correct fix is to consolidate fragments into one entire-sentence msgid,
# injecting dynamic values via %(name)s kwargs — per the GNU gettext manual
# sections "Entire sentences" and "No string concatenation". See PR #4076 for
# worked examples of each consolidation pattern.
#
# BASELINE: this limit reflects pre-existing violations present when this check
# was introduced. It must only ever go DOWN. Each time you fix a template, lower
# the limit by the number of lines fixed so the improvement is locked in.
# When the count reaches 0, replace the baseline check with a hard sys.exit(1).
BASELINE_LIMIT = 44
FRAGMENT_RE = re.compile(
r'\{\{[^{}]*\b_\s*\([^)]*\)[^{}]*\}\}'
r'(?:\s*(?:<[^>]+>|\{\{(?![^}]*\b_\s*\()[^}]*\}\})\s*)+'
r'\{\{[^{}]*\b_\s*\([^)]*\)[^{}]*\}\}'
)
violations = []
for f in sorted(Path('changedetectionio').rglob('*.html')):
for lineno, line in enumerate(f.read_text().splitlines(), 1):
if FRAGMENT_RE.search(line):
violations.append(f"{f}:{lineno}: {line.strip()[:120]}")
count = len(violations)
print(f"Fragmented i18n calls found: {count} (limit: {BASELINE_LIMIT})")
for v in violations:
print(v)
if count > BASELINE_LIMIT:
print(f"\nERROR: {count} fragmented gettext calls exceed the baseline of {BASELINE_LIMIT}.")
print("Consolidate adjacent _() calls into a single entire-sentence msgid.")
print("See https://github.com/dgtlmoon/changedetection.io/issues/4074 for patterns.")
sys.exit(1)
PYEOF
test-application-3-10:
# Only run on push to master (including PR merges)
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: [lint-code, lint-translations]
needs: [lint-code, lint-translations, lint-template-i18n]
uses: ./.github/workflows/test-stack-reusable-workflow.yml
with:
python-version: '3.10'
@@ -55,7 +105,7 @@ jobs:
test-application-3-11:
# Always run
needs: [lint-code, lint-translations]
needs: [lint-code, lint-translations, lint-template-i18n]
uses: ./.github/workflows/test-stack-reusable-workflow.yml
with:
python-version: '3.11'
@@ -63,7 +113,7 @@ jobs:
test-application-3-12:
# Only run on push to master (including PR merges)
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: [lint-code, lint-translations]
needs: [lint-code, lint-translations, lint-template-i18n]
uses: ./.github/workflows/test-stack-reusable-workflow.yml
with:
python-version: '3.12'
@@ -72,7 +122,7 @@ jobs:
test-application-3-13:
# Only run on push to master (including PR merges)
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: [lint-code, lint-translations]
needs: [lint-code, lint-translations, lint-template-i18n]
uses: ./.github/workflows/test-stack-reusable-workflow.yml
with:
python-version: '3.13'
@@ -81,7 +131,7 @@ jobs:
test-application-3-14:
#if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: [lint-code, lint-translations]
needs: [lint-code, lint-translations, lint-template-i18n]
uses: ./.github/workflows/test-stack-reusable-workflow.yml
with:
python-version: '3.14'
+2
View File
@@ -352,4 +352,6 @@ changedetectionio.html_tools.elementpath_tostring: Copyright (c), 2018-2021, SIS
Recognition of fantastic contributors to the project
<sub>Developer note: see [translation guide](changedetectionio/translations/README.md) for i18n template patterns and workflow.</sub>
- Constantin Hong https://github.com/Constantin1489
+1 -1
View File
@@ -2,7 +2,7 @@
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
# Semver means never use .01, or 00. Should be .1.
__version__ = '0.54.10'
__version__ = '0.55.2'
from changedetectionio.strtobool import strtobool
from json.decoder import JSONDecodeError
@@ -7,12 +7,14 @@
<div class="tab-pane-inner" id="ai">
<script src="{{ url_for('static_content', group='js', filename='sub-tabs.js') }}"></script>
{# TRANSLATORS: 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide #}
{% set _usage_label = pgettext('AI usage stats', 'Usage') %}
{% call stab_shell('ai-settings', [
{'id': 'overview', 'label': _('Overview'), 'icon': '✦'},
{'id': 'provider', 'label': _('Provider'), 'icon': '⚙'},
{'id': 'prompts', 'label': _('Prompts'), 'icon': '≡'},
{'id': 'behaviour', 'label': _('Behaviour'), 'icon': '⚑'},
{'id': 'usage', 'label': _('Usage'), 'icon': '$'},
{'id': 'usage', 'label': _usage_label, 'icon': '$'},
]) %}
{# ── Overview ──────────────────────────────────────────────────────────── #}
@@ -375,7 +377,7 @@
<script>
(function () {
const LIVE_PROVIDERS = ['openai', 'anthropic', 'gemini', 'ollama'];
const LIVE_PROVIDERS = ['openai', 'anthropic', 'gemini', 'ollama', 'openrouter'];
const BASE_DEFAULTS = { ollama: 'http://localhost:11434' };
const KEY_HINTS = {
openai: '{{ _("platform.openai.com → API keys") }}',
@@ -399,7 +401,7 @@
fetchGroup.style.display = LIVE_PROVIDERS.includes(provider) ? '' : 'none';
const needsBase = provider === 'ollama' || provider === 'openrouter';
const needsBase = provider === 'ollama';
baseGroup.style.display = needsBase ? '' : 'none';
if (BASE_DEFAULTS[provider] !== undefined) {
if (!baseField.value) baseField.value = BASE_DEFAULTS[provider];
@@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% block content %}
{% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form, playwright_warning, only_playwright_type_watches_warning, highlight_trigger_ignored_explainer, render_conditions_fieldlist_of_formfields_as_table, render_ternary_field %}
{% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form, only_playwright_type_watches_warning, highlight_trigger_ignored_explainer, render_conditions_fieldlist_of_formfields_as_table, render_ternary_field %}
{% from '_common_fields.html' import render_common_settings_form %}
<script src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
<script src="{{url_for('static_content', group='js', filename='vis.js')}}" defer></script>
@@ -223,8 +223,8 @@ window.watchOverviewI18n = {
{%- if any_has_restock_price_processor -%}
<th>{{ _('Restock & Price') }}</th>
{%- endif -%}
<th><a class="{{ 'active '+link_order if sort_attribute == 'last_checked' else 'inactive' }}" href="{{url_for('watchlist.index', sort='last_checked', order=link_order, tag=active_tag_uuid)}}"><span class="hide-on-mobile">{{ _('Last') }}</span> {{ _('Checked') }} <span class='arrow {{link_order}}'></span></a></th>
<th><a class="{{ 'active '+link_order if sort_attribute == 'last_changed' else 'inactive' }}" href="{{url_for('watchlist.index', sort='last_changed', order=link_order, tag=active_tag_uuid)}}"><span class="hide-on-mobile">{{ _('Last') }}</span> {{ _('Changed') }} <span class='arrow {{link_order}}'></span></a></th>
<th><a class="{{ 'active '+link_order if sort_attribute == 'last_checked' else 'inactive' }}" href="{{url_for('watchlist.index', sort='last_checked', order=link_order, tag=active_tag_uuid)}}"><span class="hide-on-mobile">{{ _('Last Checked') }}</span><span class="hide-on-desktop">{{ _('Checked') }}</span> <span class='arrow {{link_order}}'></span></a></th>
<th><a class="{{ 'active '+link_order if sort_attribute == 'last_changed' else 'inactive' }}" href="{{url_for('watchlist.index', sort='last_changed', order=link_order, tag=active_tag_uuid)}}"><span class="hide-on-mobile">{{ _('Last Changed') }}</span><span class="hide-on-desktop">{{ _('Changed') }}</span> <span class='arrow {{link_order}}'></span></a></th>
<th class="empty-cell"></th>
</tr>
</thead>
+5
View File
@@ -981,6 +981,11 @@ def changedetection_app(config=None, datastore_o=None):
"queued_data": all_queued
})
if strtobool(os.getenv('HISTORY_SNAPSHOT_FILE_ALLOW_OUTSIDE_WATCH_DATADIR', 'False')):
logger.warning("SECURITY WARNING: HISTORY_SNAPSHOT_FILE_ALLOW_OUTSIDE_WATCH_DATADIR is enabled — "
"snapshot reads are NOT confined to the watch data directory. "
"This disables protection against path traversal via restored backups (GHSA-8757-69j2-hx56).")
# Start the async workers during app initialization
# Can be overridden by ENV or use the default settings
n_workers = int(os.getenv("FETCH_WORKERS", datastore.data['settings']['requests']['workers']))
+2 -2
View File
@@ -282,7 +282,7 @@ def xpath_filter(xpath_filter, html_content, append_pretty_line_formatting=False
try:
if is_xml:
# So that we can keep CDATA for cdata_in_document_to_text() to process
parser = etree.XMLParser(strip_cdata=False)
parser = etree.XMLParser(strip_cdata=False, resolve_entities=False, no_network=True)
# For XML/RSS content, use etree.fromstring to properly handle XML declarations
tree = etree.fromstring(html_content.encode('utf-8') if isinstance(html_content, str) else html_content, parser=parser)
else:
@@ -346,7 +346,7 @@ def xpath1_filter(xpath_filter, html_content, append_pretty_line_formatting=Fals
try:
if is_xml:
# So that we can keep CDATA for cdata_in_document_to_text() to process
parser = etree.XMLParser(strip_cdata=False)
parser = etree.XMLParser(strip_cdata=False, resolve_entities=False, no_network=True)
# For XML/RSS content, use etree.fromstring to properly handle XML declarations
tree = etree.fromstring(html_content.encode('utf-8') if isinstance(html_content, str) else html_content, parser=parser)
else:
+23 -15
View File
@@ -465,22 +465,21 @@ class model(EntityPersistenceMixin, watch_base):
if ',' in i:
k, v = i.strip().split(',', 2)
# The index history could contain a relative path, so we need to make the fullpath
# so that python can read it
# Cross-platform: check for any path separator (works on Windows and Unix)
if os.sep not in v and '/' not in v and '\\' not in v:
# Relative filename only, no path separators
v = os.path.join(self.data_dir, v)
else:
# It's possible that they moved the datadir on older versions
# So the snapshot exists but is in a different path
# Cross-platform: use os.path.basename instead of split('/')
snapshot_fname = os.path.basename(v)
proposed_new_path = os.path.join(self.data_dir, snapshot_fname)
if not os.path.exists(v) and os.path.exists(proposed_new_path):
v = proposed_new_path
# Always resolve history entries to within the watch's own data directory.
# Entries restored from backup could contain absolute or traversal paths —
# never trust them. Use realpath to also block symlink-based escapes.
safe_data_dir = os.path.realpath(self.data_dir)
snapshot_fname = os.path.basename(v.strip())
resolved_path = os.path.realpath(os.path.join(self.data_dir, snapshot_fname))
tmp_history[k] = v
if not resolved_path.startswith(safe_data_dir + os.sep) and resolved_path != safe_data_dir:
logger.warning(f"Skipping unsafe history entry for {self.get('uuid')}: {v!r}")
continue
if not os.path.exists(resolved_path):
continue
tmp_history[k] = resolved_path
if len(tmp_history):
self.__newest_history_key = list(tmp_history.keys())[-1]
@@ -563,6 +562,15 @@ class model(EntityPersistenceMixin, watch_base):
if not filepath:
filepath = self.history[timestamp]
# Confine every read to the watch's own data directory — defence in depth
# against any path that bypasses the history parser (e.g. direct filepath= callers).
# Set HISTORY_SNAPSHOT_FILE_ALLOW_OUTSIDE_WATCH_DATADIR=true to disable (not recommended).
if self.data_dir and not strtobool(os.getenv('HISTORY_SNAPSHOT_FILE_ALLOW_OUTSIDE_WATCH_DATADIR', 'False')):
safe_data_dir = os.path.realpath(self.data_dir)
resolved = os.path.realpath(filepath)
if not (resolved.startswith(safe_data_dir + os.sep) or resolved == safe_data_dir):
raise PermissionError(f"Snapshot path {filepath!r} is outside the watch data directory")
# Check if binary file (image, PDF, etc.)
# Binary files are NEVER saved with .br compression, only text files are
binary_extensions = ('.png', '.jpg', '.jpeg', '.gif', '.webp', '.pdf', '.bin', '.jfif')
+18 -9
View File
@@ -3,6 +3,13 @@
* Provides accessible, animated confirmation dialogs
*/
// Escapes a string for safe insertion via innerHTML
function _modalEscapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
const ModalDialog = {
/**
* Show a confirmation dialog
@@ -125,9 +132,10 @@ const ModalDialog = {
* @param {Function} onConfirm - Callback when confirmed
*/
confirmDelete: function(itemName, onConfirm) {
const safeName = _modalEscapeHTML(itemName);
return this.confirm({
title: 'Delete ' + itemName + '?',
message: `<p>Are you sure you want to delete <strong>${itemName}</strong>?</p><p>This action cannot be undone.</p>`,
title: 'Delete ' + safeName + '?',
message: `<p>Are you sure you want to delete <strong>${safeName}</strong>?</p><p>This action cannot be undone.</p>`,
type: 'danger',
confirmText: 'Delete',
cancelText: 'Cancel',
@@ -141,9 +149,10 @@ const ModalDialog = {
* @param {Function} onConfirm - Callback when confirmed
*/
confirmUnlink: function(itemName, onConfirm) {
const safeName = _modalEscapeHTML(itemName);
return this.confirm({
title: 'Unlink ' + itemName + '?',
message: `<p>Are you sure you want to unlink all watches from <strong>${itemName}</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>`,
title: 'Unlink ' + safeName + '?',
message: `<p>Are you sure you want to unlink all watches from <strong>${safeName}</strong>?</p><p>The tag will be kept but watches will be removed from it.</p>`,
type: 'warning',
confirmText: 'Unlink',
cancelText: 'Cancel',
@@ -172,11 +181,11 @@ $(document).ready(function() {
const url = $element.attr('href');
const config = {
type: $element.data('confirm-type') || 'danger',
title: $element.data('confirm-title') || 'Confirm Action',
message: $element.data('confirm-message') || '<p>Are you sure you want to proceed?</p>',
confirmText: $element.data('confirm-button') || 'Confirm',
cancelText: $element.data('cancel-button') || 'Cancel',
type: $element.attr('data-confirm-type') || 'danger',
title: $element.attr('data-confirm-title') || 'Confirm Action',
message: $element.attr('data-confirm-message') || '<p>Are you sure you want to proceed?</p>',
confirmText: $element.attr('data-confirm-button') || 'Confirm',
cancelText: $element.attr('data-cancel-button') || 'Cancel',
onConfirm: function() {
// If it's a link, navigate to the URL
if ($element.is('a')) {
@@ -180,4 +180,10 @@ $grid-gap: 0.5rem;
.pure-table td {
padding: 3px !important;
}
}
@media (min-width: 768px) {
.watch-table thead tr th .hide-on-desktop {
display: none;
}
}
File diff suppressed because one or more lines are too long
@@ -52,10 +52,6 @@
<td><code>{{ '{{diff_url}}' }}</code></td>
<td>{{ _('The URL of the diff output for the watch.') }}</td>
</tr>
<tr>
<td><code>{{ '{{diff_url}}' }}</code></td>
<td>{{ _('The URL of the diff output for the watch.') }}</td>
</tr>
<tr>
<td><code>{{ '{{diff}}' }}</code></td>
<td>{{ _('The diff output - only changes, additions, and removals') }}<br>
@@ -179,15 +179,6 @@
</div>
{% endmacro %}
{% macro playwright_warning() %}
<p><strong>{{ _('Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled.') }}</strong> {{ _('Alternatively try our') }} <a href="https://changedetection.io">{{ _('very affordable subscription based service which has all this setup for you') }}</a>.</p>
<p>{{ _('You may need to <a href="%(url_pinned)s">Enable playwright environment variable</a> and uncomment the <strong>sockpuppetbrowser</strong> in the <a href="%(url_master)s">docker-compose.yml</a> file.',
url_pinned='https://github.com/dgtlmoon/changedetection.io/blob/09ebc6ec6338545bdd694dc6eee57f2e9d2b8075/docker-compose.yml#L31',
url_master='https://github.com/dgtlmoon/changedetection.io/blob/master/docker-compose.yml')|safe }}</p>
<br>
{% endmacro %}
{% macro render_time_schedule_form(form, available_timezones, timezone_default_config) %}
<style>
.day-schedule *, .day-schedule select {
+76
View File
@@ -1,7 +1,11 @@
import io
import os
import re
import time
import pytest
from flask import url_for
from zipfile import ZipFile, ZIP_DEFLATED
from changedetectionio.tests.util import set_modified_response
from .util import live_server_setup, wait_for_all_checks, delete_all_watches
@@ -823,3 +827,75 @@ def test_unresolvable_hostname_is_allowed(client, live_server, monkeypatch):
res = client.get(url_for('watchlist.index'))
assert b'this-host-does-not-exist-xyz987.invalid' in res.data, \
"Unresolvable hostname watch should appear in the watch overview list"
def test_ghsa_8757_69j2_hx56_backup_restore_history_path_traversal(client, live_server, measure_memory_usage, datastore_path):
"""
GHSA-8757-69j2-hx56: Crafted backup ZIP with absolute path in history.txt must not
expose arbitrary local files through the preview or API endpoints.
Attack chain:
1. Attacker creates a backup ZIP with a malicious history.txt containing an absolute
path (e.g. /etc/passwd) as a snapshot reference.
2. Victim restores the backup.
3. Attacker reads the targeted file via the Preview page.
The fix ensures history entries are always resolved to os.path.basename() joined with
the watch's data_dir, and rejects entries that escape that directory.
"""
set_original_response(datastore_path=datastore_path)
datastore = live_server.app.config['DATASTORE']
watch_url = url_for('test_endpoint', _external=True)
# Create a real watch and trigger a check so we have a valid backup structure
uuid = datastore.add_watch(url=watch_url)
client.get(url_for("ui.form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
# Download a legitimate backup to use as a template
client.get(url_for("backups.request_backup"), follow_redirects=True)
time.sleep(4)
res = client.get(url_for("backups.download_backup", filename="latest"), follow_redirects=True)
assert res.content_type == "application/zip"
# Tamper: replace the history.txt inside the backup with a malicious entry
# that points at /etc/passwd (a file that exists on any Unix system)
original_zip = ZipFile(io.BytesIO(res.data))
tampered_buf = io.BytesIO()
with ZipFile(tampered_buf, 'w', ZIP_DEFLATED) as new_zip:
for item in original_zip.infolist():
data = original_zip.read(item.filename)
# Replace the watch's history.txt with a malicious absolute path entry
if item.filename.endswith('history.txt') and uuid in item.filename:
data = b'1776969105,/etc/passwd\n'
new_zip.writestr(item, data)
tampered_buf.seek(0)
tampered_zip_data = tampered_buf.read()
# Restore the tampered backup
res = client.post(
url_for("backups.restore.backups_restore_start"),
data={
'zip_file': (io.BytesIO(tampered_zip_data), 'malicious_backup.zip'),
'include_watches': 'y',
'include_watches_replace_existing': 'y',
},
content_type='multipart/form-data',
follow_redirects=True
)
assert res.status_code == 200
time.sleep(2)
# Now try to read the /etc/passwd contents via the Preview page using the injected timestamp
res = client.get(
url_for("ui.ui_preview.preview_page", uuid=uuid) + "?timestamp=1776969105",
follow_redirects=True
)
# The preview must NOT contain typical /etc/passwd content
assert b'root:' not in res.data, \
"Preview must not expose /etc/passwd — history path traversal not blocked"
assert b'/bin/' not in res.data or b'No history' in res.data or res.status_code in [404, 500], \
"Preview must not serve arbitrary local files from a malicious history entry"
@@ -311,5 +311,72 @@ class TestLLMDiffSummaryCache(unittest.TestCase):
assert watch.get_llm_diff_summary('1000', '2000', prompt=self.PROMPT) == 'Updated summary'
class TestHistoryPathTraversal(unittest.TestCase):
"""GHSA-8757-69j2-hx56: history.txt must not allow reads outside the watch data dir."""
def _make_watch(self):
mock_datastore = {'settings': {'application': {}}, 'watching': {}}
watch = Watch.model(datastore_path='/tmp', __datastore=mock_datastore, default={})
watch.ensure_data_dir_exists()
return watch
def _write_history_txt(self, watch, lines):
"""Directly write raw lines to history.txt to simulate a restored backup."""
fname = os.path.join(watch.data_dir, watch.history_index_filename)
with open(fname, 'w', encoding='utf-8') as f:
f.writelines(lines)
def test_absolute_path_in_history_is_rejected(self):
"""An absolute path like /etc/passwd must not appear in history."""
watch = self._make_watch()
self._write_history_txt(watch, ['1000000000,/etc/passwd\n'])
history = watch.history
self.assertEqual(history, {}, "Absolute path entry must be rejected")
def test_traversal_path_in_history_is_rejected(self):
"""A relative traversal path like ../../etc/passwd must not appear in history."""
watch = self._make_watch()
self._write_history_txt(watch, ['1000000000,../../etc/passwd\n'])
history = watch.history
self.assertEqual(history, {}, "Path traversal entry must be rejected")
def test_normal_snapshot_entry_is_accepted(self):
"""A bare filename written by save_history_blob must still load correctly."""
import uuid as uuid_builder
watch = self._make_watch()
watch.save_history_blob(contents="hello world", timestamp=1000000000, snapshot_id=str(uuid_builder.uuid4()))
history = watch.history
self.assertEqual(len(history), 1, "Normal snapshot entry must be accepted")
self.assertTrue(
list(history.values())[0].startswith(watch.data_dir),
"Resolved path must be inside the watch data directory"
)
def test_get_history_snapshot_blocks_outside_path_directly(self):
"""get_history_snapshot(filepath=...) must raise if the path escapes data_dir."""
watch = self._make_watch()
with self.assertRaises(PermissionError):
watch.get_history_snapshot(filepath='/etc/passwd')
def test_get_history_snapshot_blocks_traversal_directly(self):
"""get_history_snapshot(filepath=...) must raise on ../../ traversal paths."""
watch = self._make_watch()
with self.assertRaises(PermissionError):
watch.get_history_snapshot(filepath=os.path.join(watch.data_dir, '../../etc/passwd'))
def test_resolved_path_stays_inside_data_dir(self):
"""All resolved history paths must reside within the watch's data_dir."""
import uuid as uuid_builder
watch = self._make_watch()
for ts in [1000000001, 1000000002, 1000000003]:
watch.save_history_blob(contents=f"content {ts}", timestamp=ts, snapshot_id=str(uuid_builder.uuid4()))
safe_dir = os.path.realpath(watch.data_dir)
for path in watch.history.values():
self.assertTrue(
os.path.realpath(path).startswith(safe_dir),
f"Path {path!r} escapes the watch data directory"
)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
# run from dir above changedetectionio/ dir
# python3 -m pytest changedetectionio/tests/unit/test_xml_security.py
import pytest
from changedetectionio import html_tools
def _xxe_payload(file_path: str) -> str:
return f"""<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file://{file_path}">
]>
<root><item>&xxe;</item></root>"""
def test_xxe_not_expanded_xpath_filter(tmp_path):
"""xpath_filter must not expand external entities (CVE-2026-41895)."""
sentinel_file = tmp_path / "sentinel.txt"
sentinel = "xxe_sentinel_should_never_appear_in_output"
sentinel_file.write_text(sentinel)
result = html_tools.xpath_filter("//item", _xxe_payload(sentinel_file), is_xml=True)
assert sentinel not in result
def test_xxe_not_expanded_xpath1_filter(tmp_path):
"""xpath1_filter must not expand external entities (CVE-2026-41895)."""
sentinel_file = tmp_path / "sentinel.txt"
sentinel = "xxe_sentinel_should_never_appear_in_output"
sentinel_file.write_text(sentinel)
result = html_tools.xpath1_filter("//item", _xxe_payload(sentinel_file), is_xml=True)
assert sentinel not in result
+194 -63
View File
@@ -1,103 +1,234 @@
# Translation Guide
# Translators Guide
## Updating Translations
This document is for contributors who write templates (HTML) and for translators who maintain `.po` files.
It exists because fragmented `msgid`s — splitting a single sentence across multiple `_()` calls — cause
systematic translation breakage across many languages. Follow the patterns here to prevent that.
To maintain consistency and minimize unnecessary changes in translation files, run these commands:
---
```bash
python setup.py extract_messages # Extract translatable strings
python setup.py update_catalog # Update all language files
python setup.py compile_catalog # Compile to binary .mo files
## Terminology
- **Always use "monitor" or "watcher"** for the concept of watching a URL — never the bare word "watch",
which translates to "clock" (e.g. `hodinky` in Czech, `시계` in Korean, `時計` in Japanese).
- Use the **shortest suitable wording** for each language. If a language naturally uses the English
derivative, prefer that.
---
## Template rules: do not fragment `msgid`s
### Why fragments break translation
The GNU gettext manual is explicit on this:
> **[Entire sentences](https://www.gnu.org/software/gettext/manual/html_node/Entire-sentences.html)**:
> Translatable strings should be entire sentences. Because gender/number declension depends on other
> parts of the sentence, half-sentence *"dumb string concatenation"* breaks in many languages other than English.
> **[No string concatenation](https://www.gnu.org/software/gettext/manual/html_node/No-string-concatenation.html)**:
> Placing adjacent `_()` calls is semantically equivalent to runtime `strcat` concatenation, so the same
> guideline applies. The manual also notes that "in some languages the translator might want to swap the
> order" of components.
> **[No embedded URLs](https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html)**:
> URLs should not be written directly inside `msgid`s; they should be injected via `%(name)s` placeholders
> and values passed as kwargs.
> **[No unusual markup](https://www.gnu.org/software/gettext/manual/html_node/No-unusual-markup.html)**:
> "HTML markup, however, is common enough that it's probably ok to use in translatable strings."
Fragments break differently depending on language family:
| Language family | How fragmentation breaks it |
|---|---|
| SOV (Japanese, Korean, Turkish) | Verb-final word order can't be achieved when verb and subject are in separate fragments |
| Germanic (German) | Gender/case agreement between article and noun is lost across fragment boundaries |
| Romance (French, Spanish, Italian, Portuguese) | Adjective placement, subjunctive mood, verb agreement can't be maintained |
| Slavic (Czech, Ukrainian) | Case (driven by preposition/verb relationships) is easy to get wrong |
| CJK (Chinese, Japanese, Korean) | Modifier position and SVO-vs-topic-prominent differences can't be applied at fragment level |
A past workaround was redistributing translations across adjacent fragments and using `msgstr " "` (a
single space) to suppress unused fragments. This is fragile: as soon as the same short `msgid` is reused
in a different template, the redistributed translation is applied verbatim and breaks the new context.
---
## The four correct patterns
### Pattern 1 — Inline HTML embedding
Keep markup **inside** the `msgid`. Render with `| safe`. This also lets CJK translators decide how to
handle `<i>` (see CJK section below).
```jinja
{# BAD: three fragments; CJK translators can't see the <i> at all #}
{{ _('Helps reduce changes detected caused by sites shuffling lines around, combine with') }}
<i>{{ _('check unique lines') }}</i>
{{ _('below.') }}
{# GOOD: one msgid, rendered with |safe #}
{{ _('Helps reduce changes detected caused by sites shuffling lines around, combine with <i>check unique lines</i> below.') | safe }}
```
## Configuration
### Pattern 2 — URL as kwarg
All translation settings are configured in **`../../setup.cfg`** (single source of truth).
Pass URLs via `%(name)s` so translators can freely reorder them.
The configuration below is shown for reference - **edit `setup.cfg` to change settings**:
```jinja
{# BAD: URL hardcoded between three fragments #}
{{ _('Use') }}
<a target="newwindow" href="https://github.com/caronc/apprise">{{ _('AppRise Notification URLs') }}</a>
{{ _('for notification to just about any service!') }}
{# GOOD: URL passed as kwarg, <a> embedded in the msgid #}
{{ _('Use <a target="newwindow" href="%(url)s">AppRise Notification URLs</a> for notification to just about any service!',
url='https://github.com/caronc/apprise') | safe }}
```
### Pattern 3 — Literal `{{}}` escape as kwarg
Jinja2 would double-interpolate `{{token}}` inside a `_()` call. Pass it as a kwarg instead.
```jinja
{# BAD: literal {{token}} in the middle forces splitting #}
{{ _('Accepts the') }} <code>{{ '{{token}}' }}</code> {{ _('placeholders listed below') }}
{# GOOD: literal passed as kwarg; msgid stays as an entire sentence #}
{{ _('Accepts the <code>%(token)s</code> placeholders listed below', token='{{token}}') | safe }}
```
### Pattern 4 — `{% if %}` outside the `msgid`
Move conditional branches outside `_()` so each branch is a complete sentence, not a fragment.
```jinja
{# BAD: three fragments; SOV languages can't reorder %(title)s relative to "URL or Title" #}
{{ _('URL or Title') }}{% if active_tag_uuid %} {{ _('in') }} '{{ active_tag.title }}'{% endif %}
{# GOOD: branch between two complete msgids; each language can freely reorder %(title)s #}
{% if active_tag_uuid %}
{{ _("URL or Title in '%(title)s'", title=active_tag.title) }}
{% else %}
{{ _('URL or Title') }}
{% endif %}
```
---
## CJK italic policy
CJK fonts typically have no true italic cut — `<i>` falls back to a mechanical slant that reduces
legibility. Now that `<i>` is inside `msgid`s, CJK translators can handle it per-locale. Apply this policy
for `ja` / `zh` / `zh_Hant_TW`:
| Context | Action |
|---|---|
| `<i>` used for general emphasis | Replace with `<strong>`, or drop if the emphasis is self-evident |
| `<strong><i>...</i></strong>` | Collapse to `<strong>...</strong>` |
| `<i>` wrapping a UI term (e.g. "check unique lines") | Wrap in locale-conventional quotation marks: 「」 for `ja`/`zh_Hant_TW`, `""` for `zh` |
---
## Translation workflow
**Always use these commands** — they read consistent settings from `setup.cfg` and produce minimal diffs:
```bash
python setup.py extract_messages # Extract translatable strings from source
python setup.py update_catalog # Propagate new msgids to all .po files
python setup.py compile_catalog # Compile .po files to binary .mo files
```
Running `pybabel` directly without the project options causes reordering, rewrapping, and line-number
churn that makes diffs hard to review.
### Configuration
All translation settings are in `setup.cfg` (single source of truth):
```ini
[extract_messages]
# Extract translatable strings from source code
mapping_file = babel.cfg
output_file = changedetectionio/translations/messages.pot
input_paths = changedetectionio
keywords = _ _l gettext
# Options to reduce unnecessary changes in .pot files
sort_by_file = true # Keeps entries ordered by file path
width = 120 # Consistent line width (prevents rewrapping)
add_location = file # Show file path only (not line numbers)
[update_catalog]
# Update existing .po files with new strings from .pot
# Note: 'locale' is omitted - Babel auto-discovers all catalogs in output_dir
input_file = changedetectionio/translations/messages.pot
output_dir = changedetectionio/translations
domain = messages
# Options for consistent formatting
width = 120 # Consistent line width
width = 120
no_fuzzy_matching = true # Avoids incorrect automatic matches
[compile_catalog]
# Compile .po files to .mo binary format
directory = changedetectionio/translations
domain = messages
```
**Key formatting options:**
- `sort_by_file = true` - Orders entries by file path (consistent ordering)
- `width = 120` - Fixed line width prevents text rewrapping
- `add_location = file` - Shows file path only, not line numbers (reduces git churn)
- `no_fuzzy_matching = true` - Prevents incorrect automatic fuzzy matches
---
## Why Use These Commands?
## Multi-language fix process
Running pybabel commands directly without consistent options causes:
- ❌ Entries get reordered differently each time
- ❌ Text gets rewrapped at different widths
- ❌ Line numbers change every edit (if not configured)
- ❌ Large diffs that make code review difficult
When you find a translation error in **any** language, you must check all others for the same `msgid`:
Using `python setup.py` commands ensures:
- ✅ Consistent ordering (by file path, not alphabetically)
- ✅ Consistent line width (120 characters, no rewrapping)
- ✅ File-only locations (no line number churn)
- ✅ No fuzzy matching (prevents incorrect auto-translations)
- ✅ Minimal diffs (only actual changes show up)
- ✅ Easier code review and git history
```bash
for lang in cs de en_GB en_US es fr it ja ko pt_BR tr uk zh zh_Hant_TW; do
echo "=== $lang ===" && grep -A1 'msgid "YourString"' changedetectionio/translations/$lang/LC_MESSAGES/messages.po
done
```
These commands read settings from `../../setup.cfg` automatically.
1. Identify every language with the same problem
2. Fix all affected `.po` files in the same session
3. Recompile: `python setup.py compile_catalog`
## Supported Languages
Never fix one language and move on.
- `cs` - Czech (Čeština)
- `de` - German (Deutsch)
- `en_GB` - English (UK)
- `en_US` - English (US)
- `fr` - French (Français)
- `it` - Italian (Italiano)
- `ja` - Japanese (日本語)
- `ko` - Korean (한국어)
- `pt_BR` - Portuguese (Brasil)
- `zh` - Chinese Simplified (中文简体)
- `zh_Hant_TW` - Chinese Traditional (繁體中文)
---
## Adding a New Language
## Supported languages
1. Initialize the new language catalog:
```bash
pybabel init -i changedetectionio/translations/messages.pot -d changedetectionio/translations -l NEW_LANG_CODE
```
2. Compile it:
```bash
python setup.py compile_catalog
```
| Code | Language |
|---|---|
| `cs` | Czech (Čeština) |
| `de` | German (Deutsch) |
| `en_GB` | English (UK) |
| `en_US` | English (US) |
| `es` | Spanish (Español) |
| `fr` | French (Français) |
| `it` | Italian (Italiano) |
| `ja` | Japanese (日本語) |
| `ko` | Korean (한국어) |
| `pt_BR` | Portuguese (Brasil) |
| `tr` | Turkish (Türkçe) |
| `uk` | Ukrainian (Українська) |
| `zh` | Chinese Simplified (中文简体) |
| `zh_Hant_TW` | Chinese Traditional (繁體中文) |
Babel will auto-discover the new language on subsequent translation updates.
## Adding a new language
## Translation Notes
```bash
pybabel init -i changedetectionio/translations/messages.pot \
-d changedetectionio/translations \
-l NEW_LANG_CODE
# Reset POT-Creation-Date to the sentinel so it matches the other catalogs
sed -i 's|^"POT-Creation-Date: .*\\n"$|"POT-Creation-Date: 1970-01-01 00:00+0000\\n"|' \
changedetectionio/translations/NEW_LANG_CODE/LC_MESSAGES/messages.po
python setup.py compile_catalog
```
From CLAUDE.md:
- Always use "monitor" or "watcher" terminology (not "clock")
- Use the most brief wording suitable
- When finding issues in one language, check ALL languages for the same issue
Babel auto-discovers the new language on subsequent runs.
---
## CI linter
A GitHub Actions job (`lint-template-i18n`) checks for adjacent `{{ _(...) }}` calls on the same line
separated only by HTML — the primary symptom of fragmented `msgid`s. It enforces a declining baseline:
the count of existing violations may only go down, never up. When you fix a template, lower the
`BASELINE_LIMIT` in `.github/workflows/test-only.yml` by the number of lines you fixed.
See [issue #4074](https://github.com/dgtlmoon/changedetection.io/issues/4074) for full background and
[PR #4076](https://github.com/dgtlmoon/changedetection.io/pull/4076) for worked consolidation examples.
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-02 11:40+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: cs\n"
@@ -769,6 +769,12 @@ msgstr "Zpět"
msgid "Clear Snapshot History"
msgstr "Vymazat historii snímků"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Využití"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -785,10 +791,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2215,13 +2217,17 @@ msgid "Checked"
msgstr "Zkontrolováno"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Poslední"
msgid "Last Checked"
msgstr "Poslední Zkontrolováno"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Změněno"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Poslední Změněno"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Nejsou nakonfigurována žádná sledování webových stránek, do výše uvedeného pole přidejte adresu URL nebo"
@@ -2270,18 +2276,10 @@ msgstr "Cena"
msgid "No information"
msgstr "Žádné informace"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Probíhá kontrola"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "Ve frontě"
@@ -3695,25 +3693,6 @@ msgstr ""
msgid "Verify this rule against current snapshot"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr ""
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr ""
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-14 03:57+0100\n"
"Last-Translator: \n"
"Language: de\n"
@@ -785,6 +785,12 @@ msgstr "Zurück"
msgid "Clear Snapshot History"
msgstr "Snapshot-Verlauf löschen"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Nutzung"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -801,10 +807,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2266,13 +2268,17 @@ msgid "Checked"
msgstr "Geprüft"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Zuletzt"
msgid "Last Checked"
msgstr "Zuletzt Geprüft"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Geändert"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Zuletzt Geändert"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Es sind keine Website-Überwachungen konfiguriert. Bitte fügen Sie im Feld oben eine URL hinzu, oder"
@@ -2321,18 +2327,10 @@ msgstr "Preis"
msgid "No information"
msgstr "Keine Informationen"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Jetzt prüfen"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "Wartend"
@@ -3749,29 +3747,6 @@ msgstr "Diese Zeile/Regel entfernen"
msgid "Verify this rule against current snapshot"
msgstr "Überprüfen Sie diese Regel anhand des aktuellen Snapshots."
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
"Fehler Diese Überwachung benötigt Chrome (mit Playwright/Sockpuppetbrowser), aber das Abrufen über Chrome ist nicht"
" aktiviert."
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr "Alternativ können Sie auch unser"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "ein sehr günstiges Abonnement-basiertes Angebot, das all diese Einstellungen für Sie übernimmt"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"Sie müssen möglicherweise <a href=\"%(url_pinned)s\">Aktivieren der Umgebungsvariable „Playwright“</a> und entferne "
"den Kommentar von <strong>sockpuppetbrowser</strong> in der <a href=\"%(url_master)s\">docker-compose.yml</a> Datei."
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "Legen Sie einen Stunden-/Wochenplan fest."
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io\n"
"Report-Msgid-Bugs-To: https://github.com/dgtlmoon/changedetection.io\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-12 16:33+0100\n"
"Last-Translator: British English Translation Team\n"
"Language: en_GB\n"
@@ -767,6 +767,12 @@ msgstr ""
msgid "Clear Snapshot History"
msgstr ""
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -783,10 +789,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2211,13 +2213,17 @@ msgid "Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr ""
@@ -2266,18 +2272,10 @@ msgstr ""
msgid "No information"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr ""
@@ -3689,25 +3687,6 @@ msgstr ""
msgid "Verify this rule against current snapshot"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr ""
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr ""
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: https://github.com/dgtlmoon/changedetection.io\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-12 16:37+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en_US\n"
@@ -767,6 +767,12 @@ msgstr ""
msgid "Clear Snapshot History"
msgstr ""
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -783,10 +789,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2211,13 +2213,17 @@ msgid "Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr ""
@@ -2266,18 +2272,10 @@ msgstr ""
msgid "No information"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr ""
@@ -3689,25 +3687,6 @@ msgstr ""
msgid "Verify this rule against current snapshot"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr ""
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr ""
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.53.6\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-03-20 18:13+0100\n"
"Last-Translator: Adrian Gonzalez <adrian@example.com>\n"
"Language: es\n"
@@ -803,6 +803,12 @@ msgstr "Atrás"
msgid "Clear Snapshot History"
msgstr "Borrar historial de instantáneas"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Uso"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -819,10 +825,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2280,13 +2282,17 @@ msgid "Checked"
msgstr "Comprobado"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Último"
msgid "Last Checked"
msgstr "Último Comprobado"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Cambiadp"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Último Cambiadp"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "No hay monitores de detección de cambios de página web configuradas; agregue una URL en el cuadro de arriba, o"
@@ -2335,18 +2341,10 @@ msgstr "Precio"
msgid "No information"
msgstr "Sin información"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Comprobando ahora"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "En cola"
@@ -3764,29 +3762,6 @@ msgstr "Eliminar esta fila/regla"
msgid "Verify this rule against current snapshot"
msgstr "Verifique esta regla con la instantánea actual"
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
"Error: este monitor necesita Chrome (con playwright/sockpuppetbrowser), pero la obtención basada en Chrome no está "
"habilitada."
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr "Alternativamente prueba nuestro"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "servicio basado en suscripción muy asequible que tiene toda esta configuración para usted."
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"Es posible que necesites <a href=\"%(url_pinned)s\">Habilitar la variable de entorno de playwright</a> y descomentar "
"el <strong>sockpuppetbrowser</strong> en el fichero <a href=\"%(url_master)s\">docker-compose.yml</a>."
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "Establecer un horario por horas/días de la semana"
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-02 11:40+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
@@ -773,6 +773,12 @@ msgstr "Retour"
msgid "Clear Snapshot History"
msgstr "Effacer/réinitialiser l'historique"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Utilisation"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -789,10 +795,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2222,13 +2224,17 @@ msgid "Checked"
msgstr "Vérification"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Dernier"
msgid "Last Checked"
msgstr "Dernier Vérification"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Modifié"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Dernier Modifié"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Aucune surveillance de site Web configurée, veuillez ajouter une URL dans la case ci-dessus, ou"
@@ -2277,18 +2283,10 @@ msgstr "Prix"
msgid "No information"
msgstr "Aucune information"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Vérifier maintenant"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "En file d'attente"
@@ -3702,25 +3700,6 @@ msgstr ""
msgid "Verify this rule against current snapshot"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr ""
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr ""
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-02 15:32+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: it\n"
@@ -769,6 +769,12 @@ msgstr "Indietro"
msgid "Clear Snapshot History"
msgstr "Cancella cronologia snapshot"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Utilizzo"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -785,10 +791,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2213,13 +2215,17 @@ msgid "Checked"
msgstr "Controllo"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Ultimo"
msgid "Last Checked"
msgstr "Ultimo Controllo"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Modifica"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Ultimo Modifica"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Nessun monitoraggio configurato, aggiungi un URL nella casella sopra, oppure"
@@ -2268,18 +2274,10 @@ msgstr "Prezzo"
msgid "No information"
msgstr "Nessuna informazione"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Controllo in corso"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "In coda"
@@ -3691,25 +3689,6 @@ msgstr ""
msgid "Verify this rule against current snapshot"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr ""
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr ""
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.53.6\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-03-31 23:52+0900\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ja\n"
@@ -774,6 +774,12 @@ msgstr "戻る"
msgid "Clear Snapshot History"
msgstr "スナップショット履歴をクリア"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "使用量"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -790,10 +796,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2230,13 +2232,17 @@ msgid "Checked"
msgstr "チェック済み"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "最終"
msgid "Last Checked"
msgstr "前回チェック"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "変更済み"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "前回更新"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "ウェブページ変更検知ウォッチが設定されていません。上のボックスにURLを追加するか、"
@@ -2285,18 +2291,10 @@ msgstr "価格"
msgid "No information"
msgstr "情報なし"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr "前回チェック"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "今すぐチェック"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "前回更新"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "キュー済み"
@@ -3722,27 +3720,6 @@ msgstr "この行/ルールを削除"
msgid "Verify this rule against current snapshot"
msgstr "現在のスナップショットに対してこのルールを検証"
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr "エラー - このウォッチは Chromeplaywright/sockpuppetbrowser付き)が必要ですが、Chromeベースの取得が有効になっていません。"
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr "または以下をお試しください:"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "すべての設定が完了した手頃な価格のサブスクリプションサービス"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"<a href=\"%(url_pinned)s\">playwright 環境変数の有効化</a>と <a href=\"%(url_master)s\">docker-compose.yml</a> ファイル内の "
"<strong>sockpuppetbrowser</strong> のコメント解除が必要な場合があります。"
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "時間/曜日スケジュールを設定"
File diff suppressed because it is too large Load Diff
+13 -34
View File
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.54.10\n"
"Project-Id-Version: changedetection.io 0.55.2\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 2026-04-28 15:22+1000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -766,6 +766,12 @@ msgstr ""
msgid "Clear Snapshot History"
msgstr ""
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -782,10 +788,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2210,13 +2212,17 @@ msgid "Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr ""
@@ -2265,18 +2271,10 @@ msgstr ""
msgid "No information"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr ""
@@ -3688,25 +3686,6 @@ msgstr ""
msgid "Verify this rule against current snapshot"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr ""
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr ""
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.54.8\n"
"Report-Msgid-Bugs-To: mstrey@gmail.com\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-04-07 22:00-0300\n"
"Last-Translator: Gemini AI\n"
"Language: pt_BR\n"
@@ -792,6 +792,12 @@ msgstr "Voltar"
msgid "Clear Snapshot History"
msgstr "Limpar Histórico de Instantâneos"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Uso"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -808,10 +814,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2259,13 +2261,17 @@ msgid "Checked"
msgstr "Verificado"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Último"
msgid "Last Checked"
msgstr "Último Verificado"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Alterado"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Último Alterado"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Nenhum monitoramento configurado, adicione uma URL na caixa acima ou"
@@ -2314,18 +2320,10 @@ msgstr "Preço"
msgid "No information"
msgstr "Sem informações"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Verificando agora"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "Enfileirado"
@@ -3739,29 +3737,6 @@ msgstr "Remover esta linha/regra"
msgid "Verify this rule against current snapshot"
msgstr "Verificar esta regra contra o instantâneo atual"
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
"Erro - Este monitoramento precisa do Chrome (com playwright/sockpuppetbrowser), mas a busca baseada em Chrome não "
"está ativada."
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr "Alternativamente, tente nosso"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "serviço por assinatura muito acessível que já tem toda essa configuração pronta para você"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"Você pode precisar <a href=\"%(url_pinned)s\">Ativar a variável de ambiente do Playwright</a> e descomentar o "
"<strong>sockpuppetbrowser</strong> no arquivo <a href=\"%(url_master)s\">docker-compose.yml</a>."
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "Definir um agendamento por hora/dia da semana"
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.53.6\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-04-10 20:38+0300\n"
"Last-Translator: \n"
"Language: tr\n"
@@ -802,6 +802,12 @@ msgstr "Geri"
msgid "Clear Snapshot History"
msgstr "Anlık Görüntü Geçmişini Temizle"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Kullanım"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -818,10 +824,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2264,13 +2266,17 @@ msgid "Checked"
msgstr "Kontrol Edildi"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Son"
msgid "Last Checked"
msgstr "Son Kontrol Edildi"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Değiştirildi"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Son Değiştirildi"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Yapılandırılmış web sayfası değişiklik tespiti izleyicisi yok, lütfen yukarıdaki kutuya bir URL ekleyin veya"
@@ -2319,18 +2325,10 @@ msgstr "Fiyat"
msgid "No information"
msgstr "Bilgi yok"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Şimdi kontrol ediliyor"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "Sırada"
@@ -3744,29 +3742,6 @@ msgstr "Bu satırı/kuralı kaldır"
msgid "Verify this rule against current snapshot"
msgstr "Bu kuralı mevcut anlık görüntüye karşı doğrula"
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr ""
"Hata - Bu izleyicinin Chrome'a (playwright/sockpuppetbrowser ile) ihtiyacı var, ancak Chrome tabanlı getirme etkin "
"değil."
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr "Alternatif olarak şunu deneyin"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "sizin için tüm bu kuruluma sahip çok uygun fiyatlı abonelik tabanlı hizmetimiz"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"<a href=\"%(url_pinned)s\">Playwright ortam değişkenini etkinleştirmeniz</a> ve <a href=\"%(url_master)s\">docker-"
"compose.yml</a> dosyası içindeki <strong>sockpuppetbrowser</strong> satırının yorumunu kaldırmanız gerekebilir."
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "Saatlik/hafta içi bir zamanlama ayarla"
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io\n"
"Report-Msgid-Bugs-To: https://github.com/dgtlmoon/changedetection.io\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-02-19 12:30+0100\n"
"Last-Translator: \n"
"Language: uk\n"
@@ -780,6 +780,12 @@ msgstr "Назад"
msgid "Clear Snapshot History"
msgstr "Очистити історію знімків"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "Використання"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -796,10 +802,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2241,13 +2243,17 @@ msgid "Checked"
msgstr "Перевірено"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "Останній"
msgid "Last Checked"
msgstr "Останній Перевірено"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "Змінено"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "Останній Змінено"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "Немає налаштованих завдань для відстеження змін, будь ласка, додайте URL у поле вище або"
@@ -2296,18 +2302,10 @@ msgstr "Ціна"
msgid "No information"
msgstr "Немає інформації"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "Перевірка..."
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "В черзі"
@@ -3721,27 +3719,6 @@ msgstr "Видалити цей рядок/правило"
msgid "Verify this rule against current snapshot"
msgstr "Перевірити це правило на поточному знімку"
#: changedetectionio/templates/_helpers.html
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
msgid "Alternatively try our"
msgstr "Альтернативно спробуйте наш"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "дуже доступний сервіс за передплатою, де все це вже налаштовано для вас"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"Вам може знадобитися <a href=\"%(url_pinned)s\">Увімкнути змінну оточення playwright</a> та розкоментувати "
"<strong>sockpuppetbrowser</strong> у файлі <a href=\"%(url_master)s\">docker-compose.yml</a>."
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "Встановити розклад по годинах/днях тижня"
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-18 21:31+0800\n"
"Last-Translator: 吾爱分享 <admin@wuaishare.cn>\n"
"Language: zh\n"
@@ -771,6 +771,12 @@ msgstr "返回"
msgid "Clear Snapshot History"
msgstr "清除快照历史"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "使用量"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -787,10 +793,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -2216,13 +2218,17 @@ msgid "Checked"
msgstr "检查"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "最近"
msgid "Last Checked"
msgstr "最近检查"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "变更"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "最近变更"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "尚未配置网站监控项,请在上方输入 URL 或"
@@ -2271,18 +2277,10 @@ msgstr "价格"
msgid "No information"
msgstr "暂无信息"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "正在检查"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "队列中"
@@ -3695,27 +3693,6 @@ msgstr "移除此行/规则"
msgid "Verify this rule against current snapshot"
msgstr "使用当前快照验证此规则"
#: changedetectionio/templates/_helpers.html
msgid "Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled."
msgstr "错误 - 该监控项需要 Chromeplaywright/sockpuppetbrowser),但未启用基于 Chrome 的抓取。"
#: changedetectionio/templates/_helpers.html
msgid "Alternatively try our"
msgstr "也可以试试我们的"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "价格实惠的订阅服务,已为你完成全部配置"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"您可能需要<a href=\"%(url_pinned)s\">启用 Playwright 环境变量</a>,并在 <a href=\"%(url_master)s\">docker-compose.yml</a> 文件中取消注释 "
"<strong>sockpuppetbrowser</strong>。"
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "设置按小时/工作日计划"
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-04-25 01:52+0900\n"
"POT-Creation-Date: 1970-01-01 00:00+0000\n"
"PO-Revision-Date: 2026-01-15 12:00+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh_Hant_TW\n"
@@ -770,6 +770,12 @@ msgstr "返回"
msgid "Clear Snapshot History"
msgstr "清除快照歷史記錄"
#. 'Usage' here means token consumption/cost stats for the AI provider, not a how-to guide
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgctxt "AI usage stats"
msgid "Usage"
msgstr "使用量"
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Overview"
msgstr ""
@@ -786,10 +792,6 @@ msgstr ""
msgid "Behaviour"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "Usage"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "AI-powered change monitoring"
msgstr ""
@@ -1427,7 +1429,7 @@ msgstr "已將 1 個監測任務排入複查佇列。"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
msgid "Queued {} watches for rechecking ({} already queued or running)."
msgstr "已將 {} 個監測任務排入複查佇列。"
msgstr "已將 {} 個監測任務排入複查佇列{} 個已在佇列中或正在執行)。"
#: changedetectionio/blueprint/ui/__init__.py
#, python-brace-format
@@ -2215,13 +2217,17 @@ msgid "Checked"
msgstr "檢查"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last"
msgstr "上次"
msgid "Last Checked"
msgstr "上次檢查"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Changed"
msgstr "變更"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr "上次變更"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "No web page change detection watches configured, please add a URL in the box above, or"
msgstr "未設定網站監測任務,請在上方欄位新增 URL,或"
@@ -2270,18 +2276,10 @@ msgstr "價格"
msgid "No information"
msgstr "無資訊"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Checked"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html changedetectionio/templates/base.html
msgid "Checking now"
msgstr "正在檢查"
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Last Changed"
msgstr ""
#: changedetectionio/blueprint/watchlist/templates/watch-overview.html
msgid "Queued"
msgstr "已排程"
@@ -3693,27 +3691,6 @@ msgstr "移除此行 / 規則"
msgid "Verify this rule against current snapshot"
msgstr "針對目前快照驗證此規則"
#: changedetectionio/templates/_helpers.html
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
msgid "Alternatively try our"
msgstr "或者嘗試我們"
#: changedetectionio/templates/_helpers.html
msgid "very affordable subscription based service which has all this setup for you"
msgstr "非常實惠的訂閱服務,為您準備好所有設定"
#: changedetectionio/templates/_helpers.html
#, python-format
msgid ""
"You may need to <a href=\"%(url_pinned)s\">Enable playwright environment variable</a> and uncomment the "
"<strong>sockpuppetbrowser</strong> in the <a href=\"%(url_master)s\">docker-compose.yml</a> file."
msgstr ""
"您可能需要 <a href=\"%(url_pinned)s\">啟用 playwright 環境變數</a> 並取消註解 <strong>sockpuppetbrowser</strong> 在 <a "
"href=\"%(url_master)s\">docker-compose.yml</a> 檔案."
#: changedetectionio/templates/_helpers.html
msgid "Set a hourly/week day schedule"
msgstr "設定每小時 / 平日排程"
+1
View File
@@ -22,6 +22,7 @@ domain = messages
# Options for consistent formatting
width = 120
no_fuzzy_matching = true
ignore_pot_creation_date = true
ignore_obsolete = true
[compile_catalog]