---
title: "Privacy & GDPR - PerfLocale"
description: "What data PerfLocale processes, which third parties receive it, and how the plugin integrates with the WordPress Privacy tools."
canonical: "https://perflocale.com/docs/privacy/"
source: "https://perflocale.com/docs/privacy/"
format: "markdown"
---
# Privacy & GDPR

This page is a **technical description** of what data PerfLocale processes, where it’s stored, and which third parties receive it. It is _not_ legal advice. Consult a privacy lawyer to finalise privacy-policy wording and data-processing agreements for your site.

## At a glance

-   No tracking. No analytics. No visitor fingerprinting.
-   One cookie (`perflocale_lang`) storing only the chosen language slug - `HttpOnly`, `Secure`, `SameSite=Lax`, 365-day default lifetime.
-   Visitor IP is never logged or stored. The GeoIP feature (_disabled by default_) ships with no lookup service of its own; if you wire one up, the IP is sent to it only to resolve a country code, which is then cached for 24 hours.
-   Machine-translation features are admin-triggered. End-visitor content is not sent to MT providers automatically.
-   Integrates with WordPress’s built-in privacy tools: Export, Erase, and Policy Guide.

## What data is handled

### Language-preference cookie

Name: `perflocale_lang`. Value: the active language slug (e.g. `en`, `fr`, `de`). Attributes: `HttpOnly`, `Secure` on HTTPS, `SameSite=Lax`. Lifetime: 365 days by default, configurable via the `cookie_lifetime` setting or the `perflocale/cookie_lifetime` filter.

This cookie contains **no personal data** - only the UI preference. Most privacy frameworks classify it as “strictly necessary” for a multilingual site (equivalent to a shopping-cart cookie on an e-commerce site) and it does not require consent on that basis. Your local counsel should confirm this for your jurisdiction.

If you prefer to set **no cookie at all**, switch on **Cookieless mode** (Settings → URL & Routing → Language Cookie) - the cookie is never written. Language routing is URL-based, so it keeps working; you only lose “remember my language” on non-prefixed URLs. The `perflocale/privacy/consent_given` filter (below) suppresses the cookie too, so a consent plugin can gate it automatically.

### IP addresses - GeoIP feature (opt-in)

Disabled by default, and **PerfLocale bundles no geolocation service** — out of the box the feature contacts nothing at all. It only resolves a country when the site wires up a source of its own through the `perflocale/geo/lookup_country` or `perflocale/geo/providers` filters. When that is in place and the feature is enabled (Settings → URL & Routing → GeoIP Redirect), the visitor IP from `REMOTE_ADDR` (or a trusted proxy header) is passed to your lookup source once per first-time visit, and a country code comes back.

-   The IP address itself is **never persisted** to the database or logged.
-   The resolved country code is cached server-side for 24 hours (configurable) under a key derived from a site-salted HMAC of the _anonymised_ address (host bits zeroed by `wp_privacy_anonymize_ip()`) - the IP is not reversibly stored in the cache key, and one cache entry covers a whole subnet.
-   Because you choose the lookup source, its data-processing terms are yours to review. If it is a third-party API, you are the one sending visitor IPs to it.
-   Disable in one filter: `add_filter('perflocale/privacy/consent_given', '__return_false');`

### Machine-translation content (admin-triggered)

Disabled by default. When enabled and used, the plugin sends post/page/string content - whatever the administrator or a translator initiates - to the configured MT provider (DeepL, Google, Microsoft, LibreTranslate, an external agency, or — on WordPress 7.0+ — the WordPress AI Client, which routes through whatever AI provider the site has configured). The response is stored as a draft translation for human review.

-   End visitors never trigger MT - only admins, editors, or users with the `perflocale_use_mt` capability.
-   No visitor metadata is attached to MT requests. The payload is the source text plus language codes.
-   If translating content that contains personal data, ensure you have a legal basis and a data-processing agreement with the chosen MT provider.

### Accept-Language header (browser-language redirect feature)

When the browser-language redirect setting is on, the standard `Accept-Language` header is read once per first visit and matched against active site languages. The header is not stored, logged, or forwarded anywhere.

