revert: lokales Google-Fonts-Hosting entfernt + scan zeigt nur hinweis

- Feature "Schriften lokal hosten" komplett zurueckgebaut (class-fonts.php,
  tab, ajax-endpunkte, output-buffer, admin-js-handler, optionen).
- Scan: wird fonts.googleapis.com / fonts.gstatic.com erkannt, steht in der
  Aktion-spalte jetzt der nicht-klickbare hinweis "Schriftart fuer
  DSGVO-konformen Betrieb lokal hosten." statt eines buttons.
- i18n entsprechend bereinigt (123 strings).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
s4luorth
2026-06-08 07:39:34 +02:00
parent 94043baf7c
commit b6df4c64af
8 changed files with 22 additions and 669 deletions

View File

@@ -222,9 +222,18 @@
}
$tr.append( $st );
// Action: take over as a new service (only useful for uncovered third parties)
// Action column.
const $act = $( '<td></td>' );
if ( f.third_party && ! f.covered ) {
const host = ( f.host || '' ).toLowerCase();
const isGoogleFont = host === 'fonts.googleapis.com' || host === 'fonts.gstatic.com';
if ( isGoogleFont ) {
// Google Fonts can't be consent-gated meaningfully — the right fix is
// hosting them locally. Show a non-clickable hint, no button.
$act.append( $( '<span></span>' )
.css( { color: '#8a6d3b', fontStyle: 'italic' } )
.text( i18n.fontHint || 'Host this font locally for GDPR-compliant operation.' ) );
} else if ( f.third_party && ! f.covered ) {
// Prefill from the matching preset (full data) if there is one,
// otherwise just seed host + pattern.
const data = preset
@@ -250,40 +259,6 @@
$out.append( $table );
}
/* ── Local Google Fonts: download & embed ── */
$( '#cb-fonts-fetch' ).on( 'click', function () {
const i18n = cbAdmin.i18n || {};
const $btn = $( this ).prop( 'disabled', true );
const $st = $( '#cb-fonts-status' ).text( i18n.fontsFetching || 'Downloading…' );
$.post( cbAdmin.ajaxUrl, { action: 'cb_fonts_fetch', nonce: cbAdmin.fontsNonce } )
.done( function ( resp ) {
if ( ! resp || ! resp.success ) {
const msg = resp && resp.data && resp.data.message ? resp.data.message : 'Error';
$st.text( ( i18n.fontsError || 'Error:' ) + ' ' + msg );
return;
}
$st.text( resp.data.message || 'OK' );
setTimeout( function () { window.location.reload(); }, 900 );
} )
.fail( function ( xhr ) {
$st.text( ( i18n.fontsError || 'Error:' ) + ' ' + xhr.status );
} )
.always( function () {
$btn.prop( 'disabled', false );
} );
} );
$( '#cb-fonts-clear' ).on( 'click', function () {
const i18n = cbAdmin.i18n || {};
if ( ! window.confirm( i18n.fontsClearConfirm || 'Delete local fonts?' ) ) {
return;
}
$( this ).prop( 'disabled', true );
$.post( cbAdmin.ajaxUrl, { action: 'cb_fonts_clear', nonce: cbAdmin.fontsNonce } )
.always( function () { window.location.reload(); } );
} );
/* ── Activate a tab by key ── */
function activateTab( tab ) {
$( '.cb-tab-content' ).hide();

View File

@@ -37,7 +37,6 @@ require_once CB_DIR . 'includes/class-renderer.php';
require_once CB_DIR . 'includes/class-autodetect.php';
require_once CB_DIR . 'includes/class-license.php';
require_once CB_DIR . 'includes/class-updater.php';
require_once CB_DIR . 'includes/class-fonts.php';
// Language policy: German source strings for any German locale (de_*), English
// for everything else. We map the plugin's locale accordingly and ship an
@@ -60,7 +59,6 @@ function cb_init(): void {
CB_Autodetect::init();
CB_License::init();
CB_Updater::init();
CB_Fonts::init();
}
// Schedule / clear the daily license re-check.

View File

@@ -1,529 +0,0 @@
<?php
defined( 'ABSPATH' ) || exit;
/**
* Optional: host Google Fonts locally to avoid the connection to Google's CDN
* (privacy + performance).
*
* Trigger model: "button". The admin clicks "download & embed"; the plugin
* visits the configured pages, finds every Google Fonts reference and downloads
* everything once:
* - <link href="…fonts.googleapis.com/css…"> (also protocol-relative)
* - @import / direct gstatic URLs inside inline <style> blocks
* - @import / url(…gstatic…) inside SAME-ORIGIN external CSS files
* The CSS + font files are stored under wp-content/uploads, all paths are
* rewritten to the local copies, and a remote→local URL map is saved. On the
* front end a lightweight output-buffer pass swaps the mapped URLs (no network).
*
* Anything not covered by the scanned pages simply stays remote until the next
* "download & embed" run (predictable, no per-visitor latency).
*/
class CB_Fonts {
const OPTION = 'cb_fonts';
const DIR = 'gdpr-content-blocker-fonts';
// Modern browser UA so fonts.googleapis.com returns woff2 (not legacy ttf).
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
const MAX_CSS = 40;
const MAX_FONTS = 200;
/* Per-build memoisation (avoid re-downloading across pages). */
private static array $css_done = [];
private static array $font_done = [];
private static array $map = [];
private static int $font_count = 0;
private static int $css_count = 0;
public static function init(): void {
add_action( 'template_redirect', [ __CLASS__, 'start_buffer' ], 5 );
add_action( 'admin_post_cb_save_fonts', [ __CLASS__, 'handle_save' ] );
add_action( 'wp_ajax_cb_fonts_fetch', [ __CLASS__, 'ajax_fetch' ] );
add_action( 'wp_ajax_cb_fonts_clear', [ __CLASS__, 'ajax_clear' ] );
}
public static function get_settings(): array {
$s = get_option( self::OPTION, [] );
$s = wp_parse_args( is_array( $s ) ? $s : [], [
'enabled' => false,
'map' => [],
'updated_at' => 0,
'count' => 0,
'css_count' => 0,
] );
if ( ! is_array( $s['map'] ) ) {
$s['map'] = [];
}
return $s;
}
public static function is_enabled(): bool {
$s = self::get_settings();
return ! empty( $s['enabled'] ) && ! empty( $s['map'] );
}
/* ───────────────────────── front-end rewrite ───────────────────────── */
public static function start_buffer(): void {
if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
return;
}
if ( ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) ) {
return;
}
if ( ! self::is_enabled() ) {
return;
}
ob_start( [ __CLASS__, 'rewrite' ] );
}
public static function rewrite( string $html ): string {
if ( $html === '' ) {
return $html;
}
$map = self::get_settings()['map'];
if ( empty( $map ) ) {
return $html;
}
// Longest keys first so a shorter URL can't pre-empt a longer one.
uksort( $map, fn( $a, $b ) => strlen( (string) $b ) <=> strlen( (string) $a ) );
$html = str_replace( array_keys( $map ), array_values( $map ), $html );
// Drop now-pointless resource hints to Google's font hosts — a preconnect
// would still open a connection and leak the visitor's IP. Match any
// attribute order via look-aheads.
$out = preg_replace(
'#<link\b(?=[^>]*\brel=["\'](?:preconnect|dns-prefetch|preload)["\'])(?=[^>]*(?:fonts\.googleapis\.com|fonts\.gstatic\.com))[^>]*>\s*#i',
'',
$html
);
return $out === null ? $html : $out;
}
/* ───────────────────────── admin actions ───────────────────────── */
public static function handle_save(): void {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Keine Berechtigung.', 'gdpr-content-blocker' ) );
}
check_admin_referer( 'cb_fonts', 'cb_fonts_nonce' );
$s = self::get_settings();
$s['enabled'] = ! empty( $_POST['cb_fonts_enabled'] );
update_option( self::OPTION, $s );
wp_safe_redirect( admin_url( 'options-general.php?page=gdpr-content-blocker&cb_tab=fonts&cb_saved=1' ) );
exit;
}
public static function ajax_fetch(): void {
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( 'Keine Berechtigung.', 'gdpr-content-blocker' ) ], 403 );
}
check_ajax_referer( 'cb_fonts', 'nonce' );
$res = self::build_cache();
if ( is_wp_error( $res ) ) {
wp_send_json_error( [ 'message' => $res->get_error_message() ] );
}
wp_send_json_success( $res );
}
public static function ajax_clear(): void {
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( 'Keine Berechtigung.', 'gdpr-content-blocker' ) ], 403 );
}
check_ajax_referer( 'cb_fonts', 'nonce' );
self::delete_dir();
$s = self::get_settings();
$s['map'] = [];
$s['count'] = 0;
$s['css_count'] = 0;
$s['updated_at'] = 0;
update_option( self::OPTION, $s );
wp_send_json_success( [ 'message' => __( 'Lokale Schriften gelöscht.', 'gdpr-content-blocker' ) ] );
}
/* ───────────────────────── download / cache ───────────────────────── */
private static function build_cache(): array|WP_Error {
self::$css_done = [];
self::$font_done = [];
self::$map = [];
self::$font_count = 0;
self::$css_count = 0;
$upload = wp_upload_dir();
if ( ! empty( $upload['error'] ) ) {
return new WP_Error( 'cb_upload', (string) $upload['error'] );
}
$dir = trailingslashit( $upload['basedir'] ) . self::DIR;
if ( ! wp_mkdir_p( $dir ) ) {
return new WP_Error( 'cb_mkdir', __( 'Upload-Verzeichnis konnte nicht erstellt werden.', 'gdpr-content-blocker' ) );
}
$home_host = self::host( home_url() );
$pages = CB_Settings::scan_urls();
foreach ( $pages as $page ) {
$html = self::http_get( $page );
if ( $html === null ) {
continue;
}
// 1) Google Fonts stylesheets referenced directly (link href / @import).
if ( preg_match_all( '#(?:https?:)?//fonts\.googleapis\.com/[^"\'()\s<>]+#i', $html, $g ) ) {
foreach ( array_unique( $g[0] ) as $found ) {
if ( isset( self::$map[ $found ] ) ) {
continue;
}
$local = self::process_googleapis_css( self::abs_url( $found ) );
if ( $local ) {
self::$map[ $found ] = $local;
}
}
}
// 2) Direct gstatic font URLs in the HTML (e.g. inline @font-face).
if ( preg_match_all( '#(?:https?:)?//fonts\.gstatic\.com/[^"\'()\s<>]+#i', $html, $gs ) ) {
foreach ( array_unique( $gs[0] ) as $found ) {
if ( isset( self::$map[ $found ] ) ) {
continue;
}
$local = self::download_font( self::abs_url( $found ) );
if ( $local ) {
self::$map[ $found ] = $local;
}
}
}
// 3) Same-origin external CSS files that themselves reference Google Fonts.
if ( preg_match_all( '#<link\b[^>]*>#i', $html, $links ) ) {
foreach ( $links[0] as $tag ) {
if ( self::$css_count >= self::MAX_CSS ) {
break;
}
$rel = self::attr( $tag, 'rel' );
$href = self::attr( $tag, 'href' );
if ( $href === '' || stripos( $rel, 'stylesheet' ) === false ) {
continue;
}
$abs = self::abs_url( $href );
$h = self::host( $abs );
if ( $h === 'fonts.googleapis.com' || $h !== $home_host ) {
continue; // googleapis handled in (1); only our own CSS files here
}
$cache_key = 'ext:' . $abs;
if ( isset( self::$css_done[ $cache_key ] ) ) {
continue;
}
self::$css_done[ $cache_key ] = true;
$css = self::http_get( $abs, true );
if ( $css === null
|| ( stripos( $css, 'fonts.googleapis.com' ) === false
&& stripos( $css, 'fonts.gstatic.com' ) === false ) ) {
continue;
}
$localized = self::localize_css( $css, $abs, true );
if ( $localized !== $css ) {
$url = self::save( 'css', $localized );
if ( $url ) {
self::$map[ $href ] = $url;
self::$css_count++;
}
}
}
}
}
$s = self::get_settings();
$s['map'] = self::$map;
$s['count'] = self::$font_count;
$s['css_count'] = self::$css_count;
$s['updated_at'] = time();
$s['enabled'] = true; // a successful build implies the feature is wanted
update_option( self::OPTION, $s );
return [
'fonts' => self::$font_count,
'css' => self::$css_count,
'mapped' => count( self::$map ),
'message' => sprintf(
/* translators: 1: number of stylesheets, 2: number of font files */
__( '%1$d Schrift-Stylesheet(s) und %2$d Schriftdatei(en) lokal gespeichert.', 'gdpr-content-blocker' ),
self::$css_count,
self::$font_count
),
];
}
/** Fetch a googleapis CSS, localise its gstatic URLs, store it; return local URL. */
private static function process_googleapis_css( string $absUrl ): ?string {
if ( array_key_exists( $absUrl, self::$css_done ) ) {
return self::$css_done[ $absUrl ];
}
if ( self::$css_count >= self::MAX_CSS || self::host( $absUrl ) !== 'fonts.googleapis.com' ) {
return null;
}
$css = self::http_get( $absUrl, true );
if ( $css === null ) {
self::$css_done[ $absUrl ] = null;
return null;
}
$css = self::localize_css( $css, $absUrl, false );
$url = self::save( 'css', $css );
if ( $url ) {
self::$css_count++;
}
self::$css_done[ $absUrl ] = $url;
return $url;
}
/** Download a single gstatic font file; return its local URL. */
private static function download_font( string $absUrl ): ?string {
if ( array_key_exists( $absUrl, self::$font_done ) ) {
return self::$font_done[ $absUrl ];
}
if ( self::$font_count >= self::MAX_FONTS || self::host( $absUrl ) !== 'fonts.gstatic.com' ) {
return null;
}
$bin = self::http_get( $absUrl );
if ( $bin === null ) {
self::$font_done[ $absUrl ] = null;
return null;
}
$url = self::save( self::ext_from_url( $absUrl ), $bin );
if ( $url ) {
self::$font_count++;
}
self::$font_done[ $absUrl ] = $url;
return $url;
}
/**
* Rewrite a CSS body: googleapis @import → local; gstatic url() → local file.
* When $absolutize is true (external theme CSS we copy elsewhere), all other
* relative url()s are made absolute against $baseUrl so they keep working.
*/
private static function localize_css( string $css, string $baseUrl, bool $absolutize ): string {
// @import url("…googleapis…") or @import "…googleapis…"
$css = preg_replace_callback(
'#@import\s+(?:url\(\s*)?([\'"]?)([^\'"\)\s]+)\1\s*\)?\s*;#i',
function ( array $m ): string {
$abs = self::abs_url( $m[2] );
if ( self::host( $abs ) !== 'fonts.googleapis.com' ) {
return $m[0];
}
$local = self::process_googleapis_css( $abs );
return $local ? '@import url("' . $local . '");' : $m[0];
},
$css
) ?? $css;
// url(…gstatic…woff2) → local
$css = preg_replace_callback(
'#url\(\s*([\'"]?)([^\'"\)\s]+)\1\s*\)#i',
function ( array $m ) use ( $baseUrl, $absolutize ): string {
$raw = $m[2];
$abs = self::abs_url( $raw, $baseUrl );
if ( self::host( $abs ) === 'fonts.gstatic.com' ) {
$local = self::download_font( $abs );
return $local ? 'url("' . $local . '")' : $m[0];
}
// Keep other resources working when we relocate the CSS file.
if ( $absolutize && ! preg_match( '#^(data:|https?://|//)#i', $raw ) ) {
return 'url("' . $abs . '")';
}
return $m[0];
},
$css
) ?? $css;
return $css;
}
/* ───────────────────────── small helpers ───────────────────────── */
/** GET a URL with a browser UA. Returns the body (string) or null on failure. */
private static function http_get( string $url, bool $css = false ): ?string {
$args = [
'timeout' => 20,
'redirection' => 3,
'user-agent' => self::UA,
];
if ( $css ) {
$args['headers'] = [ 'Accept' => 'text/css,*/*;q=0.1' ];
}
$resp = wp_remote_get( $url, $args );
if ( is_wp_error( $resp ) || wp_remote_retrieve_response_code( $resp ) !== 200 ) {
return null;
}
$body = wp_remote_retrieve_body( $resp );
return $body === '' ? null : $body;
}
/** Save bytes to the uploads font dir (deduped by content hash); return URL. */
private static function save( string $ext, string $contents ): ?string {
$upload = wp_upload_dir();
if ( ! empty( $upload['error'] ) ) {
return null;
}
$dir = trailingslashit( $upload['basedir'] ) . self::DIR;
if ( ! wp_mkdir_p( $dir ) ) {
return null;
}
$ext = preg_replace( '/[^a-z0-9]/', '', strtolower( $ext ) ) ?: 'bin';
$name = md5( $contents ) . '.' . $ext;
$path = trailingslashit( $dir ) . $name;
if ( ! file_exists( $path ) && file_put_contents( $path, $contents ) === false ) {
return null;
}
return trailingslashit( $upload['baseurl'] ) . self::DIR . '/' . $name;
}
private static function delete_dir(): void {
$upload = wp_upload_dir();
if ( ! empty( $upload['error'] ) ) {
return;
}
$dir = trailingslashit( $upload['basedir'] ) . self::DIR;
if ( ! is_dir( $dir ) ) {
return;
}
foreach ( (array) glob( trailingslashit( $dir ) . '*' ) as $file ) {
if ( is_file( $file ) ) {
@unlink( $file );
}
}
}
/** Resolve protocol-relative / relative URLs (and decode HTML entities). */
private static function abs_url( string $url, string $base = '' ): string {
$url = html_entity_decode( trim( $url ), ENT_QUOTES );
if ( str_starts_with( $url, '//' ) ) {
return 'https:' . $url;
}
if ( preg_match( '#^(data:|https?://)#i', $url ) ) {
return $url;
}
if ( $base === '' ) {
return $url;
}
// Relative → absolute against $base.
$b = wp_parse_url( $base );
$scheme = $b['scheme'] ?? 'https';
$host = $b['host'] ?? '';
$port = isset( $b['port'] ) ? ':' . $b['port'] : '';
$path = $b['path'] ?? '/';
if ( str_starts_with( $url, '/' ) ) {
$full = $url;
} else {
$full = preg_replace( '#/[^/]*$#', '/', $path ) . $url;
}
$segs = [];
foreach ( explode( '/', $full ) as $seg ) {
if ( $seg === '..' ) {
array_pop( $segs );
} elseif ( $seg !== '.' && $seg !== '' ) {
$segs[] = $seg;
}
}
return $scheme . '://' . $host . $port . '/' . implode( '/', $segs );
}
private static function host( string $url ): string {
return strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
}
private static function ext_from_url( string $url ): string {
$path = (string) wp_parse_url( $url, PHP_URL_PATH );
$ext = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
return in_array( $ext, [ 'woff2', 'woff', 'ttf', 'otf', 'eot', 'svg' ], true ) ? $ext : 'woff2';
}
/** Read a single attribute value out of an HTML tag string. */
private static function attr( string $tag, string $name ): string {
$re = '#\b' . preg_quote( $name, '#' ) . '\s*=\s*("([^"]*)"|\'([^\']*)\'|([^\s>]+))#i';
if ( preg_match( $re, $tag, $m ) ) {
if ( ( $m[2] ?? '' ) !== '' ) {
return $m[2];
}
if ( ( $m[3] ?? '' ) !== '' ) {
return $m[3];
}
return $m[4] ?? '';
}
return '';
}
/* ───────────────────────── settings tab ───────────────────────── */
public static function render_tab(): void {
$s = self::get_settings();
$has_cache = ! empty( $s['map'] );
$when = $s['updated_at']
? wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), (int) $s['updated_at'] )
: '';
?>
<p class="description" style="max-width:760px;">
<?php esc_html_e( 'Bindet Google Fonts lokal ein: Das Plugin lädt die Schrift-Stylesheets und Schriftdateien einmalig herunter, speichert sie in Ihrem Uploads-Ordner und ersetzt die Pfade im Seitenquelltext. So wird beim Seitenaufruf keine Verbindung mehr zu Google aufgebaut (kein IP-Transfer) und die Schriften laden schneller.', 'gdpr-content-blocker' ); ?>
</p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'cb_fonts', 'cb_fonts_nonce' ); ?>
<input type="hidden" name="action" value="cb_save_fonts">
<table class="form-table" role="presentation"><tbody>
<tr>
<th scope="row"><?php esc_html_e( 'Lokales Hosting', 'gdpr-content-blocker' ); ?></th>
<td>
<label class="cb-switch" title="<?php esc_attr_e( 'Lokale Schriften ein/aus', 'gdpr-content-blocker' ); ?>">
<input type="checkbox" name="cb_fonts_enabled" value="1" <?php checked( ! empty( $s['enabled'] ) ); ?>>
<span class="cb-switch__slider"></span>
</label>
<p class="description"><?php esc_html_e( 'Wenn aktiv, werden auf der Webseite die heruntergeladenen lokalen Schriften statt der Google-Pfade ausgeliefert.', 'gdpr-content-blocker' ); ?></p>
</td>
</tr>
</tbody></table>
<?php submit_button( __( 'Speichern', 'gdpr-content-blocker' ) ); ?>
</form>
<hr>
<h2><?php esc_html_e( 'Schriften herunterladen', 'gdpr-content-blocker' ); ?></h2>
<p>
<button type="button" id="cb-fonts-fetch" class="button button-primary">
<?php esc_html_e( 'Jetzt herunterladen & einbinden', 'gdpr-content-blocker' ); ?>
</button>
<?php if ( $has_cache ) : ?>
<button type="button" id="cb-fonts-clear" class="button" style="margin-left:6px;">
<?php esc_html_e( 'Lokale Schriften löschen', 'gdpr-content-blocker' ); ?>
</button>
<?php endif; ?>
<span id="cb-fonts-status" style="margin-left:10px;"></span>
</p>
<?php if ( $has_cache ) : ?>
<p class="description">
<?php
printf(
/* translators: 1: stylesheet count, 2: font file count, 3: date/time */
esc_html__( 'Aktuell lokal: %1$d Stylesheet(s), %2$d Schriftdatei(en) – zuletzt aktualisiert am %3$s.', 'gdpr-content-blocker' ),
(int) ( $s['css_count'] ?? 0 ),
(int) ( $s['count'] ?? 0 ),
esc_html( $when )
);
?>
</p>
<?php endif; ?>
<p class="description" style="max-width:760px;">
<?php esc_html_e( 'Hinweis: Gescannt werden die Startseite und einige Unterseiten. Falls auf einzelnen Seiten weitere Google Fonts vorkommen, führen Sie die Aktion nach Inhaltsänderungen erneut aus.', 'gdpr-content-blocker' ); ?>
</p>
<?php
}
}

