Bläddra i källkod

Add localization support

pull/71/head
Nemoralis 4 månader sedan
committed by Ben Kurtovic
förälder
incheckning
5f118c0cbd
15 ändrade filer med 421 tillägg och 112 borttagningar
  1. +1
    -0
      .gitignore
  2. +118
    -0
      i18n/en.json
  3. +118
    -0
      i18n/qqq.json
  4. +1
    -0
      pyproject.toml
  5. +5
    -1
      src/copyvios/__init__.py
  6. +14
    -10
      src/copyvios/checker.py
  7. +3
    -3
      src/copyvios/templates/error.html.jinja
  8. +25
    -15
      src/copyvios/templates/index.html.jinja
  9. +8
    -8
      src/copyvios/templates/login.html.jinja
  10. +4
    -4
      src/copyvios/templates/logout.html.jinja
  11. +12
    -13
      src/copyvios/templates/settings.html.jinja
  12. +11
    -11
      src/copyvios/templates/support/base.html.jinja
  13. +15
    -15
      src/copyvios/templates/support/cv_form.html.jinja
  14. +39
    -32
      src/copyvios/templates/support/cv_result.html.jinja
  15. +47
    -0
      tool_translations_config.py

+ 1
- 0
.gitignore Visa fil

@@ -8,3 +8,4 @@ __pycache__
logs/*
!logs/.gitinclude
config.py
node_modules/

+ 118
- 0
i18n/en.json Visa fil

@@ -0,0 +1,118 @@
{
"@metadata": {
"authors": [
"Ben Kurtovic"
]
},
"site-title": "Earwig's Copyvio Detector",
"site-header-title": "Earwig's <strong>Copyvio Detector</strong>",
"log-in": "Log in",
"log-out": "Log out",
"settings": "Settings",
"maintained-by": "Maintained by $1",
"api": "API",
"source-code": "Source code",
"background-image": "Background image",
"page-title-search": "Search",
"page-title-compare": "Compare",
"page-title-settings": "Settings",
"page-title-login": "Login",
"page-title-logout": "Logout",
"page-title-error": "Error",
"login-required": "You are required to log in with your Wikimedia account to perform checks with the search engine.",
"login-error": "Error trying to log in: $1",
"login-heading": "Login",
"after-logging-in": "After logging in,",
"check-will-be-run": "your check will be run.",
"will-be-redirected-to": "you will be redirected to:",
"login-button": "Login",
"logout-heading": "Logout",
"logout-message": "Logging out will prevent you from making search engine checks.",
"logout-button": "Logout",
"error-heading": "Error!",
"error-description": "An error occurred. If it hasn't been reported (<a href=\"$1\">try to check</a>), please <a href=\"$2\">file an issue</a> or <a href=\"$3\">email me</a>. Include the following information:",
"error-bad-site-confirm": "Confirm its URL.",
"search-error-retry": "repeat the check without using the search engine",
"settings-heading": "Settings",
"settings-description": "This page contains some configurable options for the copyvio detector. Settings are saved as cookies.",
"settings-default-site": "Default site",
"settings-background": "Background",
"settings-background-list": "Randomly select from <a href=\"$1\">a subset</a> of previous <a href=\"$2\">Wikimedia Commons</a> <a href=\"$3\">Pictures of the Day</a> that work well as widescreen backgrounds, refreshed daily (default).",
"settings-background-potd": "Use the current Commons Picture of the Day, unfiltered. Certain POTDs may be unsuitable as backgrounds due to their aspect ratio or subject matter.",
"settings-background-plain": "Use a plain background.",
"settings-save": "Save",
"settings-updated": "Updated $1.",
"form-site": "Site",
"form-language": "Language",
"form-project": "Project",
"form-page": "Page",
"form-title-placeholder": "Title",
"form-title-tooltip": "Page title",
"form-or": "or",
"form-revision-id-placeholder": "Revision ID",
"form-revision-id-tooltip": "Revision ID",
"form-url-placeholder": "URL",
"form-url-tooltip": "URL to compare",
"form-copyvio-search": "Copyvio search",
"form-copyvio-compare": "Copyvio compare",
"form-use-search-engine": "Use search engine",
"form-use-links": "Use links in page",
"form-use-turnitin": "Use Turnitin",
"form-bypass-cache": "Bypass cache",
"form-submit": "Submit",
"error-bad-action": "Unknown action: <b><span class=\"mono\">$1</span></b>.",
"error-no-search-method": "No copyvio search methods were selected. A check can only be made using the search engine, links present in the page, or some combination of these.",
"error-invalid-oldid": "The revision ID <code>$1</code> is invalid. It should be an integer.",
"error-no-url": "Compare mode requires a URL to be entered. Enter one in the text box below, or choose copyvio search mode to look for content similar to the article elsewhere on the web.",
"error-unsupported-uri": "Unsupported URI scheme: $1.",
"error-no-data": "Couldn't find any text in $1. <i>Note:</i> only HTML documents, plain text pages, and PDFs are supported, and content generated by JavaScript or found inside iframes is ignored.",
"error-timeout": "The URL $1 timed out before any data could be retrieved.",
"error-search-error": "An error occurred while using the search engine ($1). <i>Note:</i> there is a daily limit on the number of search queries the tool is allowed to make. You may $2.",
"error-unknown": "An unknown error occurred.",
"error-bad-site": "The given site (project=<b><span class=\"mono\">$1</span></b>, language=<b><span class=\"mono\">$2</span></b>) doesn't seem to exist. It may also be closed or private. $3",
"error-revision-not-found": "The revision ID couldn't be found: $1.",
"error-page-not-found": "The page couldn't be found: $1.",
"intro-paragraph-1": "This tool attempts to detect $1 in articles. In <i>search mode</i>, it will check for similar content elsewhere on the web using <a href=\"https://developers.google.com/custom-search/\">Google</a>, external links present in the text of the page, or <a href=\"https://en.wikipedia.org/wiki/Wikipedia:Turnitin\">Turnitin</a> (via <a href=\"https://en.wikipedia.org/wiki/User:EranBot\">EranBot</a>), depending on which options are selected. In <i>compare mode</i>, the tool will compare the article to a specific webpage without making additional searches, like the <a href=\"https://dupdet.toolforge.org/\">Duplication Detector</a>.",
"intro-paragraph-1-copyvio-url": "https://en.wikipedia.org/wiki/WP:COPYVIO",
"intro-copyright-violations": "copyright violations",
"intro-paragraph-2": "Running a full check can take up to a minute if other websites are slow or if the tool is under heavy use. Please be patient. If you get a timeout, wait a moment and refresh the page.",
"intro-paragraph-3": "Be aware that other websites can copy from Wikipedia, so check the results carefully, especially for older or well-developed articles. Specific websites can be skipped by adding them to the $1.",
"intro-exclusions-list": "excluded URL list",
"cached-line": "Results <a id=\"cv-cached\" href=\"#\"><abbr title=\"$1\">cached</abbr></a> from $2. Originally generated in $3 seconds.",
"cached-line-search": "Results <a id=\"cv-cached\" href=\"#\"><abbr title=\"$1\">cached</abbr></a> from $2. Originally generated in $3 seconds using {{PLURAL:$4|$4 query|$4 queries}}.",
"cached-tooltip": "To save time (and money), this tool will retain the results of checks for up to 72 hours. This includes the URLs of the checked sources, but neither their content nor the content of the article. Future checks on the same page (assuming it remains unchanged) will not involve additional search queries, but a fresh comparison against the source URL will be made. If the page is modified, a new check will be run.",
"check-results-time": "Results generated in $1 seconds.",
"check-results-time-search": "Results generated in $1 seconds using {{PLURAL:$2|$2 query|$2 queries}}.",
"permalink": "Permalink.",
"violation-suspected": "Violation suspected",
"violation-possible": "Violation possible",
"violation-unlikely": "Violation unlikely",
"no-violation": "No violation",
"similarity": "similarity",
"no-matches-found": "No matches found.",
"redirected-from": "Redirected from $1. $2.",
"redirected-from-check-original": "Check original.",
"attribution-warning": "This article contains an attribution template: <code>{{$1}}</code>. Please verify that any potential copyvios are not from properly attributed sources.",
"turnitin-results": "Turnitin Results",
"turnitin-no-matches": "No matching sources found.",
"turnitin-report": "Report $1",
"turnitin-report-link": "Report $1 for text added at $2:",
"turnitin-source": "$1% of revision text ($2 words) found at $3",
"checked-sources": "Checked Sources",
"table-url": "URL",
"table-similarity": "Similarity",
"table-compare": "Compare",
"source-excluded": "Excluded",
"source-skipped": "Skipped",
"no-sources-checked": "No sources checked.",
"urls-hidden": "{{PLURAL:$1|$1 URL with lower similarity hidden.|$1 URLs with lower similarity hidden.}}",
"urls-hidden-show": "Show them.",
"search-ended-early": "The search ended early because a match was found with high similarity. $1",
"search-ended-early-retry": "Do a complete check.",
"article-label": "Article:",
"source-label": "Source:",
"compare-action": "Compare",
"ago-seconds": "{{PLURAL:$1|$1 second|$1 seconds}} ago",
"ago-minutes": "{{PLURAL:$1|$1 minute|$1 minutes}} ago",
"ago-hours": "{{PLURAL:$1|$1 hour|$1 hours}} ago"
}

+ 118
- 0
i18n/qqq.json Visa fil

@@ -0,0 +1,118 @@
{
"@metadata": {
"authors": [
"Ben Kurtovic"
]
},
"site-title": "Title of the tool shown in the browser tab",
"site-header-title": "Header title of the tool, shown at the top of every page. \"Earwig's\" is the author's username. \"Copyvio Detector\" should be bolded.",
"log-in": "Label for the login link in the header",
"log-out": "Title attribute for the logout link in the header",
"settings": "Label for the settings link in the header",
"maintained-by": "Footer text with a link to the author's user page. $1 is the complete HTML link to the author's user page.",
"api": "Link to the API documentation page",
"source-code": "Link to the source code repository",
"background-image": "Link to the background image description page",
"page-title-search": "Suffix for the browser tab title when viewing a search result",
"page-title-compare": "Suffix for the browser tab title when viewing a compare result",
"page-title-settings": "Suffix for the browser tab title on the settings page",
"page-title-login": "Suffix for the browser tab title on the login page",
"page-title-logout": "Suffix for the browser tab title on the logout page",
"page-title-error": "Suffix for the browser tab title on the error page",
"login-required": "Message explaining that the user needs to log in via OAuth to use the search engine",
"login-error": "Error message displayed when login fails. $1 is the error message.",
"login-heading": "Heading for the login page",
"after-logging-in": "Beginning of a sentence explaining what happens after login",
"check-will-be-run": "Part of a sentence, follows 'after-logging-in'. Used when the user was about to run a search.",
"will-be-redirected-to": "Part of a sentence, follows 'after-logging-in'. Used when the user was on another page.",
"login-button": "Label for the login button",
"logout-heading": "Heading for the logout page",
"logout-message": "Explanation of what logging out does",
"logout-button": "Label for the logout button",
"error-heading": "Heading for the error page",
"error-description": "Instructions for the user when an unexpected error occurs. Contains links to GitHub issues and email. $1 is the GitHub issues list URL, $2 is the new issue URL, $3 is the author's email URL.",
"error-bad-site-confirm": "Link text for confirming a wiki site's URL.",
"search-error-retry": "Link text for retrying a search without the search engine.",
"settings-heading": "Heading for the settings page",
"settings-description": "Explanation of the settings page",
"settings-default-site": "Section heading for default wiki site selection",
"settings-background": "Section heading for background image selection",
"settings-background-list": "Description for the 'list' background option. Contains several links. $1 is the POTD subset URL, $2 is the Wikimedia Commons URL, $3 is the Pictures of the Day URL.",
"settings-background-potd": "Description for the 'POTD' (Picture of the Day) background option",
"settings-background-plain": "Description for the 'plain' background option",
"settings-save": "Label for the save settings button",
"settings-updated": "Confirmation message shown after settings are saved. $1 is a comma-separated list of changed settings.",
"form-site": "Label for the site selection dropdown in the search form",
"form-language": "Title attribute for the language dropdown",
"form-project": "Title attribute for the project dropdown",
"form-page": "Label for the page title input",
"form-title-placeholder": "Placeholder text for the page title input",
"form-title-tooltip": "Tooltip for the page title input",
"form-or": "Separator label between the title and revision ID inputs",
"form-revision-id-placeholder": "Placeholder text for the revision ID input",
"form-revision-id-tooltip": "Tooltip for the revision ID input",
"form-url-placeholder": "Placeholder text for the URL input",
"form-url-tooltip": "Tooltip for the URL input in compare mode",
"form-copyvio-search": "Label for the 'search' radio button, selecting copyvio search mode",
"form-copyvio-compare": "Label for the 'compare' radio button, selecting copyvio compare mode",
"form-use-search-engine": "Label for the checkbox to enable/disable Google search engine",
"form-use-links": "Label for the checkbox to enable/disable checking external links in the page",
"form-use-turnitin": "Label for the checkbox to enable/disable Turnitin integration (currently disabled)",
"form-bypass-cache": "Label for the checkbox to bypass cached results",
"form-submit": "Label for the submit/search button",
"error-bad-action": "Error shown when an unknown action is submitted. $1 is the action name.",
"error-no-search-method": "Error shown when neither search engine nor page links are selected for checking",
"error-invalid-oldid": "Error shown when the revision ID is not a valid integer. $1 is the invalid ID.",
"error-no-url": "Error shown when no URL is provided for compare mode",
"error-unsupported-uri": "Error shown when the URL has an unsupported URI scheme. $1 is the URL.",
"error-no-data": "Error shown when no text could be extracted from a URL. $1 is the URL.",
"error-timeout": "Error shown when a URL times out. $1 is the URL.",
"error-search-error": "Error shown when the search engine fails. $1 is the error cause. $2 is a URL to retry without search engine.",
"error-unknown": "Fallback error message for unrecognized error codes",
"error-bad-site": "Error shown when the wiki site does not exist. $1 is the project code, $2 is the language code, $3 is a URL to confirm.",
"error-revision-not-found": "Error shown when the revision ID is not found. $1 is the URL, $2 is the revision ID.",
"error-page-not-found": "Error shown when the page is not found. $1 is the URL, $2 is the page title.",
"intro-paragraph-1": "First introductory paragraph explaining what the tool does (search mode vs compare mode). $1 is a link to the copyright violations page. Contains several hardcoded links: Google, Turnitin, EranBot, Duplication Detector.",
"intro-paragraph-1-copyvio-url": "URL of the copyright violations page on the local Wikipedia.",
"intro-copyright-violations": "Link text for the copyright violations page (WP:COPYVIO).",
"intro-paragraph-2": "Second introductory paragraph about performance and timeouts",
"intro-paragraph-3": "Third introductory paragraph explaining caution with results and the excluded URL list. $1 is a link to the excluded URL list page.",
"intro-exclusions-list": "Link text for the excluded URL list page.",
"cached-line": "Cached results sentence (compare mode). $1 is the tooltip text for the <abbr> around 'cached'. $2 is a complete '<abbr>' with the time difference (e.g. '1 hour ago'). $3 is the generation time in seconds.",
"cached-line-search": "Cached results sentence (search mode). $1 is the tooltip text for the <abbr> around 'cached'. $2 is a complete '<abbr>' with the time difference (e.g. '1 hour ago'). $3 is the generation time. $4 is the search query count (PLURAL).",
"cached-tooltip": "Tooltip text for the 'cached' <abbr> element in the cached results sentence.",
"check-results-time": "Generation time sentence (compare mode). $1 is the time in seconds.",
"check-results-time-search": "Generation time sentence (search mode). $1 is the time. $2 is the search query count (PLURAL).",
"permalink": "Label for a permanent link to the check results",
"violation-suspected": "Label indicating a copyright violation is suspected",
"violation-possible": "Label indicating a copyright violation is possible",
"violation-unlikely": "Label indicating a copyright violation is unlikely",
"no-violation": "Label indicating no copyright violation was found",
"similarity": "Label below the similarity percentage score",
"no-matches-found": "Text shown when no matching sources were found",
"redirected-from": "Notice that the page was redirected from another title. $1 is the redirect page URL, $2 is the original title, $3 is the URL to check the original.",
"redirected-from-check-original": "Link text for checking the original (non-redirected) page.",
"attribution-warning": "Warning when the article contains an attribution template. $1 is the URL to the template, $2 is the template name.",
"turnitin-results": "Section heading for Turnitin results",
"turnitin-no-matches": "Text shown when Turnitin found no matching sources",
"turnitin-report": "Label for a Turnitin report link. $1 is the report ID.",
"turnitin-report-link": "Turnitin report link with timestamp. $1 is the report ID, $2 is the diff URL, $3 is the timestamp string.",
"turnitin-source": "Turnitin source match description. $1 is the percentage, $2 is the word count, $3 is the source URL.",
"checked-sources": "Section heading for the list of checked sources",
"table-url": "Column header for the URL column in the sources table",
"table-similarity": "Column header for the similarity column in the sources table",
"table-compare": "Column header for the compare action column in the sources table",
"source-excluded": "Label shown next to a source that was excluded from checking",
"source-skipped": "Label shown next to a source that was skipped",
"no-sources-checked": "Text shown when no sources were checked",
"urls-hidden": "Notice that some URLs are hidden. $1 is the count of hidden URLs. Followed by a separate 'Show them.' link.",
"urls-hidden-show": "Link text to reveal hidden URLs.",
"search-ended-early": "Notice that the search ended early due to a high-similarity match. $1 is a URL to re-run with a complete check.",
"search-ended-early-retry": "Link text for re-running with a complete check after the search ended early.",
"article-label": "Label for the article text in the comparison view",
"source-label": "Label for the source text in the comparison view",
"compare-action": "Link text to compare a specific source URL",
"ago-seconds": "Time duration in seconds. $1 is the count (PLURAL). Example: '5 seconds ago'.",
"ago-minutes": "Time duration in minutes. $1 is the count (PLURAL). Example: '2 minutes ago'.",
"ago-hours": "Time duration in hours. $1 is the count (PLURAL). Example: '1 hour ago'."
}

+ 1
- 0
pyproject.toml Visa fil

@@ -15,6 +15,7 @@ dependencies = [
"pydantic >= 2.9.2",
"SQLAlchemy >= 2.0.32",
"mwoauth >= 0.4.0",
"toolforge_i18n[Flask] >= 0.1",
]

[project.urls]


+ 5
- 1
src/copyvios/__init__.py Visa fil

@@ -3,8 +3,9 @@ import logging
import os
from logging.handlers import TimedRotatingFileHandler

from flask import Flask, request
from flask import Flask, g, request
from flask.sessions import SecureCookieSessionInterface, SessionMixin
from toolforge_i18n import ToolforgeI18n, set_user_agent


class CopyviosSessionInterface(SecureCookieSessionInterface):
@@ -34,6 +35,9 @@ app.config["SESSION_COOKIE_SECURE"] = True
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
app.config["PERMANENT_SESSION_LIFETIME"] = datetime.timedelta(days=365)

set_user_agent("Copyvios/1.0 (https://github.com/earwig/copyvios; tools.copyvios@toolforge.org)")
i18n = ToolforgeI18n(app)

app.jinja_options["trim_blocks"] = True
app.jinja_options["lstrip_blocks"] = True



+ 14
- 10
src/copyvios/checker.py Visa fil

@@ -14,8 +14,9 @@ from earwigbot.wiki.copyvios import CopyvioChecker, workers
from earwigbot.wiki.copyvios.markov import DEFAULT_DEGREE, EMPTY
from earwigbot.wiki.copyvios.result import CopyvioCheckResult, CopyvioSource
from earwigbot.wiki.copyvios.workers import CopyvioWorkspace
from flask import g, session
from flask import current_app, g, session
from sqlalchemy import PoolProxiedConnection
from toolforge_i18n._flask import message as _i18n_message

from .cache import cache
from .misc import get_sql_error, sql_dialect
@@ -53,7 +54,13 @@ class CopyvioCheckError(Exception):
def do_check(query: CheckQuery) -> CopyvioCheckResult | None:
if query.submitted:
is_logged_in = _get_username() is not None
if query.use_engine and not is_logged_in and not isinstance(query, APIQuery):
bypass = current_app.config.get("COPYVIOS_BYPASS_LOGIN", False)
if (
query.use_engine
and not is_logged_in
and not isinstance(query, APIQuery)
and not bypass
):
raise CopyvioCheckError(ErrorCode.NOT_LOGGED_IN)

site = get_site(query)
@@ -299,16 +306,13 @@ def _get_cached_results(


def _format_date(cache_time: datetime) -> str:
def formatter(val: float, unit: str):
return f"{int(val)} {unit}{'' if val == 1 else 's'}"

diff = datetime.now(UTC) - cache_time
total_seconds = diff.days * 86400 + diff.seconds
if total_seconds > 3600:
return formatter(total_seconds / 3600, "hour")
if total_seconds > 60:
return formatter(total_seconds / 60, "minute")
return formatter(total_seconds, "second")
if total_seconds >= 3600:
return _i18n_message("ago-hours", num=int(total_seconds / 3600))
if total_seconds >= 60:
return _i18n_message("ago-minutes", num=int(total_seconds / 60))
return str(_i18n_message("ago-seconds", num=total_seconds))


def _cache_result(


+ 3
- 3
src/copyvios/templates/error.html.jinja Visa fil

@@ -1,10 +1,10 @@
{% extends "support/base.html.jinja" %}
{% block title %}
Error! - {{ super() }}
{{ message('page-title-error') | striptags }} - {{ super() }}
{% endblock %}
{% block content %}
<h2>Error!</h2>
<p>An error occurred. If it hasn't been reported (<a href="https://github.com/earwig/copyvios/issues">try to check</a>), please <a href="https://github.com/earwig/copyvios/issues/new">file an issue</a> or <a href="mailto:wikipedia.earwig@gmail.com">email me</a>. Include the following information:</p>
<h2>{{ message('error-heading') }}</h2>
<p>{{ message('error-description', issues_url='https://github.com/earwig/copyvios/issues', new_issue_url='https://github.com/earwig/copyvios/issues/new', email_url='mailto:wikipedia.earwig@gmail.com') }}</p>
<div id="info-box" class="red-box">
<pre>{{ traceback | trim | e }}</pre>
</div>


+ 25
- 15
src/copyvios/templates/index.html.jinja Visa fil

@@ -11,42 +11,52 @@
{% if error %}
<div id="info-box" class="red-box"><p>
{% if error.code == ErrorCode.BAD_ACTION %}
Unknown action: <b><span class="mono">{{ query.action | e }}</span></b>.
{{ message('error-bad-action', action=query.action) }}
{% elif error.code == ErrorCode.NO_SEARCH_METHOD %}
No copyvio search methods were selected. A check can only be made using the search engine, links present in the page, or some combination of these.
{{ message('error-no-search-method') }}
{% elif error.code == ErrorCode.BAD_OLDID %}
The revision ID <code>{{ query.oldid | e }}</code> is invalid. It should be an integer.
{{ message('error-invalid-oldid', oldid=query.oldid) }}
{% elif error.code == ErrorCode.NO_URL %}
Compare mode requires a URL to be entered. Enter one in the text box below, or choose copyvio search mode to look for content similar to the article elsewhere on the web.
{{ message('error-no-url') }}
{% elif error.code == ErrorCode.BAD_URI %}
Unsupported URI scheme: <a href="{{ query.url | e }}">{{ query.url | e }}</a>.
{% set url_link = '<a href="' ~ (query.url | e) ~ '">' ~ (query.url | e) ~ '</a>' %}
{{ message('error-unsupported-uri', target_url=url_link | safe) }}
{% elif error.code == ErrorCode.NO_DATA %}
Couldn't find any text in <a href="{{ query.url | e }}">{{ query.url | e }}</a>. <i>Note:</i> only HTML documents, plain text pages, and PDFs are supported, and content generated by JavaScript or found inside iframes is ignored.
{% set url_link = '<a href="' ~ (query.url | e) ~ '">' ~ (query.url | e) ~ '</a>' %}
{{ message('error-no-data', target_url=url_link | safe) }}
{% elif error.code == ErrorCode.TIMEOUT %}
The URL <a href="{{ query.url | e }}">{{ query.url | e }}</a> timed out before any data could be retrieved.
{% set url_link = '<a href="' ~ (query.url | e) ~ '">' ~ (query.url | e) ~ '</a>' %}
{{ message('error-timeout', target_url=url_link | safe) }}
{% elif error.code == ErrorCode.SEARCH_ERROR %}
An error occurred while using the search engine ({{ error.__cause__ }}). <i>Note:</i> there is a daily limit on the number of search queries the tool is allowed to make. You may <a href="{{ request.url | httpsfix | e }}&amp;use_engine=0">repeat the check without using the search engine</a>.
{% set retry_url = ((request.url | httpsfix) + '&use_engine=0') | e %}
{% set retry_link = '<a href="' ~ retry_url ~ '">' ~ (message('search-error-retry') | striptags) ~ '</a>' %}
{{ message('error-search-error', error_cause=error.__cause__, no_engine_url=retry_link | safe) }}
{% else %}
An unknown error occurred.
{{ message('error-unknown') }}
{% endif %}
</p></div>
{% elif not g.site %}
<div id="info-box" class="red-box">
<p>The given site (project=<b><span class="mono">{{ query.project | e }}</span></b>, language=<b><span class="mono">{{ query.lang | e }}</span></b>) doesn't seem to exist. It may also be closed or private. <a href="https://{{ query.lang | e }}.{{ query.project | e }}.org/">Confirm its URL.</a></p>
{% set site_confirm_link = '<a href="https://' ~ (query.lang | e) ~ '.' ~ (query.project | e) ~ '.org/">' ~ (message('error-bad-site-confirm') | striptags) ~ '</a>' %}
<p>{{ message('error-bad-site', project=query.project, lang=query.lang, site_url=site_confirm_link | safe) }}</p>
</div>
{% elif query.oldid and not result %}
<div id="info-box" class="red-box">
<p>The revision ID couldn't be found: <a href="https://{{ g.site.domain | e }}/w/index.php?oldid={{ query.oldid | e }}">{{ query.oldid | e }}</a>.</p>
{% set rev_link = '<a href="https://' ~ (g.site.domain | e) ~ '/w/index.php?oldid=' ~ (query.oldid | e) ~ '">' ~ (query.oldid | e) ~ '</a>' %}
<p>{{ message('error-revision-not-found', revision_url=rev_link | safe) }}</p>
</div>
{% elif query.title and not result %}
<div id="info-box" class="red-box">
<p>The page couldn't be found: <a href="{{ g.page.url }}">{{ g.page.title | e }}</a>.</p>
{% set page_link = '<a href="' ~ (g.page.url | e) ~ '">' ~ (g.page.title | e) ~ '</a>' %}
<p>{{ message('error-page-not-found', page_url=page_link | safe) }}</p>
</div>
{% endif %}
{% endif %}
<p>This tool attempts to detect <a href="https://en.wikipedia.org/wiki/WP:COPYVIO">copyright violations</a> in articles. In <i>search mode</i>, it will check for similar content elsewhere on the web using <a href="https://developers.google.com/custom-search/">Google</a>, external links present in the text of the page, or <a href="https://en.wikipedia.org/wiki/Wikipedia:Turnitin">Turnitin</a> (via <a href="https://en.wikipedia.org/wiki/User:EranBot">EranBot</a>), depending on which options are selected. In <i>compare mode</i>, the tool will compare the article to a specific webpage without making additional searches, like the <a href="https://dupdet.toolforge.org/">Duplication Detector</a>.</p>
<p>Running a full check can take up to a minute if other websites are slow or if the tool is under heavy use. Please be patient. If you get a timeout, wait a moment and refresh the page.</p>
<p>Be aware that other websites can copy from Wikipedia, so check the results carefully, especially for older or well-developed articles. Specific websites can be skipped by adding them to the <a href="https://en.wikipedia.org/wiki/User:EarwigBot/Copyvios/Exclusions">excluded URL list</a>.</p>
{% set ui_lang = query.lang or g.lang_code or 'en' %}
<p>{{ message('intro-paragraph-1',
copyright_url=('<a href="' ~ (message('intro-paragraph-1-copyvio-url') | e) ~ '">' ~ (message('intro-copyright-violations') | striptags) ~ '</a>') | safe) }}</p>
<p>{{ message('intro-paragraph-2') }}</p>
<p>{{ message('intro-paragraph-3', exclusions_url=('<a href="https://en.wikipedia.org/wiki/User:EarwigBot/Copyvios/Exclusions">' ~ (message('intro-exclusions-list') | striptags) ~ '</a>') | safe) }}</p>
{% include "support/cv_form.html.jinja" %}
{% if result %}
{% include "support/cv_result.html.jinja" %}


+ 8
- 8
src/copyvios/templates/login.html.jinja Visa fil

@@ -1,22 +1,22 @@
{% extends "support/base.html.jinja" %}
{% block title %}
Login - {{ super() }}
{{ message('page-title-login') | striptags }} - {{ super() }}
{% endblock %}
{% block content %}
{% if error %}
<div id="info-box" class="red-box">
<p>Error trying to log in: {{ error | safe }}</p>
<p>{{ message('login-error', error_text=error) }}</p>
</div>
{% endif %}
<h2>Login</h2>
<p>You are required to log in with your Wikimedia account to perform checks with the search engine.</p>
<h2>{{ message('login-heading') }}</h2>
<p>{{ message('login-required') }}</p>
{% if request.args.get("next") %}
<p>
After logging in,
{{ message('after-logging-in') }}
{% if is_search %}
your check will be run.
{{ message('check-will-be-run') }}
{% else %}
you will be redirected to: {{ request.args["next"] | safe }}
{{ message('will-be-redirected-to') }} {{ request.args["next"] | safe }}
{% endif %}
</p>
{% endif %}
@@ -27,7 +27,7 @@
<span class="oo-ui-fieldLayout-field">
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-flaggedElement-primary oo-ui-flaggedElement-progressive oo-ui-labelElement oo-ui-buttonInputWidget">
<button type="submit" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">
<span class="oo-ui-labelElement-label">Login</span>
<span class="oo-ui-labelElement-label">{{ message('login-button') }}</span>
</button>
</span>
</span>


+ 4
- 4
src/copyvios/templates/logout.html.jinja Visa fil

@@ -1,17 +1,17 @@
{% extends "support/base.html.jinja" %}
{% block title %}
Logout - {{ super() }}
{{ message('page-title-logout') | striptags }} - {{ super() }}
{% endblock %}
{% block content %}
<h2>Logout</h2>
<p>Logging out will prevent you from making search engine checks.</p>
<h2>{{ message('logout-heading') }}</h2>
<p>{{ message('logout-message') }}</p>
<form action="{{ request.script_root }}/logout" method="post">
<div class="oo-ui-layout oo-ui-fieldLayout oo-ui-fieldLayout-align-left">
<div class="oo-ui-fieldLayout-body">
<span class="oo-ui-fieldLayout-field">
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-flaggedElement-primary oo-ui-flaggedElement-progressive oo-ui-labelElement oo-ui-buttonInputWidget">
<button type="submit" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">
<span class="oo-ui-labelElement-label">Logout</span>
<span class="oo-ui-labelElement-label">{{ message('logout-button') }}</span>
</button>
</span>
</span>


+ 12
- 13
src/copyvios/templates/settings.html.jinja Visa fil

@@ -1,6 +1,6 @@
{% extends "support/base.html.jinja" %}
{% block title %}
Settings - {{ super() }}
{{ message('page-title-settings') | striptags }} - {{ super() }}
{% endblock %}
{% block content %}
{% if status %}
@@ -8,10 +8,10 @@
<p>{{ status | safe }}</p>
</div>
{% endif %}
<h2>Settings</h2>
<p>This page contains some configurable options for the copyvio detector. Settings are saved as cookies.</p>
<h2>{{ message('settings-heading') }}</h2>
<p>{{ message('settings-description') }}</p>
<form action="{{ request.script_root }}/settings" method="post">
<h3>Default site</h2>
<h3>{{ message('settings-default-site') }}</h2>
<div class="oo-ui-layout oo-ui-labelElement oo-ui-fieldLayout oo-ui-fieldLayout-align-top">
<div class="oo-ui-fieldLayout-body">
<div class="oo-ui-fieldLayout-field">
@@ -47,17 +47,16 @@
</div>
</div>

<h3>Background</h2>
{% set background_options = [
("list", 'Randomly select from <a href="https://commons.wikimedia.org/wiki/User:The_Earwig/POTD">a subset</a> of previous <a href="https://commons.wikimedia.org/">Wikimedia Commons</a> <a href="https://commons.wikimedia.org/wiki/Commons:Picture_of_the_day">Pictures of the Day</a> that work well as widescreen backgrounds, refreshed daily (default).'),
("potd", 'Use the current Commons Picture of the Day, unfiltered. Certain POTDs may be unsuitable as backgrounds due to their aspect ratio or subject matter.'),
("plain", "Use a plain background."),
]
%}
<h3>{{ message('settings-background') }}</h2>
<div class="oo-ui-layout oo-ui-labelElement oo-ui-fieldLayout oo-ui-fieldLayout-align-top">
<div class="oo-ui-fieldLayout-body">
<div class="oo-ui-fieldLayout-field">
<div class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-radioSelectInputWidget">
{% set background_options = [
("list", message('settings-background-list', potd_subset_url='https://commons.wikimedia.org/wiki/User:The_Earwig/POTD', commons_url='https://commons.wikimedia.org/', potd_url='https://commons.wikimedia.org/wiki/Commons:Picture_of_the_day')),
("potd", message('settings-background-potd')),
("plain", message('settings-background-plain')),
] %}
{% for value, desc in background_options %}
<div class="oo-ui-layout oo-ui-labelElement oo-ui-fieldLayout oo-ui-fieldLayout-align-inline">
<div class="oo-ui-fieldLayout-body">
@@ -67,7 +66,7 @@
</span>
</span>
<span class="oo-ui-fieldLayout-header">
<label for="background-{{ value | e }}" class="oo-ui-labelElement-label">{{ desc | safe }}</label>
<label for="background-{{ value | e }}" class="oo-ui-labelElement-label">{{ desc }}</label>
</span>
</div>
</div>
@@ -83,7 +82,7 @@
<span class="oo-ui-fieldLayout-field">
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-flaggedElement-primary oo-ui-flaggedElement-progressive oo-ui-labelElement oo-ui-buttonInputWidget">
<button type="submit" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">
<span class="oo-ui-labelElement-label">Save</span>
<span class="oo-ui-labelElement-label">{{ message('settings-save') }}</span>
</button>
</span>
</span>


+ 11
- 11
src/copyvios/templates/support/base.html.jinja Visa fil

@@ -1,8 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<html {{ push_html_lang(g.interface_language_code) }}>
<head>
<meta charset="utf-8">
<title>{% block title %}Earwig's Copyvio Detector{% endblock %}</title>
<title>{% block title %}{{ message('site-title') | striptags }}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://tools-static.wmflabs.org/cdnjs/ajax/libs/oojs-ui/0.41.3/oojs-ui-core-wikimediaui.min.css" integrity="sha512-xL+tTXAo7a4IAwNrNqBcOGWSqJF6ip0jg4SEda2mapAUxPzfOZQ7inazR4TvSCblHQjwtTOkUDIFtnpaSrg3xg==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
<link rel="stylesheet" href="https://tools-static.wmflabs.org/cdnjs/ajax/libs/oojs-ui/0.41.3/oojs-ui-images-wikimediaui.min.css" integrity="sha512-A0LSCuOGH1+SyLhOs4eSKGbNgIEGXgIGh4ytb0GRj9GSUsjmmK6LFzB/E0o9ymRUvD+q7bZyv74XpboQt5qFvQ==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
@@ -21,14 +21,14 @@
<div id="container"{{' class="splash"' if splash else ''}}>
<div id="content">
<header>
<h1><a href="/">Earwig&apos;s <strong>Copyvio Detector</strong></a></h1>
<h1><a href="/">{{ message('site-header-title') }}</a></h1>
{% set logged_in_user = session.get("username") %}
{% if logged_in_user %}
<a class="logout-link" href="/logout" title="Log out">{{ logged_in_user | safe }}</a>
<a class="logout-link" href="/logout" title="{{ message('log-out') | striptags | e }}">{{ logged_in_user | safe }}</a>
{% else %}
<a class="login-link" href="/login" title="Log in">Login</a>
<a class="login-link" href="/login" title="{{ message('log-in') | striptags | e }}">{{ message('log-in') }}</a>
{% endif %}
<a id="settings-link" href="/settings">Settings</a>
<a id="settings-link" href="/settings">{{ message('settings') }}</a>
</header>
<main>
{% set notice = get_notice() %}
@@ -44,13 +44,13 @@
</div>
<footer>
<ul>
<li>Maintained by <a href="https://en.wikipedia.org/wiki/User:The_Earwig">Ben Kurtovic</a></li>
<li><a href="{{ request.script_root }}/api">API</a></li>
<li><a href="https://github.com/earwig/copyvios">Source code</a></li>
<li>{{ message('maintained-by', author_url='<a href="https://en.wikipedia.org/wiki/User:The_Earwig">Ben Kurtovic</a>' | safe) }}</li>
<li><a href="{{ request.script_root }}/api">{{ message('api') }}</a></li>
<li><a href="https://github.com/earwig/copyvios">{{ message('source-code') }}</a></li>
{% if desc_url %}
<li><a href="{{ desc_url | e }}">Background image</a></li>
<li><a href="{{ desc_url | e }}">{{ message('background-image') }}</a></li>
{% endif %}
</ul>
</footer>
</body>
</html>
</html{{ pop_html_lang(g.interface_language_code) }}>

+ 15
- 15
src/copyvios/templates/support/cv_form.html.jinja Visa fil

@@ -1,8 +1,8 @@
<form id="cv-form" action="{{ request.script_root }}/" method="get">
<div class="oo-ui-layout oo-ui-horizontalLayout">
<label class="site oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Site</label>
<label class="site oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-site') }}</label>
<div class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-dropdownInputWidget oo-ui-dropdownInputWidget-php">
<select name="lang" required="" class="oo-ui-inputWidget-input oo-ui-indicator-down" title="Language">
<select name="lang" required="" class="oo-ui-inputWidget-input oo-ui-indicator-down" title="{{ message('form-language') | striptags | e }}">
{% set selected_lang = query.orig_lang if query.orig_lang else cookies["CopyviosDefaultLang"].value if "CopyviosDefaultLang" in cookies else cache.bot.wiki.get_site().lang %}
{% for lang in cache.langs %}
{% if lang.code == selected_lang %}
@@ -14,7 +14,7 @@
</select>
</div>
<div class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-dropdownInputWidget oo-ui-dropdownInputWidget-php">
<select name="project" required="" class="oo-ui-inputWidget-input oo-ui-indicator-down" title="Project">
<select name="project" required="" class="oo-ui-inputWidget-input oo-ui-indicator-down" title="{{ message('form-project') | striptags | e }}">
{% set selected_project = query.project if query.project else cookies["CopyviosDefaultProject"].value if "CopyviosDefaultProject" in cookies else cache.bot.wiki.get_site().project %}
{% for project in cache.projects %}
{% if project.code == selected_project %}
@@ -27,17 +27,17 @@
</div>
</div>
<div class="oo-ui-layout oo-ui-horizontalLayout">
<label for="cv-title" class="page oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Page</label>
<label for="cv-title" class="page oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-page') }}</label>
<div class="page-title oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-textInputWidget oo-ui-textInputWidget-type-text oo-ui-textInputWidget-php">
<input id="cv-title" type="text" class="oo-ui-inputWidget-input" name="title" placeholder="Title" title="Page title"
<input id="cv-title" type="text" class="oo-ui-inputWidget-input" name="title" placeholder="{{ message('form-title-placeholder') | striptags | e }}" title="{{ message('form-title-tooltip') | striptags | e }}"
{% if query.title %}
value="{{ g.page.title if g.page else query.title | e}}"
{% endif %}
>
</div>
<label class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">or</label>
<label class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-or') }}</label>
<div class="page-oldid oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-textInputWidget oo-ui-textInputWidget-type-text oo-ui-textInputWidget-php">
<input id="cv-oldid" type="text" class="oo-ui-inputWidget-input" name="oldid" placeholder="Revision ID" title="Revision ID"
<input id="cv-oldid" type="text" class="oo-ui-inputWidget-input" name="oldid" placeholder="{{ message('form-revision-id-placeholder') | striptags | e }}" title="{{ message('form-revision-id-tooltip') | striptags | e }}"
{% if query.oldid %}
value="{{ query.oldid | e }}"
{% endif %}
@@ -49,7 +49,7 @@
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-radioInputWidget">
<input id="action-search" class="oo-ui-inputWidget-input" type="radio" name="action" value="search" {{ 'checked="checked"' if (query.action == "search" or not query.action) else "" }}><span></span>
</span>
<label for="action-search" class="action oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Copyvio search</label>
<label for="action-search" class="action oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-copyvio-search') }}</label>
</span>

<input type="hidden" name="use_engine" value="0">
@@ -58,7 +58,7 @@
<input id="cv-cb-engine" class="cv-search oo-ui-inputWidget-input" type="checkbox" name="use_engine" value="1" {{ 'checked="checked"' if query.use_engine else "" }}>
<span class="oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert"></span>
</span>
<label for="cv-cb-engine" class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Use search engine</label>
<label for="cv-cb-engine" class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-use-search-engine') }}</label>
</span>

<input type="hidden" name="use_links" value="0">
@@ -67,7 +67,7 @@
<input id="cv-cb-links" class="cv-search oo-ui-inputWidget-input" type="checkbox" name="use_links" value="1" {{ 'checked="checked"' if query.use_links else "" }}>
<span class="oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert"></span>
</span>
<label for="cv-cb-links" class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Use links in page</label>
<label for="cv-cb-links" class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-use-links') }}</label>
</span>

<!-- 2026-06-07: Turnitin disabled, API does not work
@@ -77,7 +77,7 @@
<input id="cv-cb-turnitin" class="cv-search oo-ui-inputWidget-input" type="checkbox" name="turnitin" value="1" {{ 'checked="checked"' if query.turnitin else "" }}>
<span class="oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert"></span>
</span>
<label for="cv-cb-turnitin" class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Use Turnitin</label>
<label for="cv-cb-turnitin" class="oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-use-turnitin') }}</label>
</span>
-->
</div>
@@ -86,10 +86,10 @@
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-radioInputWidget">
<input id="action-compare" class="oo-ui-inputWidget-input" type="radio" name="action" value="compare" {{ 'checked="checked"' if query.action == "compare" else "" }}><span></span>
</span>
<label for="action-compare" class="action oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">Copyvio compare</label>
<label for="action-compare" class="action oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelElement oo-ui-labelWidget">{{ message('form-copyvio-compare') }}</label>
</span>
<div class="compare-url cv-compare-oo-ui oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-textInputWidget oo-ui-textInputWidget-type-text oo-ui-textInputWidget-php">
<input type="text" class="cv-compare oo-ui-inputWidget-input" name="url" placeholder="URL" title="URL to compare"
<input type="text" class="cv-compare oo-ui-inputWidget-input" name="url" placeholder="{{ message('form-url-placeholder') | striptags | e }}" title="{{ message('form-url-tooltip') | striptags | e }}"
{% if query.url %}
value="{{ query.url | e }}"
{% endif %}
@@ -102,13 +102,13 @@
<input id="cb-nocache" class="oo-ui-inputWidget-input" type="checkbox" name="nocache" value="1" {{ 'checked="checked"' if query.nocache else "" }}>
<span class="oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert"></span>
</span>
<label for="cb-nocache">Bypass cache</label>
<label for="cb-nocache">{{ message('form-bypass-cache') }}</label>
</div>
{% endif %}
<div class="oo-ui-layout oo-ui-horizontalLayout">
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-flaggedElement-primary oo-ui-flaggedElement-progressive oo-ui-buttonWidget">
<button type="submit" class="oo-ui-inputWidget-input oo-ui-buttonElement-button">
<span class="oo-ui-labelElement-label">Submit</span>
<span class="oo-ui-labelElement-label">{{ message('form-submit') }}</span>
</button>
</span>
</div>


+ 39
- 32
src/copyvios/templates/support/cv_result.html.jinja Visa fil

@@ -1,15 +1,17 @@
<div id="generation-time">
Results
{% if result.metadata.cached %}
<a id="cv-cached" href="#">cached<span>To save time (and money), this tool will retain the results of checks for up to 72 hours. This includes the URLs of the checked sources, but neither their content nor the content of the article. Future checks on the same page (assuming it remains unchanged) will not involve additional search queries, but a fresh comparison against the source URL will be made. If the page is modified, a new check will be run.</span></a> from <abbr title="{{ result.metadata.cache_time }}">{{ result.metadata.cache_age }} ago</abbr>. Originally
{% endif %}
generated in <span class="mono">{{ result.time | round(3) }}</span>
{% if query.action == "search" %}
seconds using <span class="mono">{{ result.queries }}</span> quer{{ "y" if result.queries == 1 else "ies" }}.
{% set time_abbr = '<abbr title="' ~ (result.metadata.cache_time | e) ~ '">' ~ (result.metadata.cache_age | striptags) ~ '</abbr>' %}
{% if query.action == "search" %}
{{ message('cached-line-search', tooltip_text=(message('cached-tooltip') | striptags | e), time_abbr=time_abbr | safe, gen_time=result.time | round(3), num_queries=result.queries) }}
{% else %}
{{ message('cached-line', tooltip_text=(message('cached-tooltip') | striptags | e), time_abbr=time_abbr | safe, gen_time=result.time | round(3)) }}
{% endif %}
{% elif query.action == "search" %}
{{ message('check-results-time-search', time=result.time | round(3), num_queries=result.queries) }}
{% else %}
seconds.
{{ message('check-results-time', time=result.time | round(3)) }}
{% endif %}
<a href="{{ get_permalink(query) | e }}">Permalink.</a>
<a href="{{ get_permalink(query) | e }}">{{ message('permalink') }}</a>
</div>

<div id="cv-result" class="{{ 'red' if result.confidence >= T_SUSPECT else 'yellow' if result.confidence >= T_POSSIBLE else 'green' }}-box">
@@ -27,29 +29,31 @@
{% endif %}
{% if query.redirected_from %}
<br />
<span id="redirected-from">Redirected from <a href="https://{{ g.site.domain | e }}/w/index.php?title={{ result.metadata.redirected_from.title | e }}&amp;redirect=no">{{ result.metadata.redirected_from.title | e }}</a>. <a href="{{ request.url | httpsfix | e }}&amp;noredirect=1">Check original.</a></span>
{% set redirect_link = '<a href="https://' ~ (g.site.domain | e) ~ '/w/index.php?title=' ~ (result.metadata.redirected_from.title | urlencode) ~ '&redirect=no">' ~ (result.metadata.redirected_from.title | e) ~ '</a>' %}
{% set check_orig_link = '<a href="' ~ ((request.url | httpsfix) ~ '&noredirect=1' | e) ~ '">' ~ (message('redirected-from-check-original') | striptags) ~ '</a>' %}
<span id="redirected-from">{{ message('redirected-from', redirect_page_url=redirect_link | safe, check_original_url=check_orig_link | safe) }}</span>
{% endif %}
</td>
<td>
<div>
{% if result.confidence >= T_SUSPECT %}
Violation suspected
{{ message('violation-suspected') }}
{% elif result.confidence >= T_POSSIBLE %}
Violation possible
{{ message('violation-possible') }}
{% elif result.sources %}
Violation unlikely
{{ message('violation-unlikely') }}
{% else %}
No violation
{{ message('no-violation') }}
{% endif %}
</div>
<div>{{ (result.confidence * 100) | round(1) }}%</div>
<div>similarity</div>
<div>{{ message('similarity') }}</div>
</td>
<td>
{% if result.url %}
<a href="{{ result.url | e }}">{{ result.url | urlstrip | e }}</a>
{% else %}
<span id="result-head-no-sources">No matches found.</span>
<span id="result-head-no-sources">{{ message('no-matches-found') }}</span>
{% endif %}
</td>
</tr>
@@ -58,27 +62,28 @@

{% set attrib = get_attribution_info(g.site, g.page) %}
{% if attrib %}
{% set template_link = '<a href="' ~ (attrib[1] | e) ~ '">' ~ (attrib[0] | e) ~ '</a>' %}
<div id="attribution-warning" class="yellow-box">
This article contains an attribution template: <code>{{ '{{' }}<a href="{{ attrib[1] | e }}">{{ attrib[0] | e }}</a>{{ '}}' }}</code>. Please verify that any potential copyvios are not from properly attributed sources.
{{ message('attribution-warning', template_url=template_link | safe) }}
</div>
{% endif %}

{% if result.metadata.turnitin_result %}
<div id="turnitin-container" class="{{ 'red' if result.metadata.turnitin_result.reports else 'green' }}-box">
<div id="turnitin-title">Turnitin Results</div>
<div id="turnitin-title">{{ message('turnitin-results') }}</div>
{% if result.metadata.turnitin_result.reports %}
<table id="turnitin-table"><tbody>
{% for report in turnitin_result.reports %}
<tr><td class="turnitin-table-cell"><a href="https://eranbot.toolforge.org/ithenticate.py?rid={{ report.reportid }}">Report {{ report.reportid }}</a> for text added at <a href="https://{{ query.lang | e }}.wikipedia.org/w/index.php?title={{ query.title | e }}&amp;diff={{ report.diffid }}"> {{ report.time_posted.strftime("%H:%M, %d %B %Y (UTC)") }}</a>:
<tr><td class="turnitin-table-cell"><a href="https://eranbot.toolforge.org/ithenticate.py?rid={{ report.reportid }}">{{ message('turnitin-report', report_id=report.reportid) }}</a> for text added at <a href="https://{{ query.lang | e }}.wikipedia.org/w/index.php?title={{ query.title | e }}&amp;diff={{ report.diffid }}"> {{ report.time_posted.strftime("%H:%M, %d %B %Y (UTC)") }}</a>:
<ul>
{% for source in report.sources %}
<li>{{ source['percent'] }}% of revision text ({{ source['words'] }} words) found at <a href="{{ source['url'] | e }}">{{ source['url'] | e }}</a></li>
<li>{{ message('turnitin-source', percent=source['percent'], word_count=source['words'], source_url=('<a href="' ~ (source['url'] | e) ~ '">' ~ (source['url'] | e) ~ '</a>') | safe) }}</li>
{% endfor %}
</ul></td></tr>
{% endfor %}
</tbody></table>
{% else %}
<div id="turnitin-summary">No matching sources found.</div>
<div id="turnitin-summary">{{ message('turnitin-no-matches') }}</div>
{% endif %}
</div>
{% endif %}
@@ -86,7 +91,7 @@
{% if query.action == "search" %}
{% set skips = False %}
<div id="sources-container">
<div id="sources-title">Checked Sources</div>
<div id="sources-title">{{ message('checked-sources') }}</div>
{% if result.sources %}
<table id="cv-result-sources">
<colgroup>
@@ -95,52 +100,54 @@
<col>
</colgroup>
<tr>
<th>URL</th>
<th>Similarity</th>
<th>Compare</th>
<th>{{ message('table-url') }}</th>
<th>{{ message('table-similarity') }}</th>
<th>{{ message('table-compare') }}</th>
</tr>
{% for source in result.sources %}
<tr {{ 'class="source-default-hidden"' if loop.index0 >= 10 else 'id="source-row-selected"' if loop.index0 == 0 else "" }}>
<td><a {{ 'id="source-selected"' if loop.index0 == 0 else "" }} class="source-url" href="{{ source.url | e }}">{{ source.url | e }}</a></td>
<td>
{% if source.excluded %}
<span class="source-excluded">Excluded</span>
<span class="source-excluded">{{ message('source-excluded') }}</span>
{% elif source.skipped %}
{% set skips = True %}
<span class="source-skipped">Skipped</span>
<span class="source-skipped">{{ message('source-skipped') }}</span>
{% else %}
<span class="source-similarity {{ "source-suspect" if source.confidence >= T_SUSPECT else "source-possible" if source.confidence >= T_POSSIBLE else "source-novio" }}">{{ (source.confidence * 100) | round(1) }}%</span>
{% endif %}
</td>
<td>
<a href="{{ request.script_root | e }}/?lang={{ query.lang | e }}&amp;project={{ query.project | e }}&amp;oldid={{ query.oldid or g.page.lastrevid | e }}&amp;action=compare&amp;url={{ source.url | e }}">Compare</a>
<a href="{{ request.script_root | e }}/?lang={{ query.lang | e }}&amp;project={{ query.project | e }}&amp;oldid={{ query.oldid or g.page.lastrevid | e }}&amp;action=compare&amp;url={{ source.url | e }}">{{ message('compare-action') }}</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="cv-source-footer">
No sources checked.
{{ message('no-sources-checked') }}
</div>
{% endif %}
{% if result.sources | length > 10 %}
<div id="cv-additional" class="cv-source-footer">
{{ result.sources | length - 10 }} URL{{ "s" if result.sources | length > 11 else "" }} with lower similarity hidden. <a id="show-additional-sources" href="#">Show them.</a>
{{ message('urls-hidden', num_urls=result.sources | length - 10) }}
<a href="#" id="show-additional-sources">{{ message('urls-hidden-show') }}</a>
</div>
{% endif %}
{% if skips or result.possible_miss %}
<div class="cv-source-footer">
The search ended early because a match was found with high similarity. <a href="{{ request.url | httpsfix | e }}&amp;noskip=1">Do a complete check.</a>
{% set retry_link = '<a href="' ~ ((request.url | httpsfix) ~ '&noskip=1' | e) ~ '">' ~ (message('search-ended-early-retry') | striptags) ~ '</a>' %}
{{ message('search-ended-early', retry_url=retry_link | safe) }}
</div>
{% endif %}
</div>
{% endif %}
<table id="cv-chain-table">
<tr>
<td class="cv-chain-cell">Article: <div class="cv-chain-detail">
<td class="cv-chain-cell">{{ message('article-label') }} <div class="cv-chain-detail">
<p>{{ highlight_delta(result.article_chain, result.best.chains[1] if result.best else None) }}</p>
</div></td>
<td class="cv-chain-cell">Source: <div class="cv-chain-detail">
<td class="cv-chain-cell">{{ message('source-label') }} <div class="cv-chain-detail">
<p>{{ highlight_delta(result.best.chains[0], result.best.chains[1]) if result.best else "" }}</p>
</div></td>
</tr>


+ 47
- 0
tool_translations_config.py Visa fil

@@ -0,0 +1,47 @@
from toolforge_i18n import TranslationsConfig

config = TranslationsConfig(
variables={
"login-error": ["error_text"],
"settings-updated": ["changes"],
"error-bad-action": ["action"],
"error-invalid-oldid": ["oldid"],
"error-unsupported-uri": ["target_url"],
"error-no-data": ["target_url"],
"error-timeout": ["target_url"],
"error-bad-site": ["project", "lang", "site_url"],
"error-revision-not-found": ["revision_url"],
"error-page-not-found": ["page_url"],
"error-search-error": ["error_cause", "no_engine_url"],
"check-results-time": ["time"],
"check-results-time-search": ["time", "num_queries"],
"cached-line": ["tooltip_text", "time_abbr", "gen_time"],
"cached-line-search": ["tooltip_text", "time_abbr", "gen_time", "num_queries"],
"redirected-from": ["redirect_page_url", "check_original_url"],
"attribution-warning": ["template_url"],
"turnitin-report": ["report_id"],
"turnitin-report-link": ["report_id", "diff_url"],
"turnitin-source": ["percent", "word_count", "source_url"],
"search-ended-early": ["retry_url"],
"urls-hidden": ["num_urls"],
"ago-seconds": ["num"],
"ago-minutes": ["num"],
"ago-hours": ["num"],
"maintained-by": ["author_url"],
"error-description": ["issues_url", "new_issue_url", "email_url"],
"intro-paragraph-3": ["exclusions_url"],
"intro-paragraph-1": ["copyright_url"],
"intro-paragraph-1-copyvio-url": [],
"settings-background-list": ["potd_subset_url", "commons_url", "potd_url"],
},
allowed_html_elements={
"a": {"href", "id"},
"abbr": {"title"},
"b": set(),
"br": set(),
"code": set(),
"i": set(),
"span": {"class", "id"},
"strong": set(),
},
)

Laddar…
Avbryt
Spara