### Background-job dispatch identity

Long-running operations (XLIFF imports, bulk machine translation, migration runs, full-site string scans, data exports) are queued as background jobs and persisted as rows in the dedicated `{$wpdb->prefix}perflocale_jobs` table. Each row records the user ID of the admin who dispatched it in the indexed `created_by` column so the worker can re-validate the capability when it eventually runs. On Erase Personal Data and on `delete_user`, **`created_by` is zeroed on every job row that carried the user’s ID**; the worker’s next cap re-validation then fails any still-pending job cleanly. Completed and failed jobs auto-purge after 24 hours via daily GC regardless.

### Admin user preferences

For registered users with admin access, PerfLocale stores up to five small UI-state entries against the user profile: the per-page list length on the Strings, Translations, and Languages admin screens, plus which language columns are hidden on the Strings and Translations tables. These are pure UI preferences - no payment data, no behavioural tracking. They’re exported by Tools → Export Personal Data and deleted by Tools → Erase Personal Data or automatically when WordPress deletes the user.

## WordPress Privacy tools integration

PerfLocale registers with all three WordPress privacy surfaces:

-   **Tools → Export Personal Data:** exports the user’s PerfLocale admin-UI preferences (per-page list lengths, hidden language columns) plus a summary row for each background job they dispatched (job type, status, created-at). Both are small, fixed sets, so everything is emitted on page 1.
-   **Tools → Erase Personal Data:** on a single pass — (1) zeroes `created_by` on every background-job row that carried the user’s ID; (2) deletes every PerfLocale admin-UI preference outright. Returns **integer counts**: `items_removed` covers the UI-meta deletions plus the job-row anonymisations, and `items_retained` is `0` — PerfLocale keeps nothing that identifies the data subject after the pass.
-   **Settings → Privacy → Policy Guide:** suggested privacy-policy text is registered via `wp_add_privacy_policy_content()`. The sections shown adapt to which features are enabled - the browser-language, GeoIP, edge-redirect, and machine-translation paragraphs each appear only when that feature is switched on. The language-cookie and admin-preferences sections always appear.

## Consent-management integration

The `perflocale_lang` cookie, the GeoIP redirect, and the browser-language redirect are all gated by a single filter. Any consent-management plugin can hook it to hold them back until the visitor has consented:

```php
add_filter( 'perflocale/privacy/consent_given', function (): bool {
	// Example: Complianz
	if ( function_exists( 'complianz_has_consent' ) ) {
		return (bool) complianz_has_consent( 'functional' );
	}

	// Example: Cookiebot
	if ( class_exists( '\Cybot\Consent' ) ) {
		return \Cybot\Consent::has_consent( 'preferences' );
	}

	return true;
} );
```

When the filter returns `false`, the `perflocale_lang` cookie is not set, **no outbound request** is made (including to the GeoIP provider), and no redirect is issued. On the next page load, once the visitor grants consent, the filter returns `true` and normal behaviour resumes.

## What you (as site owner) still need to do

-   Publish a privacy policy describing these processes in your own words. The Policy Guide page gives you suggested text you can adapt.
-   Sign Data Processing Agreements with any third parties you enable: the geolocation source you wire up, your MT provider, and any exchange-rate or analytics services you configure separately. PerfLocale bundles no geolocation or exchange-rate service, so each of those is a choice you make and a contract you own.
-   Install a consent-management plugin if you need pre-consent gating. PerfLocale itself doesn’t show a banner - that’s better handled by a dedicated plugin (Cookiebot, Complianz, Iubenda, OneTrust, etc.).
-   For EU deployments of DeepL, configure the EU endpoint / key so content stays in-region.

## Related references

-   [Hook reference - `perflocale/privacy/consent_given`](https://perflocale.com/docs/hooks/#perflocale-privacy-consent-given)
-   [Storing API keys as environment variables or PHP constants](https://perflocale.com/docs/api-key-constants/)
-   [GeoIP redirect documentation](https://perflocale.com/docs/geo-redirect/)
