Files
GDPR-Content-Blocker/hilfsdaten/build-en-mo.py
s4luorth a0972b4bbf feat: belegte domains als liste mit papierkorb + status-live-refresh
- Bei voller Lizenz werden alle belegten Domains untereinander gelistet, je
  mit Papierkorb-Button + Bestaetigungsdialog (statt Dropdown).
- Trash gibt die Domain frei (deaktiviert serverseitig) und aktiviert diese Seite.
- Lizenz-Tab re-validiert den Status live (max alle 5 Min), damit eine an anderer
  Stelle freigegebene Domain hier sofort als ungueltig erscheint statt stale aktiv.
- EN-Uebersetzung (122) aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:27:34 +02:00

185 lines
7.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Build gdpr-content-blocker-en_US.po + .mo from strings.txt (German keys in
file order) + the EN list below (same order)."""
import struct, sys, os
EN = [
"Invalid response from the license server.",
"Insufficient permissions.",
"Please enter a license key.",
"License activated successfully.",
"All seats for this license are in use. Release a domain to activate this site.",
"Activation failed.",
"Please select a valid domain to release.",
"Released %s and activated this site.",
"License is no longer valid.",
"GDPR Content Blocker is not licensed. Protection stays active, but please add a %s for updates and support.",
"License key",
"Active",
"Invalid",
"All seats in use",
"Not activated",
"Status",
"Domain",
"Bound domains",
"This license is already active on the following domains. Remove one to free a seat — this site will then be activated automatically.",
"Really remove this domain from the license?",
"Remove domain",
"Hidden for security. To change it, deactivate first.",
"Deactivate license",
"Try again",
"Activate license",
"Recipient:",
"Purpose:",
"⚠ Data transfer to a third country outside the EU/EEA",
"Provider's privacy policy",
"Load %s now",
"Always load this service from now on",
"Loading this content from %s requires your consent. Personal data (e.g. your IP address) will be transmitted to the provider.",
"Withdraw consent for external content",
"Affects only the release of external embeds (e.g. maps, videos). Cookie settings are managed separately.",
"Transfer to a third country outside the EU/EEA",
"Recipient",
"Purpose",
"Sets cookies",
"Yes",
"No",
"Privacy policy",
"GDPR Content Blocker",
"Really remove this service?",
"New service",
"Scanning website …",
"Scan failed:",
"No external resources found.",
"Provider / Host",
"Type",
"Count",
"Example URL",
"Action",
"covered",
"third party",
"own domain",
"Add as service",
"Use template",
"Template available",
"Scanned pages:",
"Found on",
"Google Ireland Ltd., Ireland / Google LLC, USA",
"Display of interactive maps and location information.",
"Embedding and playback of videos.",
"OpenStreetMap Foundation, United Kingdom",
"Display of interactive maps.",
"Vimeo LLC, USA",
"Settings saved.",
"Services",
"Scan",
"Appearance",
"Shortcodes",
"License",
"About the plugin",
"+ Add empty service",
"— Insert template —",
"Save services",
"The license server visits your website and lists all embedded third-party resources (iframes, scripts, fonts, images, …). This shows at a glance which external services you should block. Requires an active license.",
"Scan website",
"Save appearance",
"new",
"Show/hide details",
"Blocker on/off",
"Remove service",
"Internal name (slug)",
"Provider name",
"Detection pattern (domain/path)",
"Recipient (incl. country)",
"Provider's privacy policy URL",
"Processing purpose",
"Custom placeholder text (empty = default)",
"Data transfer to a third country (outside EU/EEA)",
"Loads external scripts",
"Placeholder text color",
"Placeholder background color",
"Button: background color",
"Button: text color",
"Button hover: background color",
"Button hover: text color",
"Custom CSS",
"Loaded after the CSS variables and can override them. Tip: use the same prefix, e.g. .cb-blocker .cb-blocker__button { … }",
"CSS classes:",
"Withdrawal (privacy policy)",
"Renders a clearly visible link that withdraws consent for external embeds and reloads the page (Art. 7(3) GDPR). Does NOT affect the cookie consent of a separate cookie plugin. Options: text=\"\", style=\"link|button\", note=\"yes|no\".",
"Block content manually",
"Wraps an iframe and replaces it with the placeholder of the given service. Use the internal name from the \"Services\" tab.",
"Services overview (privacy policy)",
"Lists all configured services with recipient, third-country note, purpose and privacy link. Ideal for inclusion in your privacy policy (Art. 13 GDPR).",
"Version %s",
"GDPR Content Blocker loads external embeds (e.g. Google Maps, YouTube, OpenStreetMap, Vimeo) only after the visitor actively consents. Before the click no connection to the third party is made — so no personal data (e.g. the IP address) is transmitted. This lets you embed external content in a GDPR-compliant way without a heavy consent tool.",
"Features",
"True click-to-load (no preloaded iframe)",
"Granular consent per service",
"Art.-13-compliant placeholder with recipient, purpose, third-country note and privacy link",
"Easy withdrawal via shortcode (Art. 7(3) GDPR)",
"Automatic detection of common providers + manual shortcode",
"Legal",
"Imprint",
"Privacy policy",
"Note: This plugin is a technical tool and does not constitute legal advice. The site operator is responsible for the legally compliant configuration (recipients, purposes, privacy policy).",
"Developed by %s",
"Please activate a license first (\"License\" tab).",
"Unexpected response from the license server.",
]
base = sys.argv[1]
strings_file = os.path.join(base, 'hilfsdaten', 'strings.txt')
de = []
with open(strings_file, encoding='utf-8') as f:
for line in f:
if line.startswith('MSGID\t'):
de.append(line[len('MSGID\t'):].rstrip('\n'))
if len(de) != len(EN):
print(f"MISMATCH: {len(de)} German vs {len(EN)} English"); sys.exit(1)
header = ("Project-Id-Version: GDPR Content Blocker\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en_US\n")
entries = {'': header}
for d, e in zip(de, EN):
entries[d] = e
def po_escape(s):
return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
po_path = os.path.join(base, 'gdpr-content-blocker', 'languages', 'gdpr-content-blocker-en_US.po')
with open(po_path, 'w', encoding='utf-8') as f:
f.write('msgid ""\nmsgstr ""\n')
for line in header.rstrip('\n').split('\n'):
f.write('"%s\\n"\n' % po_escape(line))
f.write('\n')
for d, e in zip(de, EN):
f.write('msgid "%s"\n' % po_escape(d))
f.write('msgstr "%s"\n\n' % po_escape(e))
def write_mo(entries, out):
keys = sorted(entries.keys())
ids = b''; strs = b''; offsets = []
for k in keys:
kb = k.encode('utf-8'); vb = entries[k].encode('utf-8')
offsets.append((len(ids), len(kb), len(strs), len(vb)))
ids += kb + b'\x00'; strs += vb + b'\x00'
keystart = 7 * 4 + 16 * len(keys)
valuestart = keystart + len(ids)
ko = []; vo = []
for o1, l1, o2, l2 in offsets:
ko += [l1, o1 + keystart]; vo += [l2, o2 + valuestart]
out_b = struct.pack("Iiiiiii", 0x950412de, 0, len(keys), 7 * 4, 7 * 4 + len(keys) * 8, 0, 0)
out_b += struct.pack("i" * len(ko), *ko) + struct.pack("i" * len(vo), *vo) + ids + strs
with open(out, 'wb') as f:
f.write(out_b)
mo_path = os.path.join(base, 'gdpr-content-blocker', 'languages', 'gdpr-content-blocker-en_US.mo')
write_mo(entries, mo_path)
print(f"OK: {len(de)} strings -> {os.path.basename(po_path)} + .mo")