From d9130ca7f0cae987b187a4c854cf214629499b7f Mon Sep 17 00:00:00 2001 From: MrChengLen Date: Tue, 29 Sep 2026 09:01:30 +0200 Subject: [PATCH 1/3] =?UTF-8?q?fix(pages):=20website=20texts=20match=20the?= =?UTF-8?q?=20service=20=E2=80=94=20terms,=20privacy,=20/enterprise,=20/pr?= =?UTF-8?q?icing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A claim-by-claim check of the public pages against the code: - Terms §8 no longer quotes Pro/Business prices (€7/€19, long stale); they come from the pricing page. Cancellation is described by email, since the dashboard has no portal link. - Privacy policy: rate limits as they are (per route, 5/hour to 120/minute); the stored-data list adds the preferred language, the email-verification timestamp and per-request usage rows; Hetzner "data centre in the EU" and Zoho Amsterdam/Dublin instead of "Frankfurt"; paid plans not yet available. Logging out now also removes the legacy filemorph_api_key browser entry (auth.js), so §6 holds. - /pricing and /enterprise: API access without a key, v1.1.0 as the tagged release, the audit log's actual scope, the veraPDF gate, "180+ format pairs" counted as distinct pairs, support response times agreed per contract, a PGP key on request. - /formats: exact target size only for JPEG, WebP and AVIF. - /security and security.txt drop GitHub Security Advisories, which are not enabled on this repository. - The redaction page no longer calls app/ee AGPL; the JSON-LD featureList adds the PDF tools and PDF -> PDF/A. Guards: terms carry no euro price in either language; the enterprise pair count is pinned to the registry (spelling variants merged); logout() clears every signed-in localStorage key. Catalogs regenerated, all new strings translated. Full suite green on main ed28b44; ruff, i18n drift, Tailwind freshness, gitleaks and scope-guard clean; security-auditor PASS, code-reviewer findings addressed. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 28 ++ app/api/routes/seo.py | 4 - app/core/jsonld.py | 2 + app/core/redact_content.py | 8 +- app/static/js/auth.js | 2 + app/templates/dashboard.html | 2 +- app/templates/enterprise.html | 14 +- app/templates/formats.html | 4 +- app/templates/pricing.html | 6 +- app/templates/privacy.html | 16 +- app/templates/security.html | 5 - app/templates/terms.html | 4 +- locale/de/LC_MESSAGES/messages.mo | Bin 113233 -> 114633 bytes locale/de/LC_MESSAGES/messages.po | 336 +++++++++++----------- locale/en/LC_MESSAGES/messages.mo | Bin 3119 -> 3119 bytes locale/en/LC_MESSAGES/messages.po | 194 ++++++------- locale/messages.pot | 192 ++++++------- tests/test_cookie_notice.py | 14 + tests/test_format_lists_match_registry.py | 18 ++ tests/test_pricing_centralized.py | 12 + 20 files changed, 472 insertions(+), 389 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9fcfe0..83d2b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,34 @@ Versions follow [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Fixed — website texts match what the service does + +The public pages were checked claim by claim against the code: + +- **Terms §8** no longer quotes subscription prices (they come from the + pricing page) and describes cancellation by email. +- **Privacy policy:** the rate-limit description is accurate (set per route, + from 5 contact messages per hour to 120 requests per minute); the list of + stored data adds the preferred language, the email-verification timestamp + and per-request usage rows; sub-processor locations are corrected (Hetzner: + a data centre in the EU; Zoho: Amsterdam and Dublin); paid plans are not + yet available; the date is refreshed. Logging out now also removes the + legacy `filemorph_api_key` browser entry (`auth.js`), as §6 says — a test + in `tests/test_cookie_notice.py` pins it. +- **/pricing and /enterprise:** API access without a key, v1.1.0 as the + tagged release, the audit log's scope, the veraPDF gate, "180+ format + pairs" (distinct pairs, pinned by a test), support response times agreed + per contract, and a PGP key on request. +- **/formats** limits exact target-size compression to JPEG, WebP and AVIF. +- **Dashboard:** account deletion names the 10-year tax-retention record. +- **/security** and `/.well-known/security.txt` drop the GitHub Security + Advisories option, which is not enabled on this repository. +- The redaction page no longer calls its engine AGPL open source (`app/ee` + is commercially licensed); the JSON-LD `featureList` adds the PDF tools + and PDF → PDF/A. +- `tests/test_pricing_centralized.py` fails if the terms page hardcodes a + euro price in either language. + ### Fixed — API docs: keys are optional, video compression and converter registration described correctly The API reference marked every file route "Authentication: Required", and the diff --git a/app/api/routes/seo.py b/app/api/routes/seo.py index de29355..ba07857 100644 --- a/app/api/routes/seo.py +++ b/app/api/routes/seo.py @@ -286,9 +286,5 @@ async def security_txt() -> str: f"Canonical: {base}/.well-known/security.txt", f"Policy: {base}/security", "", - "# Reports about FileMorph itself (the open-source software at", - "# https://github.com/MrChengLen/FileMorph) are also welcome via", - "# GitHub Security Advisories on the repository.", - "", ] return "\n".join(lines) diff --git a/app/core/jsonld.py b/app/core/jsonld.py index 86884b5..3231257 100644 --- a/app/core/jsonld.py +++ b/app/core/jsonld.py @@ -75,6 +75,8 @@ def build_site_jsonld(app_base_url: str) -> tuple[str, str]: "Convert audio (MP3, WAV, FLAC, OGG, M4A, AAC, WMA, Opus)", "Convert video (MP4, MOV, AVI, MKV, WebM, FLV, WMV)", "Compress images to an exact target size", + "Split, extract and compress PDF files", + "Convert PDF to PDF/A-2b", "No account required", "Self-hostable via Docker", "REST API for programmatic conversion", diff --git a/app/core/redact_content.py b/app/core/redact_content.py index 51ae5fe..7da37c2 100644 --- a/app/core/redact_content.py +++ b/app/core/redact_content.py @@ -83,7 +83,8 @@ "Processed on EU servers with no third-party AI — detection is deterministic " "(regex + checksums), not a language model. Your file is held in memory and " "deleted right after processing; detected values are never logged or stored. The " - "redaction engine is open source (AGPLv3) and auditable." + "redaction engine is commercially licensed, not part of the AGPLv3 core; the " + "source is published for audit but is not open source." ), "legal_notice": ( "Assists with PII removal — review before sharing. Detects emails, IBANs, phone " @@ -170,8 +171,9 @@ "Verarbeitung auf EU-Servern ohne Dritt-KI — die Erkennung ist deterministisch " "(Regex + Prüfziffern), kein Sprachmodell. Deine Datei liegt nur im " "Arbeitsspeicher und wird direkt nach der Verarbeitung gelöscht; erkannte Werte " - "werden nie protokolliert oder gespeichert. Die Schwärzungs-Engine ist Open " - "Source (AGPLv3) und auditierbar." + "werden nie protokolliert oder gespeichert. Die Schwärzungs-Engine ist kommerziell " + "lizenziert, nicht Teil des AGPLv3-Kerns; der Quellcode ist zur Prüfung " + "einsehbar, aber nicht Open Source." ), "legal_notice": ( "Unterstützt beim Entfernen von PII — vor Weitergabe prüfen. Erkennt E-Mails, " diff --git a/app/static/js/auth.js b/app/static/js/auth.js index 9b5eea7..592d467 100644 --- a/app/static/js/auth.js +++ b/app/static/js/auth.js @@ -52,6 +52,8 @@ function logout() { localStorage.removeItem(ACCESS_KEY); localStorage.removeItem(REFRESH_KEY); + // Legacy key older versions stored; privacy.html §6 promises it goes on logout. + localStorage.removeItem('filemorph_api_key'); window.location.href = '/login'; } diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index cc83135..5390837 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -80,7 +80,7 @@

{{ _('Email language') }}

{{ _('Danger zone') }}

-

{{ _('Permanently delete your account, API keys, and cancel any active subscription. Conversion-job records are anonymised. This cannot be undone.') }}

+

{{ _('Permanently delete your account, API keys, and cancel any active subscription. Conversion-job records are anonymised. If you\'ve made a payment, we keep a minimal invoice-link record for 10 years (German tax law). This cannot be undone.') }}

{{ ub.button(_('Delete account'), id='delete-account-btn', size='sm', variant='danger') }}

{{ _('Support SLA') }}

-

{{ _('Response-time targets by severity — critical 4 h, high 24 h, medium/low in the next regular release. Targets apply Mon–Fri 09:00–18:00 CET, excluding German public holidays.') }}

+

{{ _('Response-time targets by severity are individually agreed in the support contract.') }}

@@ -356,9 +356,9 @@