View File

@@ -170,7 +170,6 @@ class CB_Settings {
'newServiceLbl' => __( 'Neuer Dienst', 'gdpr-content-blocker' ),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'scanNonce' => wp_create_nonce( 'cb_scan' ),
'fontsNonce' => wp_create_nonce( 'cb_fonts' ),
'i18n' => [
'scanning' => __( 'Scanne Webseite …', 'gdpr-content-blocker' ),
'scanError' => __( 'Scan fehlgeschlagen:', 'gdpr-content-blocker' ),
@@ -189,9 +188,7 @@ class CB_Settings {
'templateAvail' => __( 'Vorlage verfügbar', 'gdpr-content-blocker' ),
'scannedPages'=> __( 'Gescannte Seiten:', 'gdpr-content-blocker' ),
'foundOn' => __( 'Gefunden auf', 'gdpr-content-blocker' ),
'fontsFetching' => __( 'Schriften werden heruntergeladen …', 'gdpr-content-blocker' ),
'fontsError' => __( 'Fehler beim Herunterladen:', 'gdpr-content-blocker' ),
'fontsClearConfirm'=> __( 'Lokale Schriften wirklich löschen?', 'gdpr-content-blocker' ),
'fontHint' => __( 'Schriftart für DSGVO-konformen Betrieb lokal hosten.', 'gdpr-content-blocker' ),
],
] );
}
@@ -277,9 +274,6 @@ class CB_Settings {
<a href="#cb-tab-style" class="nav-tab" data-cb-tab="style">
<?php esc_html_e( 'Darstellung', 'gdpr-content-blocker' ); ?>
</a>
<a href="#cb-tab-fonts" class="nav-tab" data-cb-tab="fonts">
<?php esc_html_e( 'Schriften', 'gdpr-content-blocker' ); ?>
</a>
<a href="#cb-tab-shortcodes" class="nav-tab" data-cb-tab="shortcodes">
<?php esc_html_e( 'Shortcodes', 'gdpr-content-blocker' ); ?>
</a>
@@ -352,11 +346,6 @@ class CB_Settings {
</form>
</div>
<!-- Fonts Tab -->
<div id="cb-tab-fonts" class="cb-tab-content" style="display:none;">
<?php CB_Fonts::render_tab(); ?>
</div>
<!-- Shortcodes Tab -->
<div id="cb-tab-shortcodes" class="cb-tab-content" style="display:none;">
<?php self::render_shortcodes_help(); ?>
@@ -747,7 +736,7 @@ class CB_Settings {
* Build the list of URLs to scan: the home page plus a few published
* pages/posts, so the scan covers more than just the front page.
*/
public static function scan_urls(): array {
private static function scan_urls(): array {
$urls = [ home_url( '/' ) ];
$posts = get_posts( [

View File

@@ -6,51 +6,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Language: en_US\n"
msgid "Keine Berechtigung."
msgstr "Insufficient permissions."
msgid "Lokale Schriften gelöscht."
msgstr "Local fonts deleted."
msgid "Upload-Verzeichnis konnte nicht erstellt werden."
msgstr "The upload directory could not be created."
msgid "%1$d Schrift-Stylesheet(s) und %2$d Schriftdatei(en) lokal gespeichert."
msgstr "%1$d font stylesheet(s) and %2$d font file(s) stored locally."
msgid "Bindet Google Fonts lokal ein: Das Plugin lädt die Schrift-Stylesheets und Schriftdateien einmalig herunter, speichert sie in Ihrem Uploads-Ordner und ersetzt die Pfade im Seitenquelltext. So wird beim Seitenaufruf keine Verbindung mehr zu Google aufgebaut (kein IP-Transfer) und die Schriften laden schneller."
msgstr "Hosts Google Fonts locally: the plugin downloads the font stylesheets and font files once, stores them in your uploads folder and replaces the paths in the page source. As a result no connection to Google is made on page load (no IP transfer) and fonts load faster."
msgid "Lokales Hosting"
msgstr "Local hosting"
msgid "Lokale Schriften ein/aus"
msgstr "Local fonts on/off"
msgid "Wenn aktiv, werden auf der Webseite die heruntergeladenen lokalen Schriften statt der Google-Pfade ausgeliefert."
msgstr "When enabled, the website serves the downloaded local fonts instead of the Google paths."
msgid "Speichern"
msgstr "Save"
msgid "Schriften herunterladen"
msgstr "Download fonts"
msgid "Jetzt herunterladen & einbinden"
msgstr "Download & embed now"
msgid "Lokale Schriften löschen"
msgstr "Delete local fonts"
msgid "Aktuell lokal: %1$d Stylesheet(s), %2$d Schriftdatei(en) – zuletzt aktualisiert am %3$s."
msgstr "Currently local: %1$d stylesheet(s), %2$d font file(s) — last updated on %3$s."
msgid "Hinweis: Gescannt werden die Startseite und einige Unterseiten. Falls auf einzelnen Seiten weitere Google Fonts vorkommen, führen Sie die Aktion nach Inhaltsänderungen erneut aus."
msgstr "Note: the home page and a few sub-pages are scanned. If individual pages contain additional Google Fonts, run the action again after content changes."
msgid "Ungültige Antwort vom Lizenzserver."
msgstr "Invalid response from the license server."
msgid "Keine Berechtigung."
msgstr "Insufficient permissions."
msgid "Bitte einen Lizenzschlüssel eingeben."
msgstr "Please enter a license key."
@@ -231,14 +192,8 @@ msgstr "Scanned pages:"
msgid "Gefunden auf"
msgstr "Found on"
msgid "Schriften werden heruntergeladen …"
msgstr "Downloading fonts …"
msgid "Fehler beim Herunterladen:"
msgstr "Download error:"
msgid "Lokale Schriften wirklich löschen?"
msgstr "Really delete the local fonts?"
msgid "Schriftart für DSGVO-konformen Betrieb lokal hosten."
msgstr "Host this font locally for GDPR-compliant operation."
msgid "Google Ireland Ltd., Irland / Google LLC, USA"
msgstr "Google Ireland Ltd., Ireland / Google LLC, USA"
@@ -270,9 +225,6 @@ msgstr "Scan"
msgid "Darstellung"
msgstr "Appearance"
msgid "Schriften"
msgstr "Fonts"
msgid "Shortcodes"
msgstr "Shortcodes"