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:
@@ -222,9 +222,18 @@
|
|||||||
}
|
}
|
||||||
$tr.append( $st );
|
$tr.append( $st );
|
||||||
|
|
||||||
// Action: take over as a new service (only useful for uncovered third parties)
|
// Action column.
|
||||||
const $act = $( '<td></td>' );
|
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,
|
// Prefill from the matching preset (full data) if there is one,
|
||||||
// otherwise just seed host + pattern.
|
// otherwise just seed host + pattern.
|
||||||
const data = preset
|
const data = preset
|
||||||
@@ -250,40 +259,6 @@
|
|||||||
$out.append( $table );
|
$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 ── */
|
/* ── Activate a tab by key ── */
|
||||||
function activateTab( tab ) {
|
function activateTab( tab ) {
|
||||||
$( '.cb-tab-content' ).hide();
|
$( '.cb-tab-content' ).hide();
|
||||||
|
|||||||
@@ -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-autodetect.php';
|
||||||
require_once CB_DIR . 'includes/class-license.php';
|
require_once CB_DIR . 'includes/class-license.php';
|
||||||
require_once CB_DIR . 'includes/class-updater.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
|
// Language policy: German source strings for any German locale (de_*), English
|
||||||
// for everything else. We map the plugin's locale accordingly and ship an
|
// 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_Autodetect::init();
|
||||||
CB_License::init();
|
CB_License::init();
|
||||||
CB_Updater::init();
|
CB_Updater::init();
|
||||||
CB_Fonts::init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule / clear the daily license re-check.
|
// Schedule / clear the daily license re-check.
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -170,7 +170,6 @@ class CB_Settings {
|
|||||||
'newServiceLbl' => __( 'Neuer Dienst', 'gdpr-content-blocker' ),
|
'newServiceLbl' => __( 'Neuer Dienst', 'gdpr-content-blocker' ),
|
||||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||||
'scanNonce' => wp_create_nonce( 'cb_scan' ),
|
'scanNonce' => wp_create_nonce( 'cb_scan' ),
|
||||||
'fontsNonce' => wp_create_nonce( 'cb_fonts' ),
|
|
||||||
'i18n' => [
|
'i18n' => [
|
||||||
'scanning' => __( 'Scanne Webseite …', 'gdpr-content-blocker' ),
|
'scanning' => __( 'Scanne Webseite …', 'gdpr-content-blocker' ),
|
||||||
'scanError' => __( 'Scan fehlgeschlagen:', 'gdpr-content-blocker' ),
|
'scanError' => __( 'Scan fehlgeschlagen:', 'gdpr-content-blocker' ),
|
||||||
@@ -189,9 +188,7 @@ class CB_Settings {
|
|||||||
'templateAvail' => __( 'Vorlage verfügbar', 'gdpr-content-blocker' ),
|
'templateAvail' => __( 'Vorlage verfügbar', 'gdpr-content-blocker' ),
|
||||||
'scannedPages'=> __( 'Gescannte Seiten:', 'gdpr-content-blocker' ),
|
'scannedPages'=> __( 'Gescannte Seiten:', 'gdpr-content-blocker' ),
|
||||||
'foundOn' => __( 'Gefunden auf', 'gdpr-content-blocker' ),
|
'foundOn' => __( 'Gefunden auf', 'gdpr-content-blocker' ),
|
||||||
'fontsFetching' => __( 'Schriften werden heruntergeladen …', 'gdpr-content-blocker' ),
|
'fontHint' => __( 'Schriftart für DSGVO-konformen Betrieb lokal hosten.', 'gdpr-content-blocker' ),
|
||||||
'fontsError' => __( 'Fehler beim Herunterladen:', 'gdpr-content-blocker' ),
|
|
||||||
'fontsClearConfirm'=> __( 'Lokale Schriften wirklich löschen?', 'gdpr-content-blocker' ),
|
|
||||||
],
|
],
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
@@ -277,9 +274,6 @@ class CB_Settings {
|
|||||||
<a href="#cb-tab-style" class="nav-tab" data-cb-tab="style">
|
<a href="#cb-tab-style" class="nav-tab" data-cb-tab="style">
|
||||||
<?php esc_html_e( 'Darstellung', 'gdpr-content-blocker' ); ?>
|
<?php esc_html_e( 'Darstellung', 'gdpr-content-blocker' ); ?>
|
||||||
</a>
|
</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">
|
<a href="#cb-tab-shortcodes" class="nav-tab" data-cb-tab="shortcodes">
|
||||||
<?php esc_html_e( 'Shortcodes', 'gdpr-content-blocker' ); ?>
|
<?php esc_html_e( 'Shortcodes', 'gdpr-content-blocker' ); ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -352,11 +346,6 @@ class CB_Settings {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fonts Tab -->
|
|
||||||
<div id="cb-tab-fonts" class="cb-tab-content" style="display:none;">
|
|
||||||
<?php CB_Fonts::render_tab(); ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Shortcodes Tab -->
|
<!-- Shortcodes Tab -->
|
||||||
<div id="cb-tab-shortcodes" class="cb-tab-content" style="display:none;">
|
<div id="cb-tab-shortcodes" class="cb-tab-content" style="display:none;">
|
||||||
<?php self::render_shortcodes_help(); ?>
|
<?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
|
* 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.
|
* 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( '/' ) ];
|
$urls = [ home_url( '/' ) ];
|
||||||
|
|
||||||
$posts = get_posts( [
|
$posts = get_posts( [
|
||||||
|
|||||||
Binary file not shown.
@@ -6,51 +6,12 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Language: en_US\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."
|
msgid "Ungültige Antwort vom Lizenzserver."
|
||||||
msgstr "Invalid response from the license server."
|
msgstr "Invalid response from the license server."
|
||||||
|
|
||||||
|
msgid "Keine Berechtigung."
|
||||||
|
msgstr "Insufficient permissions."
|
||||||
|
|
||||||
msgid "Bitte einen Lizenzschlüssel eingeben."
|
msgid "Bitte einen Lizenzschlüssel eingeben."
|
||||||
msgstr "Please enter a license key."
|
msgstr "Please enter a license key."
|
||||||
|
|
||||||
@@ -231,14 +192,8 @@ msgstr "Scanned pages:"
|
|||||||
msgid "Gefunden auf"
|
msgid "Gefunden auf"
|
||||||
msgstr "Found on"
|
msgstr "Found on"
|
||||||
|
|
||||||
msgid "Schriften werden heruntergeladen …"
|
msgid "Schriftart für DSGVO-konformen Betrieb lokal hosten."
|
||||||
msgstr "Downloading fonts …"
|
msgstr "Host this font locally for GDPR-compliant operation."
|
||||||
|
|
||||||
msgid "Fehler beim Herunterladen:"
|
|
||||||
msgstr "Download error:"
|
|
||||||
|
|
||||||
msgid "Lokale Schriften wirklich löschen?"
|
|
||||||
msgstr "Really delete the local fonts?"
|
|
||||||
|
|
||||||
msgid "Google Ireland Ltd., Irland / Google LLC, USA"
|
msgid "Google Ireland Ltd., Irland / Google LLC, USA"
|
||||||
msgstr "Google Ireland Ltd., Ireland / Google LLC, USA"
|
msgstr "Google Ireland Ltd., Ireland / Google LLC, USA"
|
||||||
@@ -270,9 +225,6 @@ msgstr "Scan"
|
|||||||
msgid "Darstellung"
|
msgid "Darstellung"
|
||||||
msgstr "Appearance"
|
msgstr "Appearance"
|
||||||
|
|
||||||
msgid "Schriften"
|
|
||||||
msgstr "Fonts"
|
|
||||||
|
|
||||||
msgid "Shortcodes"
|
msgid "Shortcodes"
|
||||||
msgstr "Shortcodes"
|
msgstr "Shortcodes"
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,6 @@
|
|||||||
# 139 unique strings
|
# 123 unique strings
|
||||||
MSGID Keine Berechtigung.
|
|
||||||
MSGID Lokale Schriften gelöscht.
|
|
||||||
MSGID Upload-Verzeichnis konnte nicht erstellt werden.
|
|
||||||
MSGID %1$d Schrift-Stylesheet(s) und %2$d Schriftdatei(en) lokal gespeichert.
|
|
||||||
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.
|
|
||||||
MSGID Lokales Hosting
|
|
||||||
MSGID Lokale Schriften ein/aus
|
|
||||||
MSGID Wenn aktiv, werden auf der Webseite die heruntergeladenen lokalen Schriften statt der Google-Pfade ausgeliefert.
|
|
||||||
MSGID Speichern
|
|
||||||
MSGID Schriften herunterladen
|
|
||||||
MSGID Jetzt herunterladen & einbinden
|
|
||||||
MSGID Lokale Schriften löschen
|
|
||||||
MSGID Aktuell lokal: %1$d Stylesheet(s), %2$d Schriftdatei(en) – zuletzt aktualisiert am %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.
|
|
||||||
MSGID Ungültige Antwort vom Lizenzserver.
|
MSGID Ungültige Antwort vom Lizenzserver.
|
||||||
|
MSGID Keine Berechtigung.
|
||||||
MSGID Bitte einen Lizenzschlüssel eingeben.
|
MSGID Bitte einen Lizenzschlüssel eingeben.
|
||||||
MSGID Lizenz erfolgreich aktiviert.
|
MSGID Lizenz erfolgreich aktiviert.
|
||||||
MSGID Alle Plätze dieser Lizenz sind belegt. Geben Sie eine Domain frei, um diese Seite zu aktivieren.
|
MSGID Alle Plätze dieser Lizenz sind belegt. Geben Sie eine Domain frei, um diese Seite zu aktivieren.
|
||||||
@@ -74,9 +61,7 @@ MSGID Vorlage übernehmen
|
|||||||
MSGID Vorlage verfügbar
|
MSGID Vorlage verfügbar
|
||||||
MSGID Gescannte Seiten:
|
MSGID Gescannte Seiten:
|
||||||
MSGID Gefunden auf
|
MSGID Gefunden auf
|
||||||
MSGID Schriften werden heruntergeladen …
|
MSGID Schriftart für DSGVO-konformen Betrieb lokal hosten.
|
||||||
MSGID Fehler beim Herunterladen:
|
|
||||||
MSGID Lokale Schriften wirklich löschen?
|
|
||||||
MSGID Google Ireland Ltd., Irland / Google LLC, USA
|
MSGID Google Ireland Ltd., Irland / Google LLC, USA
|
||||||
MSGID Darstellung interaktiver Karten und Standortinformationen.
|
MSGID Darstellung interaktiver Karten und Standortinformationen.
|
||||||
MSGID Einbettung und Wiedergabe von Videos.
|
MSGID Einbettung und Wiedergabe von Videos.
|
||||||
@@ -87,7 +72,6 @@ MSGID Einstellungen gespeichert.
|
|||||||
MSGID Dienste
|
MSGID Dienste
|
||||||
MSGID Scan
|
MSGID Scan
|
||||||
MSGID Darstellung
|
MSGID Darstellung
|
||||||
MSGID Schriften
|
|
||||||
MSGID Shortcodes
|
MSGID Shortcodes
|
||||||
MSGID Lizenz
|
MSGID Lizenz
|
||||||
MSGID Über das Plugin
|
MSGID Über das Plugin
|
||||||
|
|||||||
@@ -121,21 +121,5 @@
|
|||||||
"Unerwartete Antwort vom Lizenzserver.": "Unexpected response from the license server.",
|
"Unerwartete Antwort vom Lizenzserver.": "Unexpected response from the license server.",
|
||||||
"Lizenz upgraden": "Upgrade license",
|
"Lizenz upgraden": "Upgrade license",
|
||||||
"Schalten Sie weitere Domains frei, um diese Seite zusätzlich zu aktivieren – oder geben Sie unten eine bestehende Domain frei.": "Unlock more domains to additionally activate this site — or release an existing domain below.",
|
"Schalten Sie weitere Domains frei, um diese Seite zusätzlich zu aktivieren – oder geben Sie unten eine bestehende Domain frei.": "Unlock more domains to additionally activate this site — or release an existing domain below.",
|
||||||
"Schriften": "Fonts",
|
"Schriftart für DSGVO-konformen Betrieb lokal hosten.": "Host this font locally for GDPR-compliant operation."
|
||||||
"Schriften werden heruntergeladen …": "Downloading fonts …",
|
|
||||||
"Fehler beim Herunterladen:": "Download error:",
|
|
||||||
"Lokale Schriften wirklich löschen?": "Really delete the local fonts?",
|
|
||||||
"Upload-Verzeichnis konnte nicht erstellt werden.": "The upload directory could not be created.",
|
|
||||||
"Lokale Schriften gelöscht.": "Local fonts deleted.",
|
|
||||||
"%1$d Schrift-Stylesheet(s) und %2$d Schriftdatei(en) lokal gespeichert.": "%1$d font stylesheet(s) and %2$d font file(s) stored locally.",
|
|
||||||
"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.": "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.",
|
|
||||||
"Lokales Hosting": "Local hosting",
|
|
||||||
"Lokale Schriften ein/aus": "Local fonts on/off",
|
|
||||||
"Wenn aktiv, werden auf der Webseite die heruntergeladenen lokalen Schriften statt der Google-Pfade ausgeliefert.": "When enabled, the website serves the downloaded local fonts instead of the Google paths.",
|
|
||||||
"Speichern": "Save",
|
|
||||||
"Schriften herunterladen": "Download fonts",
|
|
||||||
"Jetzt herunterladen & einbinden": "Download & embed now",
|
|
||||||
"Lokale Schriften löschen": "Delete local fonts",
|
|
||||||
"Aktuell lokal: %1$d Stylesheet(s), %2$d Schriftdatei(en) – zuletzt aktualisiert am %3$s.": "Currently local: %1$d stylesheet(s), %2$d font file(s) — last updated on %3$s.",
|
|
||||||
"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.": "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."
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user