{{ _('Ready for a 15-minute pilot call

- {{ _('Confidential inquiries via') }} - security.txt - {{ _('are encrypted with the same key.') }} + {{ _('Confidential inquiries: email') }} + security@filemorph.io + {{ _('— a PGP key is available on request.') }}

diff --git a/app/templates/formats.html b/app/templates/formats.html index a16785f..07e49e7 100644 --- a/app/templates/formats.html +++ b/app/templates/formats.html @@ -16,7 +16,7 @@ {{ _('← Back to converter') }}

{{ _('Supported file formats') }}

- {{ _('FileMorph converts between the formats below and compresses images to an exact target size (video too, by quality). Everything runs for free, with no account — or self-host it under AGPLv3. Pick any pair and start on the') }} + {{ _('FileMorph converts between the formats below and compresses JPEG, WebP and AVIF images to an exact target size — PNG, TIFF and video use quality-based compression instead. Everything runs for free, with no account — or self-host it under AGPLv3. Pick any pair and start on the') }} {{ _('converter') }}. {{ _('Prefer operations over formats?') }} {{ _('See all tools') }}. @@ -54,7 +54,7 @@

{{ _('Compress to a target size') }}

- {{ _('FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size (for example 5 MB) using a binary search — useful for upload limits and email attachments.') }} + {{ _('FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size (for example 5 MB) using a binary search — useful for upload limits and email attachments. PNG, TIFF and video below compress by quality only, not to an exact size.') }} {{ _('Try the compressor →') }}

diff --git a/app/templates/pricing.html b/app/templates/pricing.html index 1f33097..dd854ef 100644 --- a/app/templates/pricing.html +++ b/app/templates/pricing.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% import "_components/card.html" as uc %} {% block title %}{{ _('Pricing — free converter, paid plans') }} | FileMorph{% endblock %} -{% block meta_description %}{{ _('FileMorph pricing: the open-source converter is free to self-host. Paid plans add volume, higher limits and API access.') }}{% endblock %} +{% block meta_description %}{{ _('FileMorph pricing: the open-source converter is free to self-host. Paid plans add volume, higher limits and larger API quotas.') }}{% endblock %} {% block content %}
@@ -20,7 +20,7 @@

{{ _('Free engine. Pay only for hosting or a li {% endif %}

- {{ _('Using without an account:') }} {{ _('up to') }} {{ anon_plan.max_file_size_mb }} MB {{ _('per file') }} · {{ _('single file at a time') }} · {{ _('10 requests/minute') }} · {{ _('no API access') }} + {{ _('Using without an account:') }} {{ _('up to') }} {{ anon_plan.max_file_size_mb }} MB {{ _('per file') }} · {{ _('single file at a time') }} · {{ _('10 requests/minute') }} · {{ _('API access without a key') }}

@@ -278,7 +278,7 @@

{{ _('Compare plans') }}

  • {{ _('Tamper-evident audit log') }}
  • - {{ _('Being transparent about what is not done yet: no external ISO 27001 certification and no external penetration test yet (both planned once a paying pilot justifies the cost), and no tagged release cut yet — the signing/SBOM pipeline is wired in CI and produces those artefacts on the first tag. Everything else above is in the repository today and auditable.') }} + {{ _('Being transparent about what is not done yet: no external ISO 27001 certification and no external penetration test yet (both planned once a paying pilot justifies the cost). v1.1.0, tagged 2026-06-01, is the latest signed release with a CycloneDX SBOM and an image digest; changes since then ship in the continuously built image ahead of the next tag. Everything else above is in the repository today and auditable.') }}

    {% endcall %} diff --git a/app/templates/privacy.html b/app/templates/privacy.html index a37b436..5f39a91 100644 --- a/app/templates/privacy.html +++ b/app/templates/privacy.html @@ -6,7 +6,7 @@
    {{ _('← Back') }}

    {{ _('Privacy Policy') }}

    -

    {{ _('Last updated: 2026-08-18 · Community + Cloud Edition') }}

    +

    {{ _('Last updated: 2026-09-28 · Community + Cloud Edition') }}

    @@ -21,8 +21,8 @@

    {{ _('1. Who is responsible?') }}

    {{ _('2. What data we process') }}

    {{ _('2a. Uploaded files:') }} {{ _('Files you upload for conversion or compression are processed entirely server-side and deleted immediately after the converted output is returned to you. No file content is stored, logged, or retained in any form.') }}

    -

    {{ _('2b. User accounts (Cloud Edition):') }} {{ _('If you register an account, we store your email address, a bcrypt hash of your password (never the plaintext), your subscription tier, your account creation timestamp, and — once you upgrade to a paid tier — the Stripe customer identifier that links your account to Stripe. Account data is persisted in our PostgreSQL database for the lifetime of your account. You can delete your account at any time through the dashboard\'s "Delete account" flow, or by request to privacy@filemorph.io. On deletion we erase your password hash and API keys, and cancel any active paid subscription. Conversion-job records are anonymised — your account ID is removed — and the resulting anonymous rows are retained for aggregate service-quality analytics; under GDPR Art. 4(1), they no longer relate to you. Stripe transaction records are retained by Stripe under their own tax-retention obligations. For accounts that have made at least one payment, German tax law (HGB §257, AO §147) obliges us to retain a minimal invoice-link record — your email address, Stripe customer identifier, and last billing tier — for 10 years from the end of the calendar year of your last payment; all other personal data is erased on deletion. This retention is the legal-obligation exception under GDPR Art. 17(3)(b); the data is held solely for tax audit and is hard-deleted at the end of the retention period. Erasure otherwise complies with GDPR Art. 17.') }}

    -

    {{ _('2c. Transactional emails:') }} {{ _('When you request a password reset, we generate a single-use token (valid for 30 minutes) and send it to your registered email address as a reset link. The outgoing message is delivered from') }} no-reply@filemorph.io {{ _('via our email provider (see § 3a). We do not send marketing emails.') }}

    +

    {{ _('2b. User accounts (Cloud Edition):') }} {{ _('If you register an account, we store your email address, a bcrypt hash of your password (never the plaintext), your subscription tier, your account creation timestamp, your preferred language for account emails, the timestamp your email address was verified (once you complete that step), and — once you upgrade to a paid tier — the Stripe customer identifier that links your account to Stripe. We also record one row per API/conversion request you make — the endpoint, the file size, and how long it took, never the file content — to enforce your monthly quota and show usage in your dashboard. Account data is persisted in our PostgreSQL database for the lifetime of your account. You can delete your account at any time through the dashboard\'s "Delete account" flow, or by request to privacy@filemorph.io. On deletion we erase your password hash and API keys, and cancel any active paid subscription. Conversion-job records are anonymised — your account ID is removed — and the resulting anonymous rows are retained for aggregate service-quality analytics; under GDPR Art. 4(1), they no longer relate to you. Stripe transaction records are retained by Stripe under their own tax-retention obligations. For accounts that have made at least one payment, German tax law (HGB §257, AO §147) obliges us to retain a minimal invoice-link record — your email address, Stripe customer identifier, and last billing tier — for 10 years from the end of the calendar year of your last payment; all other personal data is erased on deletion. This retention is the legal-obligation exception under GDPR Art. 17(3)(b); the data is held solely for tax audit and is hard-deleted at the end of the retention period. Erasure otherwise complies with GDPR Art. 17.') }}

    +

    {{ _('2c. Transactional emails:') }} {{ _('When you register, we send a verification link to your email address (valid for 7 days). When you request a password reset, we generate a separate single-use token (valid for 30 minutes) and send it as a reset link. Outgoing messages are delivered from') }} no-reply@filemorph.io {{ _('via our email provider (see § 3a). We do not send marketing emails.') }}

    {{ _('2d. Server logs:') }} {{ _('Our web server automatically records standard access log data for each request: IP address, request timestamp, HTTP method, URL path, response status code, and response size. Application-level events (logins, password-reset requests, subscription changes) are logged as structured JSON without plaintext email addresses — only the email domain is kept for debug purposes. Logs are rotated by the hosting infrastructure under standard operational policies (typically up to 30 days) and accessed only for security, abuse-detection, and debugging.') }}

    {{ _('2e. API keys:') }} {{ _('If you generate an API key (via the dashboard or command-line tool), only the SHA-256 hash of your key is stored. The plaintext key is shown to you exactly once at creation time and is never persisted on our servers.') }}

    {{ _('2f. Contact form submissions:') }} {{ _('If you use the contact form (also linked from our Impressum), we process your message text, your email address, and an optional name solely to handle your inquiry. The message is not stored in our database — it is delivered to the operator by email (via the provider in § 3a) and kept only as long as needed to deal with your request. Legal basis: Art. 6(1)(f) GDPR (handling inquiries), or Art. 6(1)(b) GDPR where it concerns an existing contract. We do not use it for marketing.') }}

    @@ -39,12 +39,12 @@

    {{ _('3. Legal basis (GDPR Art. 6

    {{ _('3a. External services (Sub-Processors)') }}

    {{ _('To operate FileMorph we rely on the following sub-processors. Each one receives only the minimum data needed for its task:') }}

    {{ _('Stripe (payments):') }} {{ _('When you upgrade to a paid tier, we create a Stripe customer record containing your email address and an internal user identifier, then redirect you to') }} checkout.stripe.com {{ _('to complete payment. Stripe handles all card details directly — FileMorph never sees or stores your card data. After checkout, Stripe returns a customer identifier which we store to manage your subscription. Stripe is a US-based company; the transfer is covered by the EU Standard Contractual Clauses under Stripe\'s Data Processing Agreement. Legal basis: Art. 6(1)(b) GDPR. See') }} {{ _('Stripe\'s privacy policy') }}.

    -

    {{ _('Zoho Mail (transactional email):') }} {{ _('Password-reset and other account-related emails are delivered through Zoho Mail (') }}smtp.zoho.eu{{ _(', hosted in Frankfurt, EU). Zoho receives the recipient address and the email contents (including the reset link). Legal basis: Art. 6(1)(b) GDPR. See') }} {{ _('Zoho\'s privacy policy') }}.

    +

    {{ _('Zoho Mail (transactional email):') }} {{ _('Password-reset and other account-related emails are delivered through Zoho Mail (') }}smtp.zoho.eu{{ _(', EU data centres in Amsterdam (NL) and Dublin (IE)). Zoho receives the recipient address and the email contents (including the reset link). Legal basis: Art. 6(1)(b) GDPR. See') }} {{ _('Zoho\'s privacy policy') }}.

    {{ _('4. Hosting') }}

    -

    {{ _('This service is hosted by Hetzner Online GmbH, Germany (Frankfurt data centre). Your requests are routed through Cloudflare\'s network for DDoS protection and performance. See') }} {{ _('Hetzner\'s privacy policy') }} {{ _('and') }} {{ _('Cloudflare\'s privacy policy') }} {{ _('for their data processing terms.') }}

    +

    {{ _('This service is hosted by Hetzner Online GmbH — data centre in the EU. Your requests are routed through Cloudflare\'s network for DDoS protection and performance. See') }} {{ _('Hetzner\'s privacy policy') }} {{ _('and') }} {{ _('Cloudflare\'s privacy policy') }} {{ _('for their data processing terms.') }}

    @@ -58,7 +58,7 @@

    {{ _('6. No cookies, no tracking'

    {{ _('FileMorph sets') }} {{ _('no cookies') }} {{ _('on its own domain and runs no tracking or analytics scripts. German law (§ 25 TDDDG) treats your browser\'s') }} localStorage {{ _('the same as cookies, so the few keys we write there while you are signed in are listed in full:') }}

    • fm_access_token / fm_refresh_token — {{ _('your JWT session tokens; strictly necessary to keep you signed in.') }}
    • -
    • filemorph_api_key — {{ _('optional; remembers the API key shown in your dashboard across reloads so you don\'t need to paste it again. Cleared on logout.') }}
    • +
    • filemorph_api_key — {{ _('legacy; current versions no longer write it, but a value saved by an older version is still read. Cleared on logout and when you delete your account.') }}
    • fm_cookie_notice_dismissed — {{ _('remembers that you closed the informational cookie notice shown to first-time visitors, so it appears only once. The only key ever written while you are not signed in.') }}

    {{ _('All of them are strictly necessary for the function you asked for, so they fall under the exemption in Art. 5(3) ePrivacy Directive / § 25 Abs. 2 Nr. 2 TDDDG — no consent banner is required. The cookie notice we show first-time visitors is informational only: it asks for no consent because none is needed. If you are not signed in, nothing is stored in your browser until you dismiss that notice — and then only the dismissal flag listed above.') }}

    @@ -68,12 +68,12 @@

    {{ _('6. No cookies, no tracking'

    {{ _('7. Rate limiting & abuse prevention') }}

    -

    {{ _('We apply a temporary in-memory rate limit (max. 10 requests per minute per IP address) to protect the service from abuse. IP addresses used by the rate limiter are processed transiently in memory only and are never written to disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate interest in service stability and security.') }}

    +

    {{ _('We apply in-memory rate limits to protect the service from abuse — set per route, from 5 contact-form messages per hour up to 120 requests per minute. Several account routes count the limit per signed-in account rather than per IP address, and failed API-key attempts get a separate per-IP budget. IP addresses and account identifiers used by the rate limiter are processed transiently in memory only and are never written to disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate interest in service stability and security.') }}

    {{ _('8. Changes to this policy') }}

    -

    {{ _('We may update this policy when the service changes. Accounts and billing are live as of April 2026; persistent file history (Cloud Edition Phase 2) is planned but not yet implemented. The date at the top indicates the current version.') }}

    +

    {{ _('We may update this policy when the service changes. Accounts are live; paid plans are not yet available. Persistent file history (Cloud Edition Phase 2) is planned but not yet implemented. The date at the top indicates the current version.') }}

    diff --git a/app/templates/security.html b/app/templates/security.html index d6d9516..28a9d5b 100644 --- a/app/templates/security.html +++ b/app/templates/security.html @@ -16,11 +16,6 @@

    {{ _('Reporting a vulnerability') {{ security_contact_email }} {{ _('with a clear description of the issue and any steps required to reproduce it. Encrypted mail is welcome — request our PGP key at the same address if needed.') }}

    -

    - {{ _('For issues in the open-source codebase itself, you may alternatively use') }} - {{ _('GitHub Security Advisories') }}. - {{ _('That channel is preferred for issues that affect every self-hosted instance, since the repository maintainers can coordinate a CVE and release a patched version centrally.') }} -

    diff --git a/app/templates/terms.html b/app/templates/terms.html index fced80c..753e323 100644 --- a/app/templates/terms.html +++ b/app/templates/terms.html @@ -6,7 +6,7 @@
    {{ _('← Back') }}

    {{ _('Terms of Use') }}

    -

    {{ _('Last updated: 2026-06-22 · Community + Cloud Edition') }}

    +

    {{ _('Last updated: 2026-09-28 · Community + Cloud Edition') }}

    @@ -55,7 +55,7 @@

    {{ _('7. Governing Law') }}

    {{ _('8. Subscriptions (Cloud Edition)') }}

    -

    {{ _('Paid plans (Pro €7/month, Business €19/month) are billed monthly via Stripe and renew automatically until cancelled. You may cancel at any time through the Stripe customer portal linked from your account dashboard; cancellation takes effect at the end of the current billing period and you retain access for the remainder of that period.') }}

    +

    {{ _('Paid plans are billed monthly via Stripe, at the prices shown on the pricing page at the time of your order, and renew automatically until cancelled. You may cancel at any time by emailing') }} legal@filemorph.io{{ _('; cancellation takes effect at the end of the current billing period and you retain access for the remainder of that period.') }}

    {{ _('Refunds are not issued for partial billing periods. In the event of a documented service outage exceeding 24 hours within a single billing period, you may request a discretionary credit by contacting:') }} legal@filemorph.io.

    {{ _("Payment processing is handled by Stripe Payments Europe Ltd.; Stripe's terms apply to the processing of your payment details.") }}

    diff --git a/locale/de/LC_MESSAGES/messages.mo b/locale/de/LC_MESSAGES/messages.mo index 6816fec37d476abce0c07f60957525af31f662ce..343219890d46c6774ba32ec9b59794c98fed5cd4 100644 GIT binary patch delta 17113 zcmZ|V2YeM(-v99#TIjvk0bB@}gwTuhA{{~(EO?Vlk}Eg&hFgH3cv;1jrlJ9Y1yBSP z5h)QO3MfWJU9sYdEB1m578Kj+D$nOTXIT9|ujjw7-F?rQa^{@h`JFT805u=soM~CFP&|eM@Sg6LRR!P10k&nG#Br3H^|Y)Sn2ozJ zgmbV>FUz88YZ11@>u@-hVjas$TL0vrDJQ-!S}H(p2r5*wvXv(CZ_U!Ya$1F!2;}rw_+>Yhc)qa)PpBcJ@^`% zV2!?()fn5M&iBHmI2qNTJnV=IQ60G(9o&a%&_A#P@3(&7;5uyH&$0&MI#dtdKsDej zHp5?0p={LOvZ`YF2K5Y5|iT@!!sPz<3!&}%!8v* zFPx3)$$U)18&DPQ!fIHK3i)AF&tFFRVts-{+iFE`^x`RaKVF3z+Dd~=L+cJA{?E{W z&YT#@ z=iZ=~8v74$4E~by9JJ@4H>wA-(4xgIDx`U1Eh`=8qe8eDH4XQnDtZw= zz>`=V4~{b(c@A~`4ODJ?hKk^S(7~qTz1&J#eL2urjX|~WYIDH~`7S}thFkseBff`F z4SvNhpTyRbze2sZ<^(e>+h8Zk{ZPp~7d13Jz}L#1E)Y-89i;`F&WM@^-9= z`%yhSf(qSBxD`+1GQ4e)d9Lwf(}32f7j^T?V{kd;%drlAip_Yx^&JN(SToBktqrj| zP6dqA4m1{D0asWQ7>*tCU?P>*bPTwD#lUuZk$H^ z72*duQ4b$Om7hf=&FiS#_ySddHQkgmQA0HbJ7O;Co3I?!upOw#J%Q@jaa0a{gK1c2 z2CE|um_hv2l9ilL57wfl&rZ}Bm7yv;fk&C#d=Sxr#EXB|80aOQ9=Xon((%Qm-lH>>~2hL!9{1p|E z+WBTyG{(-9TcC1b81}~bxELQqjdh0t%OW_|AkAs=je71g?1w+1lDXSF^AQ_}RVZ&qoqrSu z@_y?{4wM`}qlRF4$ZWMz3N7mjLOL9YwzWBIS%1Y!5z9J(%TPVNvWQPEE!!`-|E`6m zq1P9ihTnt(xo;t1s)3JTPdtpt4>|bGzwjn;QHb8d$@o3i!;#mSq?wKyyKQ(eK8c!6 zr*IrLUSej&TvUUj*aF|en)nSWg1_KWw67=rn>o0FZdXTbhcu=aqgooksyGkzqWMTC ztxc#3>n%0YuO(^-2I3+dhr_V~XJ9qLmWfxOLVpXY{=5E4MS5_ne_=0nq5Kr4;J>gT zev8Mj$_=K1H&6}w0Cj)u8_oRhgNoQ(zZ^k_@&;6c51<?UGY-NBmMF!Y)N?=YFZ|r=0Gdf zt2iD%MYSyRZ{`L4Q4yGe127k>;$~C@+fY4v3_Da}8skLDpDs6%?XkktHxz4dek`g1 z)37z~x56B#2dn%GccWggA2l=wksGa-upOSlGnlf{B;h&KJa4zk+&9?wa#RBsq3XFA z58^sh1E#K4!x;ZU4mxvU0rCN~Hlad$7HebmTln%}6YPa0I2>QVGg#|ZbKg%mgmRVJ zOb15d9LnRc6>h`!cmy>oKEbxU-};RM2V1W(V><}d(#c8LvYP?Na~zI@I&`<4~K043k8|`jXlU8M{R#=7!JLnceBv$Z%SH*PE}^ zVN_3ELq+UMRF4{$n(KW~Sw04pq|;Cx$j9**$By_kX5go##9u9Mw1Em~atBm-+}(s1 zXQPs_!#$>F7o*0mKdNWbQ7;Pm*Oy>5%B!#*-hm4B7MzL)Q5~wc(IoZyjl^F)+Rh2R z_&ML>*oN{as2A9qOa)D_0p$YJ{fkj!yaM&&Pw`?rgH^EEW-~2YV=Cn+UW<35>S>m| z*Rm#Y&;>PSi%>7T4i&mJsERiFw*A~?)1Pr#%?b`=K>Mo?qE20P+*R3wg}?t2$U zSxEdAGQdCy&z}k2S2jEM{5@S_;lrJA{Kt=4zUBv%52kUp~w;2E1%};84 z8$ZOAd(0mk%;V$|%Js@v6Yv()G&+UdG3PN8`gQm%<>MH`=l3z?aKL_L8bgh0PvA@8y><%Pngj6dD61FQXYd^pHh{pNn z^H52+7&CDdw#I{~7rlc@x^t)@vyPfb)+s+_#d2uS;~r8FOa#McnI57=F|A1 z$#y)AB_+!bHP3Va9Gy<(Cr@~W9NCvgtvd%Z^e zyx+QwgVnrv4=S{SkDGmd1**bPZ?Qkb_pu!w_=ow(yo2i~SACnEEZ&Vd*yn_072{oa zH#VjfopCoRIZt34tW2dzg`_bx&#YvbSe5fLP_yQ8 zY=Kd9a5d`1JFyBrfi3YV)O-Gk!|^mKa_R3Ae~oR$`=%$aqI&pGRMMP9z2Llmy~PLS z{6(l9^+YXPgHVy!jtcc-*bq-*7yJe_En9qOK1S)N_x4HpCx)Y1JRTp!EPTo4Hz8K1 zT=B7K*vmMV^2eyL9R3M=1@0?EHQ@B8=C|DssB)LjOvHMjB0B)na2SrnWSE0o4$4qV zY4gv`jpJ|=zA!6aYt-{2P(v{nt84y8 zI8bO7p(homq~i{3hVS4-_${`@)b6qtS^c>($KdpDq>wxk?4(j;S_9&S7A#`Bsoxs)}V%B zD{3q&a2%dM^|0e_=6V-YgT|mL3ZTY%5h`-Gq9U~kpR8nAk6F+VtYHGfvA-*iUaXFY={T28orFm zi8rx5evE2xm8!Nnp0rwWpbHtOmS&+UD8{z5cr_}NJF3~<7HU7Lg7;97JBPYICB^ps zM6)F-)HSM`o;F3@*9jH50jT#)L+|hZ5*%oZHlcFi2~-G=qbmLwRlyJFV2v8KH$+`g z4H<&EZ-#%q05#o~pk~#BzDH3JI)xg#i)z|dXY$XwgoDMHgLmNjsN|Sm%l0-Ht5IXN z7L{aMQA4#G^`ZlK5cQu5Dt?4Zuvb0X+ZFG{ODO*vXJhaBrop%QZbsF+3l*uyQA70i`gYPgae@;{ zqBE$T|A0}f-@sIKBkFnyswW#!kvM|tz%kU=ocros6 zm^2l?&k4=rFHmFii(hWg$c%YMRML#Zv6zSI$!_$fD`rqWfti@n*fgvkwx&E9l?%D3 zdX}LY@IaD-ZXCRUYCxqXCe)2l(d8K*Ql|w`P&4Av?gi^?TD%%h!I?d>e=^5u&f3xY^y6? z)YA6;UBKj4wzY-pD_h&%XFI)(ZSA34n>oD{594K;|3li@)~}q{fQrOg$M%-kXHi)n zXm49j;Cx(*X&r2fMa?>b)A6Z}=DEff*%nKMH4-1gaepz>_IxMP&_>rA^Q@#$>@VbP<#kQD+%TPnM z2Nl_4c%$b3=Nu@juesP{^{uE}xXbqu)EFK{hShq}FMp6`?mvzCcKnE1x|^h%St$P$`S>guXp;EYVB7kN>&LMlJ~PBb^4B5Ee}%B>P(s5CMxa7I zcbG}a7*3?T5*5-DI0L^xExm(>n|VGRdsAM4UQVDw+<1ifh^3>-TTzibgjzpd7{UBs z$-!|>vhmS z{|x?x@*CJ5e^!~KZ8^oZhEg6n)vWPDbr0)x}&~Q z15lwVK&=ahQPcEeycxg2F}QGsnYK^kBpOn4rb(*Vv&^i?M}0%~;X*u!8nRxOnVnP? zCe`zW9H_->Q9nHPqNdB+I1taG8jwEQ_Ws;&0IK3UP|0;Cs-e%LlK3=gNUb@x_dB5> zwxHM)72!-&ZVa8n{O8yj!~fmNiTf|Nt?i6mk1Na=oxIBS{s^RZo zXZ#U0TiRZ2K2l>**`15(NDMUut1*c8p^~^(AZfCCNWfHFjDN4pji?@6=GxYGcnvB7 zSLT_8=^9j|{)&p+1E?N8i|XO~s3iLuwIu(Hy01pQ+1PZ(J1F0kiKR|j}D=FdK5K1KSK5JD^xwdqasj0WXcX|-_Qd);6zk|ijbj6 zS_?SP*XcIwgIiEz^$x0sU!y|z8>*sKh30x6)cNtKii0=;SECyC8Y;3UQ4RVYm84a| zCTCKy2Ki@YaiD26Ct^O2AERD$b&;v)T2v2jMkU(@R3x58CDTi&B$}nS0bSSp-^GCzl%uE?{tFf2I#Dy9JEI!Z8_!@CF2I2?6QMHHm_CD}@GMTj zK5_H;UXIH4k8mV5Nth1I!DJmyW_y40aRct8-1}zRx*9)0?bSvuH(5LuHLqvmM7$SQIZ{2EI*z-2?QCWzZx7VY_Zau2P zhfzIv0WYh>pAF-3%AN0^Cseo_H59Lwn8?goYg_M9eh3|0w$2RQHY}vvpB-&8eg7Zi zpa&=KI+S~%=JgCz!$Q~{m!cZD7qt+*hU)P-tb%Asd&fjNSS8#px7UsW_?_V5r!nya`)?BjfZ>X=+=MUQ6KOQT7$oBp* zS)=V{{{I7Y|ML%c1UT??%+OW-Ip6{NKqz7AHQ&CD`+z`7O5x)xuMF0KY}8WIGO- zf1`d>*{S$tehgIiiMej;kuePOKnUL@zSD7vx03@nAR+NyI|M zPIe*~iu)A=3fw@h6Up=L3cHKS-W=JYTFL;&Eer%hC1=N-9-LF)=FD@ViLe)w5o4W5 z*l`!Q(PF145i4-w5ros)RT@=Vsm=?Pw zb7v2%)GxE_p4rXplGMMqD>-{v<3S%UUHgB&AVDzwD$}!xZ0>(w7Nj%5FvA?kE&JD; zXRFi?1)}+G)ESmFmM0?dK-vDeDb>oV<<+ZPmKofh(!V&8h&r@0&y7Z@Cq&p28i>3| z)FB5ViE!M+F2;B>vhiRcVF(l!l?;2bao2@`m_z7-c|o2{jcBZ>&}&V|jcecnbT94} zbt!qfs3zgz9f{nLAt?ObwI?UEiV+ccpOyq886MT5*9 zGB&a>Tr#($NlX8{f~VZ{Y9az|G_7Rv%2Bn*5HCN2;ry~0t1>IKAM4%Id4WqV<`Oig zC{SETyVIP7E;G?BDrpvP*G6O1J<}<61JRh1I@*mEl2Y-&B1Umxm$DmgnO(12AgZS4 z21!g(kj@42qb_4bcht~WqNpemjeB8FW|Y+~-3KjeGD*lwBnFU^AGGa>qu~|+aH&@Gr$>cm% zD~5?_o6f>ZUd7$QqIk^7rzZg?<`xCKj`Cjn_Q?cH$fCdDzLv%E&?BvrASLu`=)W^e1z!;N| zR+x#O6Yz$T(Va)Uv??@CUBGIW>n$OD`3M!qx@0&d*FM)H#cy`Wj^}!JV&w=BSq+Vz z&2T0s;`z+%{6B_*29*`uQi_3cxGNKP?+ZE zEx8H4VFeXiLvcRhc@+nvJRf$Z>XXi^V$_x8KURHCBA?|yFQa7a*(d7w1t(I#%3k*U z*=y?jX}3UzvR@#z3Z3p5&bUN4=%ninK-9l%e=`@=IrDNO=0n;cuz;q3p8niscyjX=R zw&oO&=V51bFrHra;qR?dY7KBkrB7rwkUr&hHG6hCT}-$}9Cm2Xe6GisNQYiyTS zy82Jwe?2qP+hF7d=q9s~uK^#CuoKodoZ=L3n?Z}5dD?ui3KB&=g29{u;?BF>AOpju zV;->`9j(}3vAOhoQ+os(kMa@C>~3~x^}FmgrE8kntw;X9dyhZf?Z1*eK%5B2SPTCD zuP)!$-0oGiMZWhYm*qw48Ifb_#vsMr@^fwMe^)Ed_={awxqN@BeZG1!jmW8y+3tc! zba5h|hE6Nu)5L=6C8C~=q?-|8tyJ&lFxHG&VUZj1!akflB?e|Lc(q0SAxt0UEwQeX z8jEBHLSCYK2_1H({w1gDCkyfj&~{D*WF&39G0lqtT-1sZZt38^_bbh zM?T{9ryEUTEs7IsO{hRPFRBfL*9)3PJ}Z=JC^NAr5uV40Xu^)Sx5d`>!d_{vC`Aa zOQKuI%l&N(S-m*1n1;CF@$K9wlkGkkbEwH*-RKhytvJXnnmtkOW_zl9`D2cD%(CyPlrqa@`O8g|4(n~V ztk1q(3mQSB0>-X%V{f}hJujcwKaC3%keaEb^LyJ(>@MYprrJ+77?VCH$lBwC$vou- zS?}gY`DNj!|AjpjozdDd&FrJnXOahuzCJLtDmxI3m-lz=jx~~_f-LMIb}()*{ZemF zHSf<^WXqCg$e@`y{T$A`8u;L-MD21I`@g=uazox;NZvYsFS(4yl+v zidL|c#$xT5Jzh>z6-nSl>`&{VTl$|y_N3DHr`xT{7l!TO)l0s8q-pv3g?6=Ct-b91 z)2E5S@RzqyGs-J&u&dW-GB%t((c68cPo;zD6TID5Y3tQ?hWR{Q(yNd`;2TatX&vF~&no6!9Gi7vaWJ32Jhvdkc95lj$xMD}|GO`XIm5jjnNwF?`J?L6ajWgF4YZb;_Kqh>8L0C4tL=O3 zq5Rh62bi}joA-auH~k#WWxqpCnl+s~Ba8D%H1=tw{cp7g*=gkqZneLu(m$7AvfI$M zEy~iQDWH8DVbS8H3HmA z@|nx@etd>AOM4@}%7siFFB!}#>7AKSv7Z5_V(+72*40RiABk~(9f#xIUN6JRO!GE- s|@ck)4yiJXkI6rHZCu+AA(2UY24%nst1HRnfLjV8( delta 15846 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 diff --git a/locale/de/LC_MESSAGES/messages.po b/locale/de/LC_MESSAGES/messages.po index b4a92f9..db4b0de 100644 --- a/locale/de/LC_MESSAGES/messages.po +++ b/locale/de/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: FileMorph VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-28 19:39+0200\n" +"POT-Creation-Date: 2026-09-29 08:59+0200\n" "PO-Revision-Date: 2026-05-08 11:00+0200\n" "Last-Translator: FileMorph \n" "Language: de\n" @@ -56,13 +56,13 @@ msgid "+ New Key" msgstr "+ Neuer Key" msgid "" -", hosted in Frankfurt, EU). Zoho receives the recipient address and the " -"email contents (including the reset link). Legal basis: Art. 6(1)(b) " -"GDPR. See" +", EU data centres in Amsterdam (NL) and Dublin (IE)). Zoho receives the " +"recipient address and the email contents (including the reset link). " +"Legal basis: Art. 6(1)(b) GDPR. See" msgstr "" -", gehostet in Frankfurt, EU). Zoho erhält die Empfänger-Adresse und die E" -"-Mail-Inhalte (einschließlich des Reset-Links). Rechtsgrundlage: Art. " -"6(1)(b) DSGVO. Siehe" +", EU-Rechenzentren in Amsterdam (NL) und Dublin (IE)). Zoho erhält die " +"Empfänger-Adresse und die E-Mail-Inhalte (einschließlich des Reset-" +"Links). Rechtsgrundlage: Art. 6(1)(b) DSGVO. Siehe" msgid "" ". To exercise other rights (access, rectification, restriction), contact " @@ -179,6 +179,13 @@ msgstr "9. Admin-Zugriff (Transparenz)" msgid "9. Right of Withdrawal (Consumers)" msgstr "9. Widerrufsrecht (Verbraucher)" +msgid "" +"; cancellation takes effect at the end of the current billing period and " +"you retain access for the remainder of that period." +msgstr "" +" — die Kündigung wird dann zum Ende der laufenden Abrechnungsperiode " +"wirksam, und du behältst für den Rest dieser Periode Zugriff." + msgid "A description of the vulnerability and its potential impact." msgstr "" "Eine Beschreibung der Sicherheitslücke und ihrer potenziellen " @@ -205,6 +212,9 @@ msgstr "API-Doku ↗" msgid "API Keys" msgstr "API-Schlüssel" +msgid "API access without a key" +msgstr "API-Zugriff ohne Schlüssel" + msgid "API calls / month" msgstr "API-Aufrufe / Monat" @@ -420,17 +430,18 @@ msgstr "Behörden · Krankenhäuser · Kanzleien" msgid "" "Being transparent about what is not done yet: no external ISO 27001 " "certification and no external penetration test yet (both planned once a " -"paying pilot justifies the cost), and no tagged release cut yet — the " -"signing/SBOM pipeline is wired in CI and produces those artefacts on the " -"first tag. Everything else above is in the repository today and " -"auditable." +"paying pilot justifies the cost). v1.1.0, tagged 2026-06-01, is the " +"latest signed release with a CycloneDX SBOM and an image digest; changes " +"since then ship in the continuously built image ahead of the next tag. " +"Everything else above is in the repository today and auditable." msgstr "" "Transparent zu dem, was noch fehlt: noch keine externe " "ISO-27001-Zertifizierung und kein externer Penetrationstest (beide " -"geplant, sobald ein zahlender Pilot den Aufwand rechtfertigt), und noch " -"kein getaggtes Release — die Signier-/SBOM-Pipeline ist in der CI " -"verdrahtet und erzeugt diese Artefakte beim ersten Tag. Alles andere oben" -" liegt heute im Repository und ist prüfbar." +"geplant, sobald ein zahlender Pilot den Aufwand rechtfertigt). v1.1.0, " +"getaggt am 1. Juni 2026, ist das aktuelle signierte Release mit " +"CycloneDX-SBOM und Image-Digest; Änderungen seitdem laufen im fortlaufend" +" gebauten Image, bis zum nächsten Git-Tag. Alles andere oben liegt heute " +"im Repository und ist prüfbar." msgid "Best quality" msgstr "Beste Qualität" @@ -576,8 +587,8 @@ msgstr "Kompressions-Modus" msgid "Concurrent slots" msgstr "Gleichzeitige Slots" -msgid "Confidential inquiries via" -msgstr "Vertrauliche Anfragen über" +msgid "Confidential inquiries: email" +msgstr "Vertrauliche Anfragen: E-Mail an" msgid "Confirm Password" msgstr "Passwort bestätigen" @@ -610,11 +621,11 @@ msgid "Confirming your email…" msgstr "Deine E-Mail-Adresse wird bestätigt…" msgid "" -"Conformance gate runs as CI workflow. No release without green veraPDF " -"against a worst-case source PDF." +"Conformance check runs in CI on every push to main and every pull " +"request, against a worst-case source PDF." msgstr "" -"Konformitäts-Gate läuft als CI-Workflow. Kein Release ohne grünes veraPDF" -" gegen einen Worst-Case-Source-PDF." +"Die Konformitätsprüfung läuft in der CI bei jedem Push auf main und jedem" +" Pull Request, gegen ein Worst-Case-Quell-PDF." msgid "" "Conformance secured by veraPDF CI gate against a worst-case source PDF — " @@ -888,13 +899,6 @@ msgstr "" "Alle Konvertierungen von FileMorph — Bilder, Dokumente, Tabellen, Audio " "und Video, plus Komprimierung auf eine Zielgröße. Kostenlos, Open Source." -msgid "" -"Every operation in a continuous hash chain. Tampering with an old row " -"breaks every following one." -msgstr "" -"Jede Operation in einer fortlaufenden Hash-Chain. Manipulation an einer " -"alten Zeile bricht die nachfolgende Kette." - msgid "" "External ISO 27001 certification and external pen test are planned as " "Year-2 roadmap items — both will be implemented as soon as the first " @@ -935,34 +939,38 @@ msgstr "FileMorph Compliance Edition" msgid "" "FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size " "(for example 5 MB) using a binary search — useful for upload limits and " -"email attachments." +"email attachments. PNG, TIFF and video below compress by quality only, " +"not to an exact size." msgstr "" "FileMorph kann ein JPEG-, WebP- oder AVIF-Bild per Binärsuche auf eine " "exakte Zielgröße verkleinern (zum Beispiel 5 MB) — praktisch für Upload-" -"Limits und E-Mail-Anhänge." +"Limits und E-Mail-Anhänge. PNG, TIFF und Video darunter werden nur per " +"Qualität komprimiert, nicht auf eine exakte Größe." msgid "" -"FileMorph closes this gap: the open-source engine covers 16+ format pairs" -" and runs on your own Hetzner / on-premises / air-gap infrastructure. The" -" Compliance Edition additionally provides the contracts, SLAs and roadmap" -" guarantees that an EVB-IT-compliant procurement requires." +"FileMorph closes this gap: the open-source engine covers 180+ format " +"pairs and runs on your own Hetzner / on-premises / air-gap " +"infrastructure. The Compliance Edition additionally provides the " +"contracts, SLAs and roadmap guarantees that an EVB-IT-compliant " +"procurement requires." msgstr "" -"FileMorph schließt diese Lücke: Die Open-Source-Engine deckt 16+ Format-" +"FileMorph schließt diese Lücke: Die Open-Source-Engine deckt 180+ Format-" "Paare ab und läuft auf Ihrer eigenen Hetzner / On-Premise / Air-Gap-" "Infrastruktur. Die Compliance-Edition liefert zusätzlich die Verträge, " "die SLAs und die Roadmap-Garantien, die ein EVB-IT-konformer Einkauf " "voraussetzt." msgid "" -"FileMorph converts between the formats below and compresses images to an " -"exact target size (video too, by quality). Everything runs for free, with" -" no account — or self-host it under AGPLv3. Pick any pair and start on " -"the" +"FileMorph converts between the formats below and compresses JPEG, WebP " +"and AVIF images to an exact target size — PNG, TIFF and video use " +"quality-based compression instead. Everything runs for free, with no " +"account — or self-host it under AGPLv3. Pick any pair and start on the" msgstr "" "FileMorph konvertiert zwischen den folgenden Formaten und komprimiert " -"Bilder auf eine exakte Zielgröße (Video ebenfalls, per Qualität). Alles " -"läuft kostenlos und ohne Konto — oder hoste es selbst unter AGPLv3. Wähle" -" ein beliebiges Paar und starte im" +"JPEG-, WebP- und AVIF-Bilder auf eine exakte Zielgröße — PNG, TIFF und " +"Video werden stattdessen per Qualität komprimiert. Alles läuft kostenlos " +"und ohne Konto — oder hoste es selbst unter AGPLv3. Wähle ein beliebiges " +"Paar und starte im" msgid "" "FileMorph is a file conversion and compression service. The Community " @@ -1022,10 +1030,11 @@ msgstr "FileMorph unterliegt deutschem Recht (Hamburg)." msgid "" "FileMorph pricing: the open-source converter is free to self-host. Paid " -"plans add volume, higher limits and API access." +"plans add volume, higher limits and larger API quotas." msgstr "" "FileMorph-Preise: Der Open-Source-Konverter ist kostenlos selbst hostbar." -" Bezahlte Tarife bieten mehr Volumen, höhere Limits und API-Zugang." +" Bezahlte Tarife bieten mehr Volumen, höhere Limits und größere API-" +"Kontingente." msgid "FileMorph sets" msgstr "FileMorph setzt" @@ -1114,9 +1123,6 @@ msgstr "" msgid "For developers & SaaS" msgstr "Für Entwickler & SaaS" -msgid "For issues in the open-source codebase itself, you may alternatively use" -msgstr "Für Probleme im Open-Source-Codebase selbst kannst du alternativ" - msgid "For public sector & compliance" msgstr "Für Behörden & Compliance" @@ -1189,9 +1195,6 @@ msgstr "API-Key holen →" msgid "Get started free" msgstr "Kostenlos starten" -msgid "GitHub Security Advisories" -msgstr "GitHub Security Advisories" - msgid "GitHub ↗" msgstr "GitHub ↗" @@ -1314,43 +1317,53 @@ msgstr "" msgid "" "If you register an account, we store your email address, a bcrypt hash of" " your password (never the plaintext), your subscription tier, your " -"account creation timestamp, and — once you upgrade to a paid tier — the " -"Stripe customer identifier that links your account to Stripe. Account " -"data is persisted in our PostgreSQL database for the lifetime of your " -"account. You can delete your account at any time through the dashboard's " -"\"Delete account\" flow, or by request to privacy@filemorph.io. On " -"deletion we erase your password hash and API keys, and cancel any active " -"paid subscription. Conversion-job records are anonymised — your account " -"ID is removed — and the resulting anonymous rows are retained for " -"aggregate service-quality analytics; under GDPR Art. 4(1), they no longer" -" relate to you. Stripe transaction records are retained by Stripe under " -"their own tax-retention obligations. For accounts that have made at least" -" one payment, German tax law (HGB §257, AO §147) obliges us to retain a " -"minimal invoice-link record — your email address, Stripe customer " -"identifier, and last billing tier — for 10 years from the end of the " -"calendar year of your last payment; all other personal data is erased on " -"deletion. This retention is the legal-obligation exception under GDPR " -"Art. 17(3)(b); the data is held solely for tax audit and is hard-deleted " -"at the end of the retention period. Erasure otherwise complies with GDPR " -"Art. 17." +"account creation timestamp, your preferred language for account emails, " +"the timestamp your email address was verified (once you complete that " +"step), and — once you upgrade to a paid tier — the Stripe customer " +"identifier that links your account to Stripe. We also record one row per " +"API/conversion request you make — the endpoint, the file size, and how " +"long it took, never the file content — to enforce your monthly quota and " +"show usage in your dashboard. Account data is persisted in our PostgreSQL" +" database for the lifetime of your account. You can delete your account " +"at any time through the dashboard's \"Delete account\" flow, or by " +"request to privacy@filemorph.io. On deletion we erase your password hash " +"and API keys, and cancel any active paid subscription. Conversion-job " +"records are anonymised — your account ID is removed — and the resulting " +"anonymous rows are retained for aggregate service-quality analytics; " +"under GDPR Art. 4(1), they no longer relate to you. Stripe transaction " +"records are retained by Stripe under their own tax-retention obligations." +" For accounts that have made at least one payment, German tax law (HGB " +"§257, AO §147) obliges us to retain a minimal invoice-link record — your " +"email address, Stripe customer identifier, and last billing tier — for 10" +" years from the end of the calendar year of your last payment; all other " +"personal data is erased on deletion. This retention is the legal-" +"obligation exception under GDPR Art. 17(3)(b); the data is held solely " +"for tax audit and is hard-deleted at the end of the retention period. " +"Erasure otherwise complies with GDPR Art. 17." msgstr "" "Wenn du ein Konto registrierst, speichern wir deine E-Mail-Adresse, einen" " bcrypt-Hash deines Passworts (niemals den Klartext), deinen Abonnement-" -"Tarif, den Zeitstempel deiner Kontoerstellung und — sobald du auf einen " +"Tarif, den Zeitstempel deiner Kontoerstellung, deine bevorzugte Sprache " +"für Konto-E-Mails, den Zeitstempel der Bestätigung deiner E-Mail-Adresse " +"(sobald du diesen Schritt abschließt) und — sobald du auf einen " "kostenpflichtigen Tarif upgradest — den Stripe-Kunden-Identifikator, der " -"dein Konto mit Stripe verknüpft. Kontodaten werden für die Lebensdauer " -"deines Kontos in unserer PostgreSQL-Datenbank gespeichert. Du kannst dein" -" Konto jederzeit über den \"Konto löschen\"-Vorgang im Dashboard oder per" -" Anfrage an privacy@filemorph.io löschen. Bei der Löschung entfernen wir " -"deinen Passwort-Hash und deine API-Schlüssel und kündigen ein etwaiges " -"aktives kostenpflichtiges Abonnement. Konvertierungs-Job-Datensätze " -"werden anonymisiert — deine Konto-ID wird entfernt — und die so " -"entstehenden anonymen Zeilen werden zur aggregierten Service-" -"Qualitätsanalyse aufbewahrt; nach DSGVO Art. 4 Abs. 1 beziehen sie sich " -"nicht mehr auf dich. Stripe-Transaktionsdatensätze werden von Stripe " -"aufgrund eigener steuerrechtlicher Aufbewahrungspflichten aufbewahrt. Bei" -" Konten, die mindestens eine Zahlung getätigt haben, verpflichtet uns " -"deutsches Steuerrecht (HGB §257, AO §147), einen minimalen Rechnungs-" +"dein Konto mit Stripe verknüpft. Außerdem erfassen wir pro " +"API-/Konvertierungsanfrage eine Zeile — den Endpunkt, die Dateigröße und " +"die Dauer, nie den Dateiinhalt —, um dein monatliches Kontingent " +"durchzusetzen und die Nutzung in deinem Dashboard anzuzeigen. Kontodaten " +"werden für die Lebensdauer deines Kontos in unserer PostgreSQL-Datenbank " +"gespeichert. Du kannst dein Konto jederzeit über den \"Konto " +"löschen\"-Vorgang im Dashboard oder per Anfrage an privacy@filemorph.io " +"löschen. Bei der Löschung entfernen wir deinen Passwort-Hash und deine " +"API-Schlüssel und kündigen ein etwaiges aktives kostenpflichtiges " +"Abonnement. Konvertierungs-Job-Datensätze werden anonymisiert — deine " +"Konto-ID wird entfernt — und die so entstehenden anonymen Zeilen werden " +"zur aggregierten Service-Qualitätsanalyse aufbewahrt; nach DSGVO Art. 4 " +"Abs. 1 beziehen sie sich nicht mehr auf dich. Stripe-" +"Transaktionsdatensätze werden von Stripe aufgrund eigener " +"steuerrechtlicher Aufbewahrungspflichten aufbewahrt. Bei Konten, die " +"mindestens eine Zahlung getätigt haben, verpflichtet uns deutsches " +"Steuerrecht (HGB §257, AO §147), einen minimalen Rechnungs-" "Verknüpfungsdatensatz — deine E-Mail-Adresse, deinen Stripe-Kunden-" "Identifikator und deinen letzten Abrechnungstarif — für 10 Jahre ab Ende " "des Kalenderjahres deiner letzten Zahlung aufzubewahren; alle übrigen " @@ -1490,11 +1503,8 @@ msgstr "" msgid "Language" msgstr "Sprache" -msgid "Last updated: 2026-06-22 · Community + Cloud Edition" -msgstr "Stand: 22.06.2026 · Community + Cloud Edition" - -msgid "Last updated: 2026-08-18 · Community + Cloud Edition" -msgstr "Stand: 18.08.2026 · Community + Cloud Edition" +msgid "Last updated: 2026-09-28 · Community + Cloud Edition" +msgstr "Stand: 28.09.2026 · Community + Cloud Edition" msgid "Last used" msgstr "Zuletzt verwendet" @@ -1778,18 +1788,14 @@ msgid "Pages to keep" msgstr "Zu behaltende Seiten" msgid "" -"Paid plans (Pro €7/month, Business €19/month) are billed monthly via " -"Stripe and renew automatically until cancelled. You may cancel at any " -"time through the Stripe customer portal linked from your account " -"dashboard; cancellation takes effect at the end of the current billing " -"period and you retain access for the remainder of that period." +"Paid plans are billed monthly via Stripe, at the prices shown on the " +"pricing page at the time of your order, and renew automatically until " +"cancelled. You may cancel at any time by emailing" msgstr "" -"Bezahlte Pläne (Pro 7 €/Monat, Business 19 €/Monat) werden monatlich über" -" Stripe abgerechnet und verlängern sich automatisch bis zur Kündigung. Du" -" kannst jederzeit über das im Account-Dashboard verlinkte Stripe-" -"Kundenportal kündigen; die Kündigung wird zum Ende des laufenden " -"Abrechnungszeitraums wirksam, und der Zugang bleibt bis zum Periodenende " -"erhalten." +"Kostenpflichtige Tarife werden monatlich über Stripe abgerechnet, zu den " +"Preisen, die zum Zeitpunkt deiner Bestellung auf der Preisseite angezeigt" +" werden, und verlängern sich automatisch bis zur Kündigung. Zum Kündigen " +"genügt jederzeit eine E-Mail an" msgid "Paid tiers — coming soon" msgstr "Bezahlte Tarife — demnächst verfügbar" @@ -1828,12 +1834,15 @@ msgstr "Gemäß § 18 Abs. 2 MStV: Lennart Seidel, Reetwerder 25b, 21029 Hamburg msgid "" "Permanently delete your account, API keys, and cancel any active " -"subscription. Conversion-job records are anonymised. This cannot be " -"undone." +"subscription. Conversion-job records are anonymised. If you've made a " +"payment, we keep a minimal invoice-link record for 10 years (German tax " +"law). This cannot be undone." msgstr "" "Lösche dauerhaft dein Konto und deine API-Schlüssel und kündige ein " "etwaiges aktives Abonnement. Konvertierungs-Job-Datensätze werden " -"anonymisiert. Dies kann nicht rückgängig gemacht werden." +"anonymisiert. Wenn du eine Zahlung getätigt hast, behalten wir einen " +"minimalen Rechnungs-Verknüpfungsdatensatz für 10 Jahre (deutsches " +"Steuerrecht). Dies kann nicht rückgängig gemacht werden." msgid "Phone number" msgstr "Telefonnummer" @@ -2110,13 +2119,11 @@ msgid "Reset your password" msgstr "Setze dein Passwort zurück" msgid "" -"Response-time targets by severity — critical 4 h, high 24 h, medium/low " -"in the next regular release. Targets apply Mon–Fri 09:00–18:00 CET, " -"excluding German public holidays." +"Response-time targets by severity are individually agreed in the support " +"contract." msgstr "" -"Reaktionszeiten nach Schweregrad — kritisch 4 h, hoch 24 h, " -"mittel/niedrig im nächsten regulären Release. Die Zielwerte gelten Mo–Fr " -"09:00–18:00 MEZ, ausgenommen gesetzliche Feiertage in Deutschland." +"Reaktionszeit-Ziele nach Schweregrad werden individuell im Support-" +"Vertrag vereinbart." msgid "Responsible party" msgstr "Verantwortlich" @@ -2340,6 +2347,16 @@ msgstr "Anmeldung …" msgid "Signups over time" msgstr "Registrierungen über Zeit" +msgid "" +"Single-file format conversions and image/video compressions, plus " +"account, contact, billing and redaction events, go into a continuous hash" +" chain. Tampering with an old row breaks every following one." +msgstr "" +"Einzeldatei-Formatkonvertierungen und Bild-/Video-Kompressionen sowie " +"Konto-, Kontakt-, Abrechnungs- und Schwärzungs-Ereignisse fließen in eine" +" fortlaufende Hash-Chain. Manipulation an einer alten Zeile bricht die " +"nachfolgende Kette." + msgid "" "Small-business status under § 19 UStG — no VAT is charged and no USt-" "IdNr. is held." @@ -2497,15 +2514,6 @@ msgstr "Nutzungsbedingungen" msgid "Thanks — your message has been sent. We will reply soon." msgstr "Danke — deine Nachricht wurde gesendet. Wir antworten in Kürze." -msgid "" -"That channel is preferred for issues that affect every self-hosted " -"instance, since the repository maintainers can coordinate a CVE and " -"release a patched version centrally." -msgstr "" -"Dieser Kanal ist bevorzugt für Probleme, die jede selbst gehostete " -"Instanz betreffen, da die Repository-Maintainer eine CVE koordinieren und" -" eine gepatchte Version zentral veröffentlichen können." - msgid "" "The Compliance Edition is for when you need a contract behind it: DPA, " "support SLA, signed releases, and someone accountable — not " @@ -2648,13 +2656,13 @@ msgstr "" "neue Bestätigungs-E-Mail vom Dashboard an." msgid "" -"This service is hosted by Hetzner Online GmbH, Germany (Frankfurt data " -"centre). Your requests are routed through Cloudflare's network for DDoS " -"protection and performance. See" +"This service is hosted by Hetzner Online GmbH — data centre in the EU. " +"Your requests are routed through Cloudflare's network for DDoS protection" +" and performance. See" msgstr "" -"Dieser Dienst wird gehostet von Hetzner Online GmbH, Deutschland " -"(Rechenzentrum Frankfurt). Deine Anfragen werden über Cloudflares " -"Netzwerk für DDoS-Schutz und Performance geroutet. Siehe" +"Dieser Dienst wird gehostet von Hetzner Online GmbH — Rechenzentrum in " +"der EU. Deine Anfragen werden über Cloudflares Netzwerk für DDoS-Schutz " +"und Performance geroutet. Siehe" msgid "" "This tool compresses images and video. To shrink a PDF, use the dedicated" @@ -2805,18 +2813,24 @@ msgid "Video" msgstr "Video" msgid "" -"We apply a temporary in-memory rate limit (max. 10 requests per minute " -"per IP address) to protect the service from abuse. IP addresses used by " -"the rate limiter are processed transiently in memory only and are never " -"written to disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate " -"interest in service stability and security." +"We apply in-memory rate limits to protect the service from abuse — set " +"per route, from 5 contact-form messages per hour up to 120 requests per " +"minute. Several account routes count the limit per signed-in account " +"rather than per IP address, and failed API-key attempts get a separate " +"per-IP budget. IP addresses and account identifiers used by the rate " +"limiter are processed transiently in memory only and are never written to" +" disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate interest in " +"service stability and security." msgstr "" -"Wir wenden ein temporäres In-Memory-Rate-Limit (max. 10 Anfragen pro " -"Minute pro IP-Adresse) an, um den Dienst vor Missbrauch zu schützen. Vom " -"Rate-Limiter verwendete IP-Adressen werden nur transient im Speicher " -"verarbeitet und niemals auf Disk oder in Logs geschrieben. " -"Rechtsgrundlage: Art. 6(1)(f) DSGVO — berechtigtes Interesse an Dienst-" -"Stabilität und Sicherheit." +"Wir wenden In-Memory-Rate-Limits an, um den Dienst vor Missbrauch zu " +"schützen — je nach Route von 5 Kontaktformular-Nachrichten pro Stunde bis" +" zu 120 Anfragen pro Minute. Mehrere Konto-Routen zählen das Limit pro " +"angemeldetem Konto statt pro IP-Adresse, und fehlgeschlagene API-" +"Schlüssel-Versuche erhalten ein separates Budget pro IP-Adresse. Vom " +"Rate-Limiter verwendete IP-Adressen und Konto-Kennungen werden nur " +"transient im Speicher verarbeitet und niemals auf Disk oder in Logs " +"geschrieben. Rechtsgrundlage: Art. 6(1)(f) DSGVO — berechtigtes Interesse" +" an Dienst-Stabilität und Sicherheit." msgid "" "We are neither willing nor obliged to participate in dispute-resolution " @@ -2826,15 +2840,15 @@ msgstr "" " einer Verbraucherschlichtungsstelle teilzunehmen (§ 36 VSBG)." msgid "" -"We may update this policy when the service changes. Accounts and billing " -"are live as of April 2026; persistent file history (Cloud Edition Phase " -"2) is planned but not yet implemented. The date at the top indicates the " -"current version." +"We may update this policy when the service changes. Accounts are live; " +"paid plans are not yet available. Persistent file history (Cloud Edition " +"Phase 2) is planned but not yet implemented. The date at the top " +"indicates the current version." msgstr "" "Wir können diese Richtlinie aktualisieren, wenn sich der Dienst ändert. " -"Accounts und Billing sind seit April 2026 live; persistente Datei-" -"Historie (Cloud Edition Phase 2) ist geplant aber noch nicht " -"implementiert. Das Datum oben zeigt die aktuelle Version an." +"Accounts sind live; kostenpflichtige Tarife sind noch nicht verfügbar. " +"Persistente Datei-Historie (Cloud Edition Phase 2) ist geplant, aber noch" +" nicht implementiert. Das Datum oben zeigt die aktuelle Version an." msgid "" "We name it because an RFP reviewer would notice anyway — and because we " @@ -2928,13 +2942,15 @@ msgid "When to use this conversion" msgstr "Wann diese Umwandlung sinnvoll ist" msgid "" -"When you request a password reset, we generate a single-use token (valid " -"for 30 minutes) and send it to your registered email address as a reset " -"link. The outgoing message is delivered from" +"When you register, we send a verification link to your email address " +"(valid for 7 days). When you request a password reset, we generate a " +"separate single-use token (valid for 30 minutes) and send it as a reset " +"link. Outgoing messages are delivered from" msgstr "" -"Wenn du ein Passwort-Reset anforderst, generieren wir einen Einmal-Token " -"(gültig für 30 Minuten) und senden ihn als Reset-Link an deine " -"registrierte E-Mail-Adresse. Die ausgehende Nachricht wird verschickt von" +"Wenn du dich registrierst, senden wir einen Bestätigungs-Link an deine E" +"-Mail-Adresse (gültig für 7 Tage). Wenn du ein Passwort-Reset anforderst," +" generieren wir einen separaten Einmal-Token (gültig für 30 Minuten) und " +"senden ihn als Reset-Link. Ausgehende Nachrichten werden verschickt von" msgid "" "When you start a paid-tier upgrade, you are redirected to Stripe Checkout" @@ -3207,9 +3223,6 @@ msgstr "und" msgid "anonymous)" msgstr "anonym)" -msgid "are encrypted with the same key." -msgstr "werden mit demselben Schlüssel verschlüsselt." - msgid "calls / month" msgstr "Aufrufe / Monat" @@ -3296,12 +3309,18 @@ msgstr "" "Hinweis. Große Batches werden sequentiell innerhalb der Anfrage " "verarbeitet." +msgid "" +"legacy; current versions no longer write it, but a value saved by an " +"older version is still read. Cleared on logout and when you delete your " +"account." +msgstr "" +"veraltet; aktuelle Versionen schreiben ihn nicht mehr, ein von einer " +"älteren Version gespeicherter Wert wird aber noch gelesen. Wird beim " +"Abmelden und beim Löschen deines Kontos entfernt." + msgid "min 0.05 MB" msgstr "min. 0,05 MB" -msgid "no API access" -msgstr "kein API-Zugriff" - msgid "no cookies" msgstr "keine Cookies" @@ -3333,14 +3352,6 @@ msgstr "" msgid "on request" msgstr "auf Anfrage" -msgid "" -"optional; remembers the API key shown in your dashboard across reloads so" -" you don't need to paste it again. Cleared on logout." -msgstr "" -"optional; merkt sich den im Dashboard angezeigten API-Key über Reloads " -"hinweg, damit du ihn nicht erneut einfügen musst. Wird beim Logout " -"gelöscht." - msgid "or apply directly as design partner →" msgstr "oder direkt als Design-Partner bewerben →" @@ -3496,6 +3507,9 @@ msgstr "© 2026 FileMorph — AGPLv3 Open Source" msgid "±3% tolerance" msgstr "±3% Toleranz" +msgid "— a PGP key is available on request." +msgstr "— ein PGP-Schlüssel ist auf Anfrage erhältlich." + msgid "— select a file first —" msgstr "— erst eine Datei wählen —" diff --git a/locale/en/LC_MESSAGES/messages.mo b/locale/en/LC_MESSAGES/messages.mo index d043bf3b4fb8ba5f1c11c6dc899fb1973c8e02fa..372d34e69647e6b817d4ee9b9c98b3407fa99ed0 100644 GIT binary patch delta 37 ncmZ24v0h@s31((XU8BvXnG>1VEENnatV}l>vHgb${$>OK;qVK@ delta 37 ncmZ24v0h@s31(&sUBk_%nG>1VEEEhat&BGtvHgb${$>OK;hPJ; diff --git a/locale/en/LC_MESSAGES/messages.po b/locale/en/LC_MESSAGES/messages.po index 45fdf68..96fd9a7 100644 --- a/locale/en/LC_MESSAGES/messages.po +++ b/locale/en/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: FileMorph VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-28 19:39+0200\n" +"POT-Creation-Date: 2026-09-29 08:59+0200\n" "PO-Revision-Date: 2026-05-07 13:43+0200\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -45,9 +45,9 @@ msgid "+ New Key" msgstr "" msgid "" -", hosted in Frankfurt, EU). Zoho receives the recipient address and the " -"email contents (including the reset link). Legal basis: Art. 6(1)(b) " -"GDPR. See" +", EU data centres in Amsterdam (NL) and Dublin (IE)). Zoho receives the " +"recipient address and the email contents (including the reset link). " +"Legal basis: Art. 6(1)(b) GDPR. See" msgstr "" msgid "" @@ -163,6 +163,11 @@ msgstr "" msgid "9. Right of Withdrawal (Consumers)" msgstr "" +msgid "" +"; cancellation takes effect at the end of the current billing period and " +"you retain access for the remainder of that period." +msgstr "" + msgid "A description of the vulnerability and its potential impact." msgstr "" @@ -187,6 +192,9 @@ msgstr "" msgid "API Keys" msgstr "" +msgid "API access without a key" +msgstr "" + msgid "API calls / month" msgstr "" @@ -374,10 +382,10 @@ msgstr "" msgid "" "Being transparent about what is not done yet: no external ISO 27001 " "certification and no external penetration test yet (both planned once a " -"paying pilot justifies the cost), and no tagged release cut yet — the " -"signing/SBOM pipeline is wired in CI and produces those artefacts on the " -"first tag. Everything else above is in the repository today and " -"auditable." +"paying pilot justifies the cost). v1.1.0, tagged 2026-06-01, is the " +"latest signed release with a CycloneDX SBOM and an image digest; changes " +"since then ship in the continuously built image ahead of the next tag. " +"Everything else above is in the repository today and auditable." msgstr "" msgid "Best quality" @@ -510,7 +518,7 @@ msgstr "" msgid "Concurrent slots" msgstr "" -msgid "Confidential inquiries via" +msgid "Confidential inquiries: email" msgstr "" msgid "Confirm Password" @@ -544,8 +552,8 @@ msgid "Confirming your email…" msgstr "" msgid "" -"Conformance gate runs as CI workflow. No release without green veraPDF " -"against a worst-case source PDF." +"Conformance check runs in CI on every push to main and every pull " +"request, against a worst-case source PDF." msgstr "" msgid "" @@ -799,11 +807,6 @@ msgid "" "audio and video, plus compression to a target size. Free, open source." msgstr "" -msgid "" -"Every operation in a continuous hash chain. Tampering with an old row " -"breaks every following one." -msgstr "" - msgid "" "External ISO 27001 certification and external pen test are planned as " "Year-2 roadmap items — both will be implemented as soon as the first " @@ -839,21 +842,23 @@ msgstr "" msgid "" "FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size " "(for example 5 MB) using a binary search — useful for upload limits and " -"email attachments." +"email attachments. PNG, TIFF and video below compress by quality only, " +"not to an exact size." msgstr "" msgid "" -"FileMorph closes this gap: the open-source engine covers 16+ format pairs" -" and runs on your own Hetzner / on-premises / air-gap infrastructure. The" -" Compliance Edition additionally provides the contracts, SLAs and roadmap" -" guarantees that an EVB-IT-compliant procurement requires." +"FileMorph closes this gap: the open-source engine covers 180+ format " +"pairs and runs on your own Hetzner / on-premises / air-gap " +"infrastructure. The Compliance Edition additionally provides the " +"contracts, SLAs and roadmap guarantees that an EVB-IT-compliant " +"procurement requires." msgstr "" msgid "" -"FileMorph converts between the formats below and compresses images to an " -"exact target size (video too, by quality). Everything runs for free, with" -" no account — or self-host it under AGPLv3. Pick any pair and start on " -"the" +"FileMorph converts between the formats below and compresses JPEG, WebP " +"and AVIF images to an exact target size — PNG, TIFF and video use " +"quality-based compression instead. Everything runs for free, with no " +"account — or self-host it under AGPLv3. Pick any pair and start on the" msgstr "" msgid "" @@ -896,7 +901,7 @@ msgstr "" msgid "" "FileMorph pricing: the open-source converter is free to self-host. Paid " -"plans add volume, higher limits and API access." +"plans add volume, higher limits and larger API quotas." msgstr "" msgid "FileMorph sets" @@ -963,9 +968,6 @@ msgstr "" msgid "For developers & SaaS" msgstr "" -msgid "For issues in the open-source codebase itself, you may alternatively use" -msgstr "" - msgid "For public sector & compliance" msgstr "" @@ -1032,9 +1034,6 @@ msgstr "" msgid "Get started free" msgstr "" -msgid "GitHub Security Advisories" -msgstr "" - msgid "GitHub ↗" msgstr "" @@ -1127,25 +1126,29 @@ msgstr "" msgid "" "If you register an account, we store your email address, a bcrypt hash of" " your password (never the plaintext), your subscription tier, your " -"account creation timestamp, and — once you upgrade to a paid tier — the " -"Stripe customer identifier that links your account to Stripe. Account " -"data is persisted in our PostgreSQL database for the lifetime of your " -"account. You can delete your account at any time through the dashboard's " -"\"Delete account\" flow, or by request to privacy@filemorph.io. On " -"deletion we erase your password hash and API keys, and cancel any active " -"paid subscription. Conversion-job records are anonymised — your account " -"ID is removed — and the resulting anonymous rows are retained for " -"aggregate service-quality analytics; under GDPR Art. 4(1), they no longer" -" relate to you. Stripe transaction records are retained by Stripe under " -"their own tax-retention obligations. For accounts that have made at least" -" one payment, German tax law (HGB §257, AO §147) obliges us to retain a " -"minimal invoice-link record — your email address, Stripe customer " -"identifier, and last billing tier — for 10 years from the end of the " -"calendar year of your last payment; all other personal data is erased on " -"deletion. This retention is the legal-obligation exception under GDPR " -"Art. 17(3)(b); the data is held solely for tax audit and is hard-deleted " -"at the end of the retention period. Erasure otherwise complies with GDPR " -"Art. 17." +"account creation timestamp, your preferred language for account emails, " +"the timestamp your email address was verified (once you complete that " +"step), and — once you upgrade to a paid tier — the Stripe customer " +"identifier that links your account to Stripe. We also record one row per " +"API/conversion request you make — the endpoint, the file size, and how " +"long it took, never the file content — to enforce your monthly quota and " +"show usage in your dashboard. Account data is persisted in our PostgreSQL" +" database for the lifetime of your account. You can delete your account " +"at any time through the dashboard's \"Delete account\" flow, or by " +"request to privacy@filemorph.io. On deletion we erase your password hash " +"and API keys, and cancel any active paid subscription. Conversion-job " +"records are anonymised — your account ID is removed — and the resulting " +"anonymous rows are retained for aggregate service-quality analytics; " +"under GDPR Art. 4(1), they no longer relate to you. Stripe transaction " +"records are retained by Stripe under their own tax-retention obligations." +" For accounts that have made at least one payment, German tax law (HGB " +"§257, AO §147) obliges us to retain a minimal invoice-link record — your " +"email address, Stripe customer identifier, and last billing tier — for 10" +" years from the end of the calendar year of your last payment; all other " +"personal data is erased on deletion. This retention is the legal-" +"obligation exception under GDPR Art. 17(3)(b); the data is held solely " +"for tax audit and is hard-deleted at the end of the retention period. " +"Erasure otherwise complies with GDPR Art. 17." msgstr "" msgid "" @@ -1246,11 +1249,8 @@ msgstr "" msgid "Language" msgstr "" -msgid "Last updated: 2026-06-22 · Community + Cloud Edition" -msgstr "" - -msgid "Last updated: 2026-08-18 · Community + Cloud Edition" -msgstr "Last updated: 2026-08-18 · Community + Cloud Edition" +msgid "Last updated: 2026-09-28 · Community + Cloud Edition" +msgstr "Last updated: 2026-09-28 · Community + Cloud Edition" msgid "Last used" msgstr "" @@ -1503,11 +1503,9 @@ msgid "Pages to keep" msgstr "" msgid "" -"Paid plans (Pro €7/month, Business €19/month) are billed monthly via " -"Stripe and renew automatically until cancelled. You may cancel at any " -"time through the Stripe customer portal linked from your account " -"dashboard; cancellation takes effect at the end of the current billing " -"period and you retain access for the remainder of that period." +"Paid plans are billed monthly via Stripe, at the prices shown on the " +"pricing page at the time of your order, and renew automatically until " +"cancelled. You may cancel at any time by emailing" msgstr "" msgid "Paid tiers — coming soon" @@ -1543,8 +1541,9 @@ msgstr "" msgid "" "Permanently delete your account, API keys, and cancel any active " -"subscription. Conversion-job records are anonymised. This cannot be " -"undone." +"subscription. Conversion-job records are anonymised. If you've made a " +"payment, we keep a minimal invoice-link record for 10 years (German tax " +"law). This cannot be undone." msgstr "" msgid "Phone number" @@ -1772,9 +1771,8 @@ msgid "Reset your password" msgstr "" msgid "" -"Response-time targets by severity — critical 4 h, high 24 h, medium/low " -"in the next regular release. Targets apply Mon–Fri 09:00–18:00 CET, " -"excluding German public holidays." +"Response-time targets by severity are individually agreed in the support " +"contract." msgstr "" msgid "Responsible party" @@ -1983,6 +1981,12 @@ msgstr "" msgid "Signups over time" msgstr "" +msgid "" +"Single-file format conversions and image/video compressions, plus " +"account, contact, billing and redaction events, go into a continuous hash" +" chain. Tampering with an old row breaks every following one." +msgstr "" + msgid "" "Small-business status under § 19 UStG — no VAT is charged and no USt-" "IdNr. is held." @@ -2119,12 +2123,6 @@ msgstr "" msgid "Thanks — your message has been sent. We will reply soon." msgstr "" -msgid "" -"That channel is preferred for issues that affect every self-hosted " -"instance, since the repository maintainers can coordinate a CVE and " -"release a patched version centrally." -msgstr "" - msgid "" "The Compliance Edition is for when you need a contract behind it: DPA, " "support SLA, signed releases, and someone accountable — not " @@ -2224,9 +2222,9 @@ msgid "" msgstr "" msgid "" -"This service is hosted by Hetzner Online GmbH, Germany (Frankfurt data " -"centre). Your requests are routed through Cloudflare's network for DDoS " -"protection and performance. See" +"This service is hosted by Hetzner Online GmbH — data centre in the EU. " +"Your requests are routed through Cloudflare's network for DDoS protection" +" and performance. See" msgstr "" msgid "" @@ -2356,11 +2354,14 @@ msgid "Video" msgstr "" msgid "" -"We apply a temporary in-memory rate limit (max. 10 requests per minute " -"per IP address) to protect the service from abuse. IP addresses used by " -"the rate limiter are processed transiently in memory only and are never " -"written to disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate " -"interest in service stability and security." +"We apply in-memory rate limits to protect the service from abuse — set " +"per route, from 5 contact-form messages per hour up to 120 requests per " +"minute. Several account routes count the limit per signed-in account " +"rather than per IP address, and failed API-key attempts get a separate " +"per-IP budget. IP addresses and account identifiers used by the rate " +"limiter are processed transiently in memory only and are never written to" +" disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate interest in " +"service stability and security." msgstr "" msgid "" @@ -2369,10 +2370,10 @@ msgid "" msgstr "" msgid "" -"We may update this policy when the service changes. Accounts and billing " -"are live as of April 2026; persistent file history (Cloud Edition Phase " -"2) is planned but not yet implemented. The date at the top indicates the " -"current version." +"We may update this policy when the service changes. Accounts are live; " +"paid plans are not yet available. Persistent file history (Cloud Edition " +"Phase 2) is planned but not yet implemented. The date at the top " +"indicates the current version." msgstr "" msgid "" @@ -2455,9 +2456,10 @@ msgid "When to use this conversion" msgstr "" msgid "" -"When you request a password reset, we generate a single-use token (valid " -"for 30 minutes) and send it to your registered email address as a reset " -"link. The outgoing message is delivered from" +"When you register, we send a verification link to your email address " +"(valid for 7 days). When you request a password reset, we generate a " +"separate single-use token (valid for 30 minutes) and send it as a reset " +"link. Outgoing messages are delivered from" msgstr "" msgid "" @@ -2656,9 +2658,6 @@ msgstr "" msgid "anonymous)" msgstr "" -msgid "are encrypted with the same key." -msgstr "" - msgid "calls / month" msgstr "" @@ -2734,10 +2733,13 @@ msgstr "" msgid "hint. Large batches are processed sequentially inside the request." msgstr "" -msgid "min 0.05 MB" +msgid "" +"legacy; current versions no longer write it, but a value saved by an " +"older version is still read. Cleared on logout and when you delete your " +"account." msgstr "" -msgid "no API access" +msgid "min 0.05 MB" msgstr "" msgid "no cookies" @@ -2771,11 +2773,6 @@ msgstr "" msgid "on request" msgstr "" -msgid "" -"optional; remembers the API key shown in your dashboard across reloads so" -" you don't need to paste it again. Cleared on logout." -msgstr "" - msgid "or apply directly as design partner →" msgstr "" @@ -2907,6 +2904,9 @@ msgstr "" msgid "±3% tolerance" msgstr "" +msgid "— a PGP key is available on request." +msgstr "" + msgid "— select a file first —" msgstr "" diff --git a/locale/messages.pot b/locale/messages.pot index 8673c24..cc8ec6d 100644 --- a/locale/messages.pot +++ b/locale/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: FileMorph 1.1.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-28 19:39+0200\n" +"POT-Creation-Date: 2026-09-29 08:59+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -44,9 +44,9 @@ msgid "+ New Key" msgstr "" msgid "" -", hosted in Frankfurt, EU). Zoho receives the recipient address and the " -"email contents (including the reset link). Legal basis: Art. 6(1)(b) " -"GDPR. See" +", EU data centres in Amsterdam (NL) and Dublin (IE)). Zoho receives the " +"recipient address and the email contents (including the reset link). " +"Legal basis: Art. 6(1)(b) GDPR. See" msgstr "" msgid "" @@ -162,6 +162,11 @@ msgstr "" msgid "9. Right of Withdrawal (Consumers)" msgstr "" +msgid "" +"; cancellation takes effect at the end of the current billing period and " +"you retain access for the remainder of that period." +msgstr "" + msgid "A description of the vulnerability and its potential impact." msgstr "" @@ -186,6 +191,9 @@ msgstr "" msgid "API Keys" msgstr "" +msgid "API access without a key" +msgstr "" + msgid "API calls / month" msgstr "" @@ -366,10 +374,10 @@ msgstr "" msgid "" "Being transparent about what is not done yet: no external ISO 27001 " "certification and no external penetration test yet (both planned once a " -"paying pilot justifies the cost), and no tagged release cut yet — the " -"signing/SBOM pipeline is wired in CI and produces those artefacts on the " -"first tag. Everything else above is in the repository today and " -"auditable." +"paying pilot justifies the cost). v1.1.0, tagged 2026-06-01, is the " +"latest signed release with a CycloneDX SBOM and an image digest; changes " +"since then ship in the continuously built image ahead of the next tag. " +"Everything else above is in the repository today and auditable." msgstr "" msgid "Best quality" @@ -502,7 +510,7 @@ msgstr "" msgid "Concurrent slots" msgstr "" -msgid "Confidential inquiries via" +msgid "Confidential inquiries: email" msgstr "" msgid "Confirm Password" @@ -536,8 +544,8 @@ msgid "Confirming your email…" msgstr "" msgid "" -"Conformance gate runs as CI workflow. No release without green veraPDF " -"against a worst-case source PDF." +"Conformance check runs in CI on every push to main and every pull " +"request, against a worst-case source PDF." msgstr "" msgid "" @@ -791,11 +799,6 @@ msgid "" "audio and video, plus compression to a target size. Free, open source." msgstr "" -msgid "" -"Every operation in a continuous hash chain. Tampering with an old row " -"breaks every following one." -msgstr "" - msgid "" "External ISO 27001 certification and external pen test are planned as " "Year-2 roadmap items — both will be implemented as soon as the first " @@ -831,21 +834,23 @@ msgstr "" msgid "" "FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size " "(for example 5 MB) using a binary search — useful for upload limits and " -"email attachments." +"email attachments. PNG, TIFF and video below compress by quality only, " +"not to an exact size." msgstr "" msgid "" -"FileMorph closes this gap: the open-source engine covers 16+ format pairs" -" and runs on your own Hetzner / on-premises / air-gap infrastructure. The" -" Compliance Edition additionally provides the contracts, SLAs and roadmap" -" guarantees that an EVB-IT-compliant procurement requires." +"FileMorph closes this gap: the open-source engine covers 180+ format " +"pairs and runs on your own Hetzner / on-premises / air-gap " +"infrastructure. The Compliance Edition additionally provides the " +"contracts, SLAs and roadmap guarantees that an EVB-IT-compliant " +"procurement requires." msgstr "" msgid "" -"FileMorph converts between the formats below and compresses images to an " -"exact target size (video too, by quality). Everything runs for free, with" -" no account — or self-host it under AGPLv3. Pick any pair and start on " -"the" +"FileMorph converts between the formats below and compresses JPEG, WebP " +"and AVIF images to an exact target size — PNG, TIFF and video use " +"quality-based compression instead. Everything runs for free, with no " +"account — or self-host it under AGPLv3. Pick any pair and start on the" msgstr "" msgid "" @@ -888,7 +893,7 @@ msgstr "" msgid "" "FileMorph pricing: the open-source converter is free to self-host. Paid " -"plans add volume, higher limits and API access." +"plans add volume, higher limits and larger API quotas." msgstr "" msgid "FileMorph sets" @@ -952,9 +957,6 @@ msgstr "" msgid "For developers & SaaS" msgstr "" -msgid "For issues in the open-source codebase itself, you may alternatively use" -msgstr "" - msgid "For public sector & compliance" msgstr "" @@ -1021,9 +1023,6 @@ msgstr "" msgid "Get started free" msgstr "" -msgid "GitHub Security Advisories" -msgstr "" - msgid "GitHub ↗" msgstr "" @@ -1116,25 +1115,29 @@ msgstr "" msgid "" "If you register an account, we store your email address, a bcrypt hash of" " your password (never the plaintext), your subscription tier, your " -"account creation timestamp, and — once you upgrade to a paid tier — the " -"Stripe customer identifier that links your account to Stripe. Account " -"data is persisted in our PostgreSQL database for the lifetime of your " -"account. You can delete your account at any time through the dashboard's " -"\"Delete account\" flow, or by request to privacy@filemorph.io. On " -"deletion we erase your password hash and API keys, and cancel any active " -"paid subscription. Conversion-job records are anonymised — your account " -"ID is removed — and the resulting anonymous rows are retained for " -"aggregate service-quality analytics; under GDPR Art. 4(1), they no longer" -" relate to you. Stripe transaction records are retained by Stripe under " -"their own tax-retention obligations. For accounts that have made at least" -" one payment, German tax law (HGB §257, AO §147) obliges us to retain a " -"minimal invoice-link record — your email address, Stripe customer " -"identifier, and last billing tier — for 10 years from the end of the " -"calendar year of your last payment; all other personal data is erased on " -"deletion. This retention is the legal-obligation exception under GDPR " -"Art. 17(3)(b); the data is held solely for tax audit and is hard-deleted " -"at the end of the retention period. Erasure otherwise complies with GDPR " -"Art. 17." +"account creation timestamp, your preferred language for account emails, " +"the timestamp your email address was verified (once you complete that " +"step), and — once you upgrade to a paid tier — the Stripe customer " +"identifier that links your account to Stripe. We also record one row per " +"API/conversion request you make — the endpoint, the file size, and how " +"long it took, never the file content — to enforce your monthly quota and " +"show usage in your dashboard. Account data is persisted in our PostgreSQL" +" database for the lifetime of your account. You can delete your account " +"at any time through the dashboard's \"Delete account\" flow, or by " +"request to privacy@filemorph.io. On deletion we erase your password hash " +"and API keys, and cancel any active paid subscription. Conversion-job " +"records are anonymised — your account ID is removed — and the resulting " +"anonymous rows are retained for aggregate service-quality analytics; " +"under GDPR Art. 4(1), they no longer relate to you. Stripe transaction " +"records are retained by Stripe under their own tax-retention obligations." +" For accounts that have made at least one payment, German tax law (HGB " +"§257, AO §147) obliges us to retain a minimal invoice-link record — your " +"email address, Stripe customer identifier, and last billing tier — for 10" +" years from the end of the calendar year of your last payment; all other " +"personal data is erased on deletion. This retention is the legal-" +"obligation exception under GDPR Art. 17(3)(b); the data is held solely " +"for tax audit and is hard-deleted at the end of the retention period. " +"Erasure otherwise complies with GDPR Art. 17." msgstr "" msgid "" @@ -1235,10 +1238,7 @@ msgstr "" msgid "Language" msgstr "" -msgid "Last updated: 2026-06-22 · Community + Cloud Edition" -msgstr "" - -msgid "Last updated: 2026-08-18 · Community + Cloud Edition" +msgid "Last updated: 2026-09-28 · Community + Cloud Edition" msgstr "" msgid "Last used" @@ -1492,11 +1492,9 @@ msgid "Pages to keep" msgstr "" msgid "" -"Paid plans (Pro €7/month, Business €19/month) are billed monthly via " -"Stripe and renew automatically until cancelled. You may cancel at any " -"time through the Stripe customer portal linked from your account " -"dashboard; cancellation takes effect at the end of the current billing " -"period and you retain access for the remainder of that period." +"Paid plans are billed monthly via Stripe, at the prices shown on the " +"pricing page at the time of your order, and renew automatically until " +"cancelled. You may cancel at any time by emailing" msgstr "" msgid "Paid tiers — coming soon" @@ -1532,8 +1530,9 @@ msgstr "" msgid "" "Permanently delete your account, API keys, and cancel any active " -"subscription. Conversion-job records are anonymised. This cannot be " -"undone." +"subscription. Conversion-job records are anonymised. If you've made a " +"payment, we keep a minimal invoice-link record for 10 years (German tax " +"law). This cannot be undone." msgstr "" msgid "Phone number" @@ -1761,9 +1760,8 @@ msgid "Reset your password" msgstr "" msgid "" -"Response-time targets by severity — critical 4 h, high 24 h, medium/low " -"in the next regular release. Targets apply Mon–Fri 09:00–18:00 CET, " -"excluding German public holidays." +"Response-time targets by severity are individually agreed in the support " +"contract." msgstr "" msgid "Responsible party" @@ -1970,6 +1968,12 @@ msgstr "" msgid "Signups over time" msgstr "" +msgid "" +"Single-file format conversions and image/video compressions, plus " +"account, contact, billing and redaction events, go into a continuous hash" +" chain. Tampering with an old row breaks every following one." +msgstr "" + msgid "" "Small-business status under § 19 UStG — no VAT is charged and no USt-" "IdNr. is held." @@ -2106,12 +2110,6 @@ msgstr "" msgid "Thanks — your message has been sent. We will reply soon." msgstr "" -msgid "" -"That channel is preferred for issues that affect every self-hosted " -"instance, since the repository maintainers can coordinate a CVE and " -"release a patched version centrally." -msgstr "" - msgid "" "The Compliance Edition is for when you need a contract behind it: DPA, " "support SLA, signed releases, and someone accountable — not " @@ -2211,9 +2209,9 @@ msgid "" msgstr "" msgid "" -"This service is hosted by Hetzner Online GmbH, Germany (Frankfurt data " -"centre). Your requests are routed through Cloudflare's network for DDoS " -"protection and performance. See" +"This service is hosted by Hetzner Online GmbH — data centre in the EU. " +"Your requests are routed through Cloudflare's network for DDoS protection" +" and performance. See" msgstr "" msgid "" @@ -2343,11 +2341,14 @@ msgid "Video" msgstr "" msgid "" -"We apply a temporary in-memory rate limit (max. 10 requests per minute " -"per IP address) to protect the service from abuse. IP addresses used by " -"the rate limiter are processed transiently in memory only and are never " -"written to disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate " -"interest in service stability and security." +"We apply in-memory rate limits to protect the service from abuse — set " +"per route, from 5 contact-form messages per hour up to 120 requests per " +"minute. Several account routes count the limit per signed-in account " +"rather than per IP address, and failed API-key attempts get a separate " +"per-IP budget. IP addresses and account identifiers used by the rate " +"limiter are processed transiently in memory only and are never written to" +" disk or logs. Legal basis: Art. 6(1)(f) GDPR — legitimate interest in " +"service stability and security." msgstr "" msgid "" @@ -2356,10 +2357,10 @@ msgid "" msgstr "" msgid "" -"We may update this policy when the service changes. Accounts and billing " -"are live as of April 2026; persistent file history (Cloud Edition Phase " -"2) is planned but not yet implemented. The date at the top indicates the " -"current version." +"We may update this policy when the service changes. Accounts are live; " +"paid plans are not yet available. Persistent file history (Cloud Edition " +"Phase 2) is planned but not yet implemented. The date at the top " +"indicates the current version." msgstr "" msgid "" @@ -2442,9 +2443,10 @@ msgid "When to use this conversion" msgstr "" msgid "" -"When you request a password reset, we generate a single-use token (valid " -"for 30 minutes) and send it to your registered email address as a reset " -"link. The outgoing message is delivered from" +"When you register, we send a verification link to your email address " +"(valid for 7 days). When you request a password reset, we generate a " +"separate single-use token (valid for 30 minutes) and send it as a reset " +"link. Outgoing messages are delivered from" msgstr "" msgid "" @@ -2641,9 +2643,6 @@ msgstr "" msgid "anonymous)" msgstr "" -msgid "are encrypted with the same key." -msgstr "" - msgid "calls / month" msgstr "" @@ -2719,10 +2718,13 @@ msgstr "" msgid "hint. Large batches are processed sequentially inside the request." msgstr "" -msgid "min 0.05 MB" +msgid "" +"legacy; current versions no longer write it, but a value saved by an " +"older version is still read. Cleared on logout and when you delete your " +"account." msgstr "" -msgid "no API access" +msgid "min 0.05 MB" msgstr "" msgid "no cookies" @@ -2754,11 +2756,6 @@ msgstr "" msgid "on request" msgstr "" -msgid "" -"optional; remembers the API key shown in your dashboard across reloads so" -" you don't need to paste it again. Cleared on logout." -msgstr "" - msgid "or apply directly as design partner →" msgstr "" @@ -2887,6 +2884,9 @@ msgstr "" msgid "±3% tolerance" msgstr "" +msgid "— a PGP key is available on request." +msgstr "" + msgid "— select a file first —" msgstr "" diff --git a/tests/test_cookie_notice.py b/tests/test_cookie_notice.py index a177d90..422dd77 100644 --- a/tests/test_cookie_notice.py +++ b/tests/test_cookie_notice.py @@ -35,6 +35,20 @@ def test_notice_is_not_a_consent_dialog(client): assert "cookie-notice-reject" not in html +def test_logout_clears_every_signed_in_localstorage_key(): + """privacy.html §6 promises that the keys written while signed in go on + logout — including the legacy ``filemorph_api_key`` older versions stored.""" + from pathlib import Path + + js = (Path(__file__).parent.parent / "app" / "static" / "js" / "auth.js").read_text( + encoding="utf-8" + ) + body = re.search(r"function logout\(\) \{(.*?)\n \}", js, re.S) + assert body, "logout() not found in auth.js" + for key in ("ACCESS_KEY", "REFRESH_KEY", "'filemorph_api_key'"): + assert f"localStorage.removeItem({key})" in body.group(1), f"logout keeps {key}" + + def test_notice_deep_links_privacy_cookies_section(client): assert 'href="/privacy#cookies"' in client.get("/").text assert 'id="cookies"' in client.get("/privacy").text diff --git a/tests/test_format_lists_match_registry.py b/tests/test_format_lists_match_registry.py index b965fd0..31ca30f 100644 --- a/tests/test_format_lists_match_registry.py +++ b/tests/test_format_lists_match_registry.py @@ -238,3 +238,21 @@ def test_formats_md_any_to_any_lists_match_registry(): "docs/formats.md: these formats don't convert to exactly the other formats " "of their Audio/Video list:\n" + "\n".join(drift) ) + + +def test_enterprise_page_format_pair_claim_is_not_an_overclaim(client, monkeypatch): + """enterprise.html claims "180+ format pairs" — counted from the same + get_public_conversions() that /formats renders from, with spelling + variants (jpg/jpeg, tif/tiff, htm/html, …) merged so they don't count + twice. If the registry ever shrinks below 180 distinct pairs the claim + becomes false; fail loudly here instead of overclaiming on a public page.""" + from app.core.config import settings + + monkeypatch.setattr(settings, "pricing_page_enabled", True) + distinct = { + (_alias(src), _alias(tgt)) for src, tgts in get_public_conversions().items() for tgt in tgts + } + pairs = len({pair for pair in distinct if pair[0] != pair[1]}) + assert pairs >= 180, f"registry only has {pairs} distinct pairs — enterprise.html claims 180+" + for path in ("/en/enterprise", "/de/enterprise"): + assert "180+" in client.get(path).text, f"{path} lost its '180+ format pairs' claim" diff --git a/tests/test_pricing_centralized.py b/tests/test_pricing_centralized.py index d031bac..c395f7b 100644 --- a/tests/test_pricing_centralized.py +++ b/tests/test_pricing_centralized.py @@ -16,6 +16,8 @@ from __future__ import annotations +import re + from app.core.config import settings @@ -79,3 +81,13 @@ def test_commercial_pages_make_no_false_certification_claims(client, monkeypatch body = client.get(url).text assert "ISO 27001 certified" not in body assert "SOC 2" not in body + + +def test_terms_page_has_no_hardcoded_euro_price(client): + """Terms §8 must point to /pricing for the current amount, never hardcode + a euro price that can drift from app/core/pricing.py / settings (it used + to quote "Pro €7/month, Business €19/month" — already stale).""" + for url in ("/de/terms", "/en/terms"): + body = client.get(url).text + # "€7" (en) as well as "7 €" (de) + assert not re.search(r"€\s?\d|\d\s?€", body), f"{url} hardcodes a euro price in Terms" From 3f19de95abf00d6db3bfbfc8f6c5e3afc6987916 Mon Sep 17 00:00:00 2001 From: MrChengLen Date: Tue, 29 Sep 2026 09:11:50 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=EF=BB=BFchore(changelog):=20take=20the=20e?= =?UTF-8?q?ntry=20out=20to=20merge=20main=20in=20=C3=A2=E2=82=AC=E2=80=9D?= =?UTF-8?q?=20back=20in=20two=20commits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main gained two CHANGELOG entries (#170) after this branch was cut, and every PR adds its entry at the same place. Removing this PR's entry lets GitHub merge main in without a conflict; the next commit puts it back on top. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d2b80..a9fcfe0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,34 +9,6 @@ Versions follow [Semantic Versioning](https://semver.org/). ## [Unreleased] -### Fixed — website texts match what the service does - -The public pages were checked claim by claim against the code: - -- **Terms §8** no longer quotes subscription prices (they come from the - pricing page) and describes cancellation by email. -- **Privacy policy:** the rate-limit description is accurate (set per route, - from 5 contact messages per hour to 120 requests per minute); the list of - stored data adds the preferred language, the email-verification timestamp - and per-request usage rows; sub-processor locations are corrected (Hetzner: - a data centre in the EU; Zoho: Amsterdam and Dublin); paid plans are not - yet available; the date is refreshed. Logging out now also removes the - legacy `filemorph_api_key` browser entry (`auth.js`), as §6 says — a test - in `tests/test_cookie_notice.py` pins it. -- **/pricing and /enterprise:** API access without a key, v1.1.0 as the - tagged release, the audit log's scope, the veraPDF gate, "180+ format - pairs" (distinct pairs, pinned by a test), support response times agreed - per contract, and a PGP key on request. -- **/formats** limits exact target-size compression to JPEG, WebP and AVIF. -- **Dashboard:** account deletion names the 10-year tax-retention record. -- **/security** and `/.well-known/security.txt` drop the GitHub Security - Advisories option, which is not enabled on this repository. -- The redaction page no longer calls its engine AGPL open source (`app/ee` - is commercially licensed); the JSON-LD `featureList` adds the PDF tools - and PDF → PDF/A. -- `tests/test_pricing_centralized.py` fails if the terms page hardcodes a - euro price in either language. - ### Fixed — API docs: keys are optional, video compression and converter registration described correctly The API reference marked every file route "Authentication: Required", and the From f0b32de847eaed71b18355f029fe7ab2e6f8c0e6 Mon Sep 17 00:00:00 2001 From: MrChengLen Date: Tue, 29 Sep 2026 09:12:19 +0200 Subject: [PATCH 3/3] docs(changelog): website entry back on top of the merged changelog Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a510c34..7998f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,34 @@ Versions follow [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Fixed — website texts match what the service does + +The public pages were checked claim by claim against the code: + +- **Terms §8** no longer quotes subscription prices (they come from the + pricing page) and describes cancellation by email. +- **Privacy policy:** the rate-limit description is accurate (set per route, + from 5 contact messages per hour to 120 requests per minute); the list of + stored data adds the preferred language, the email-verification timestamp + and per-request usage rows; sub-processor locations are corrected (Hetzner: + a data centre in the EU; Zoho: Amsterdam and Dublin); paid plans are not + yet available; the date is refreshed. Logging out now also removes the + legacy `filemorph_api_key` browser entry (`auth.js`), as §6 says — a test + in `tests/test_cookie_notice.py` pins it. +- **/pricing and /enterprise:** API access without a key, v1.1.0 as the + tagged release, the audit log's scope, the veraPDF gate, "180+ format + pairs" (distinct pairs, pinned by a test), support response times agreed + per contract, and a PGP key on request. +- **/formats** limits exact target-size compression to JPEG, WebP and AVIF. +- **Dashboard:** account deletion names the 10-year tax-retention record. +- **/security** and `/.well-known/security.txt` drop the GitHub Security + Advisories option, which is not enabled on this repository. +- The redaction page no longer calls its engine AGPL open source (`app/ee` + is commercially licensed); the JSON-LD `featureList` adds the PDF tools + and PDF → PDF/A. +- `tests/test_pricing_centralized.py` fails if the terms page hardcodes a + euro price in either language. + ### Security — the Docker image is built without a restored build cache `docker.yml` restored BuildKit's GitHub Actions cache (`cache-from: type=gha`)