diff --git a/.github/workflows/test-only.yml b/.github/workflows/test-only.yml index 04958b7e..246d254c 100644 --- a/.github/workflows/test-only.yml +++ b/.github/workflows/test-only.yml @@ -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' diff --git a/README.md b/README.md index 4eedf4e9..db10fe57 100644 --- a/README.md +++ b/README.md @@ -352,4 +352,6 @@ changedetectionio.html_tools.elementpath_tostring: Copyright (c), 2018-2021, SIS Recognition of fantastic contributors to the project +Developer note: see [translation guide](changedetectionio/translations/README.md) for i18n template patterns and workflow. + - Constantin Hong https://github.com/Constantin1489 diff --git a/changedetectionio/blueprint/ui/templates/edit.html b/changedetectionio/blueprint/ui/templates/edit.html index 73aa0d2e..799133ae 100644 --- a/changedetectionio/blueprint/ui/templates/edit.html +++ b/changedetectionio/blueprint/ui/templates/edit.html @@ -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 %} diff --git a/changedetectionio/templates/_helpers.html b/changedetectionio/templates/_helpers.html index 882373b6..2b30ab00 100644 --- a/changedetectionio/templates/_helpers.html +++ b/changedetectionio/templates/_helpers.html @@ -179,15 +179,6 @@ {% endmacro %} - -{% macro playwright_warning() %} -
{{ _('Error - This watch needs Chrome (with playwright/sockpuppetbrowser), but Chrome based fetching is not enabled.') }} {{ _('Alternatively try our') }} {{ _('very affordable subscription based service which has all this setup for you') }}.
-{{ _('You may need to Enable playwright environment variable and uncomment the sockpuppetbrowser in the docker-compose.yml 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 }}
-