@owlmeans/consent 0.1.18-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/consts.d.ts +64 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +90 -0
- package/build/consts.js.map +1 -0
- package/build/consts.local.d.ts +4 -0
- package/build/consts.local.d.ts.map +1 -0
- package/build/consts.local.js +4 -0
- package/build/consts.local.js.map +1 -0
- package/build/gtm.d.ts +72 -0
- package/build/gtm.d.ts.map +1 -0
- package/build/gtm.js +182 -0
- package/build/gtm.js.map +1 -0
- package/build/i18n/be.json +36 -0
- package/build/i18n/de.json +36 -0
- package/build/i18n/en.json +36 -0
- package/build/i18n/es.json +36 -0
- package/build/i18n/fr.json +36 -0
- package/build/i18n/pl.json +36 -0
- package/build/i18n/ru.json +36 -0
- package/build/i18n/uk.json +36 -0
- package/build/i18n.d.ts +16 -0
- package/build/i18n.d.ts.map +1 -0
- package/build/i18n.js +29 -0
- package/build/i18n.js.map +1 -0
- package/build/index.d.ts +12 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +9 -0
- package/build/index.js.map +1 -0
- package/build/linker.d.ts +55 -0
- package/build/linker.d.ts.map +1 -0
- package/build/linker.js +334 -0
- package/build/linker.js.map +1 -0
- package/build/plugins.d.ts +30 -0
- package/build/plugins.d.ts.map +1 -0
- package/build/plugins.js +73 -0
- package/build/plugins.js.map +1 -0
- package/build/storage.d.ts +27 -0
- package/build/storage.d.ts.map +1 -0
- package/build/storage.js +86 -0
- package/build/storage.js.map +1 -0
- package/build/store.d.ts +16 -0
- package/build/store.d.ts.map +1 -0
- package/build/store.js +100 -0
- package/build/store.js.map +1 -0
- package/build/types.d.ts +205 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/types.local.d.ts +5 -0
- package/build/types.local.d.ts.map +1 -0
- package/build/types.local.js +2 -0
- package/build/types.local.js.map +1 -0
- package/package.json +33 -0
- package/src/consts.local.ts +4 -0
- package/src/consts.ts +103 -0
- package/src/gtm.ts +216 -0
- package/src/i18n/be.json +36 -0
- package/src/i18n/de.json +36 -0
- package/src/i18n/en.json +36 -0
- package/src/i18n/es.json +36 -0
- package/src/i18n/fr.json +36 -0
- package/src/i18n/pl.json +36 -0
- package/src/i18n/ru.json +36 -0
- package/src/i18n/uk.json +36 -0
- package/src/i18n.ts +39 -0
- package/src/index.ts +14 -0
- package/src/linker.ts +369 -0
- package/src/plugins.ts +88 -0
- package/src/storage.ts +96 -0
- package/src/store.ts +126 -0
- package/src/types.local.ts +4 -0
- package/src/types.ts +218 -0
- package/tests/consent.spec.ts +256 -0
- package/tests/linker.spec.ts +774 -0
- package/tests/tracking.spec.ts +191 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ConsentCategory } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Where a visitor's choice is stored.
|
|
4
|
+
*
|
|
5
|
+
* Unchanged from the widget this package generalises, and it must stay unchanged: owlmeans.com has
|
|
6
|
+
* visitors who have already chosen, and a new key would ask every one of them again.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CONSENT_KEY = "site_cookie_consent";
|
|
9
|
+
export declare const CONSENT_COOKIE_DAYS = 365;
|
|
10
|
+
/**
|
|
11
|
+
* The stored record's shape version.
|
|
12
|
+
*
|
|
13
|
+
* A record with no version predates the explicit `essential` category, when essential storage was
|
|
14
|
+
* implicitly always on. Those records are migrated in place rather than discarded — the visitor
|
|
15
|
+
* made a decision, and re-asking would be a regression they experience as the site forgetting.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CONSENT_SCHEMA_VERSION = 2;
|
|
18
|
+
/** Idempotence flag for the Consent Mode defaults. Fixed: a page may carry two bundles. */
|
|
19
|
+
export declare const CONSENT_SETUP_FLAG = "cookieConsentSetup";
|
|
20
|
+
/**
|
|
21
|
+
* The DOM event `applyConsent` dispatches on `window` after it finishes writing globals and
|
|
22
|
+
* pushing the Consent Mode update.
|
|
23
|
+
*
|
|
24
|
+
* A loader that already ran (a gate script that decided, on first paint, not to load yet) has no
|
|
25
|
+
* other way to hear a LATER grant — Consent Mode itself speaks only on `window.dataLayer`, which a
|
|
26
|
+
* tag that has not loaded is not listening to. `event.detail.record` carries the record that was
|
|
27
|
+
* just applied.
|
|
28
|
+
*/
|
|
29
|
+
export declare const CONSENT_EVENT = "owlmeans:consent";
|
|
30
|
+
export declare const CONSENT_ESSENTIAL = "essential";
|
|
31
|
+
export declare const CONSENT_ANALYTICS = "analytics";
|
|
32
|
+
export declare const CONSENT_MARKETING = "marketing";
|
|
33
|
+
/**
|
|
34
|
+
* Where an application persists an explicit interface-language choice — `@owlmeans/client-i18n`'s
|
|
35
|
+
* `owlmeans-lng`. The interface language is strictly necessary storage (the visitor asked for it by
|
|
36
|
+
* choosing it), so it is written whatever the cookie decision says and no category governs it.
|
|
37
|
+
* Repeated here rather than imported: this package has no runtime dependencies, and the linker's
|
|
38
|
+
* language part has to run in an inline `<head>` script, before any bundle exists.
|
|
39
|
+
*/
|
|
40
|
+
export declare const CONSENT_LANGUAGE_KEY = "owlmeans-lng";
|
|
41
|
+
export declare const CONSENT_LOCALES: readonly ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de', 'fr'];
|
|
42
|
+
/**
|
|
43
|
+
* The categories every OwlMeans surface starts with.
|
|
44
|
+
*
|
|
45
|
+
* What owlmeans.com already asked (analytics, marketing) plus the essential row, which the original
|
|
46
|
+
* widget left implicit — making it explicit is what lets a flow require an acknowledgement before
|
|
47
|
+
* it sets a session cookie, and what tells a visitor what is being stored regardless: the consent
|
|
48
|
+
* record itself and the interface language the visitor picked.
|
|
49
|
+
*/
|
|
50
|
+
export declare const DEFAULT_CONSENT_CATEGORIES: ConsentCategory[];
|
|
51
|
+
/**
|
|
52
|
+
* Every signal Consent Mode v2 knows, and what it defaults to before anyone has chosen.
|
|
53
|
+
*
|
|
54
|
+
* `security_storage` is granted by Google's own documented recommendation — it covers things like
|
|
55
|
+
* fraud prevention, which are strictly necessary. Everything else starts denied.
|
|
56
|
+
*/
|
|
57
|
+
export declare const CONSENT_SIGNAL_DEFAULTS: Record<string, 'granted' | 'denied'>;
|
|
58
|
+
/** The URL parameter a decorated link carries the decision in, absent an override. */
|
|
59
|
+
export declare const CONSENT_LINK_PARAM = "owlcc";
|
|
60
|
+
/** How old a decorated link's timestamp may be and still be trusted, in seconds, absent an override. */
|
|
61
|
+
export declare const CONSENT_LINK_MAX_AGE = 300;
|
|
62
|
+
/** Clock skew allowed the OTHER way — a timestamp up to this far in the future is still trusted. */
|
|
63
|
+
export declare const CONSENT_LINK_SKEW = 60;
|
|
64
|
+
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,wBAAwB,CAAA;AAEhD,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAEtD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,qBAAqB,CAAA;AAE/C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,iBAAiB,CAAA;AAElD,eAAO,MAAM,eAAe,YAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAExF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,EAmBvD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAQxE,CAAA;AAED,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,UAAU,CAAA;AAEzC,wGAAwG;AACxG,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC,oGAAoG;AACpG,eAAO,MAAM,iBAAiB,KAAK,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a visitor's choice is stored.
|
|
3
|
+
*
|
|
4
|
+
* Unchanged from the widget this package generalises, and it must stay unchanged: owlmeans.com has
|
|
5
|
+
* visitors who have already chosen, and a new key would ask every one of them again.
|
|
6
|
+
*/
|
|
7
|
+
export const CONSENT_KEY = 'site_cookie_consent';
|
|
8
|
+
export const CONSENT_COOKIE_DAYS = 365;
|
|
9
|
+
/**
|
|
10
|
+
* The stored record's shape version.
|
|
11
|
+
*
|
|
12
|
+
* A record with no version predates the explicit `essential` category, when essential storage was
|
|
13
|
+
* implicitly always on. Those records are migrated in place rather than discarded — the visitor
|
|
14
|
+
* made a decision, and re-asking would be a regression they experience as the site forgetting.
|
|
15
|
+
*/
|
|
16
|
+
export const CONSENT_SCHEMA_VERSION = 2;
|
|
17
|
+
/** Idempotence flag for the Consent Mode defaults. Fixed: a page may carry two bundles. */
|
|
18
|
+
export const CONSENT_SETUP_FLAG = 'cookieConsentSetup';
|
|
19
|
+
/**
|
|
20
|
+
* The DOM event `applyConsent` dispatches on `window` after it finishes writing globals and
|
|
21
|
+
* pushing the Consent Mode update.
|
|
22
|
+
*
|
|
23
|
+
* A loader that already ran (a gate script that decided, on first paint, not to load yet) has no
|
|
24
|
+
* other way to hear a LATER grant — Consent Mode itself speaks only on `window.dataLayer`, which a
|
|
25
|
+
* tag that has not loaded is not listening to. `event.detail.record` carries the record that was
|
|
26
|
+
* just applied.
|
|
27
|
+
*/
|
|
28
|
+
export const CONSENT_EVENT = 'owlmeans:consent';
|
|
29
|
+
export const CONSENT_ESSENTIAL = 'essential';
|
|
30
|
+
export const CONSENT_ANALYTICS = 'analytics';
|
|
31
|
+
export const CONSENT_MARKETING = 'marketing';
|
|
32
|
+
/**
|
|
33
|
+
* Where an application persists an explicit interface-language choice — `@owlmeans/client-i18n`'s
|
|
34
|
+
* `owlmeans-lng`. The interface language is strictly necessary storage (the visitor asked for it by
|
|
35
|
+
* choosing it), so it is written whatever the cookie decision says and no category governs it.
|
|
36
|
+
* Repeated here rather than imported: this package has no runtime dependencies, and the linker's
|
|
37
|
+
* language part has to run in an inline `<head>` script, before any bundle exists.
|
|
38
|
+
*/
|
|
39
|
+
export const CONSENT_LANGUAGE_KEY = 'owlmeans-lng';
|
|
40
|
+
export const CONSENT_LOCALES = ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de', 'fr'];
|
|
41
|
+
/**
|
|
42
|
+
* The categories every OwlMeans surface starts with.
|
|
43
|
+
*
|
|
44
|
+
* What owlmeans.com already asked (analytics, marketing) plus the essential row, which the original
|
|
45
|
+
* widget left implicit — making it explicit is what lets a flow require an acknowledgement before
|
|
46
|
+
* it sets a session cookie, and what tells a visitor what is being stored regardless: the consent
|
|
47
|
+
* record itself and the interface language the visitor picked.
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_CONSENT_CATEGORIES = [
|
|
50
|
+
{
|
|
51
|
+
key: CONSENT_ESSENTIAL, required: true,
|
|
52
|
+
labelKey: 'essential', descriptionKey: 'essentialDesc',
|
|
53
|
+
globalVar: 'owlConsentEssential',
|
|
54
|
+
signals: ['security_storage', 'functionality_storage'],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: CONSENT_ANALYTICS,
|
|
58
|
+
labelKey: 'analytics', descriptionKey: 'analyticsDesc',
|
|
59
|
+
globalVar: 'owlConsentAnalytics',
|
|
60
|
+
signals: ['analytics_storage'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: CONSENT_MARKETING,
|
|
64
|
+
labelKey: 'marketing', descriptionKey: 'marketingDesc',
|
|
65
|
+
globalVar: 'owlConsentMarketing',
|
|
66
|
+
signals: ['ad_storage', 'ad_user_data', 'ad_personalization'],
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
/**
|
|
70
|
+
* Every signal Consent Mode v2 knows, and what it defaults to before anyone has chosen.
|
|
71
|
+
*
|
|
72
|
+
* `security_storage` is granted by Google's own documented recommendation — it covers things like
|
|
73
|
+
* fraud prevention, which are strictly necessary. Everything else starts denied.
|
|
74
|
+
*/
|
|
75
|
+
export const CONSENT_SIGNAL_DEFAULTS = {
|
|
76
|
+
ad_storage: 'denied',
|
|
77
|
+
ad_user_data: 'denied',
|
|
78
|
+
ad_personalization: 'denied',
|
|
79
|
+
analytics_storage: 'denied',
|
|
80
|
+
functionality_storage: 'denied',
|
|
81
|
+
personalization_storage: 'denied',
|
|
82
|
+
security_storage: 'granted',
|
|
83
|
+
};
|
|
84
|
+
/** The URL parameter a decorated link carries the decision in, absent an override. */
|
|
85
|
+
export const CONSENT_LINK_PARAM = 'owlcc';
|
|
86
|
+
/** How old a decorated link's timestamp may be and still be trusted, in seconds, absent an override. */
|
|
87
|
+
export const CONSENT_LINK_MAX_AGE = 300;
|
|
88
|
+
/** Clock skew allowed the OTHER way — a timestamp up to this far in the future is still trusted. */
|
|
89
|
+
export const CONSENT_LINK_SKEW = 60;
|
|
90
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,qBAAqB,CAAA;AAEhD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEvC,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAA;AAEtD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAA;AAE/C,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAA;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAA;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAA;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,cAAc,CAAA;AAElD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAExF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D;QACE,GAAG,EAAE,iBAAiB,EAAE,QAAQ,EAAE,IAAI;QACtC,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe;QACtD,SAAS,EAAE,qBAAqB;QAChC,OAAO,EAAE,CAAC,kBAAkB,EAAE,uBAAuB,CAAC;KACvD;IACD;QACE,GAAG,EAAE,iBAAiB;QACtB,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe;QACtD,SAAS,EAAE,qBAAqB;QAChC,OAAO,EAAE,CAAC,mBAAmB,CAAC;KAC/B;IACD;QACE,GAAG,EAAE,iBAAiB;QACtB,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe;QACtD,SAAS,EAAE,qBAAqB;QAChC,OAAO,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,oBAAoB,CAAC;KAC9D;CACF,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAyC;IAC3E,UAAU,EAAE,QAAQ;IACpB,YAAY,EAAE,QAAQ;IACtB,kBAAkB,EAAE,QAAQ;IAC5B,iBAAiB,EAAE,QAAQ;IAC3B,qBAAqB,EAAE,QAAQ;IAC/B,uBAAuB,EAAE,QAAQ;IACjC,gBAAgB,EAAE,SAAS;CAC5B,CAAA;AAED,sFAAsF;AACtF,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAA;AAEzC,wGAAwG;AACxG,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAA;AAEvC,oGAAoG;AACpG,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,wGAAwG;AACxG,eAAO,MAAM,YAAY,QAA4C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAErC,wGAAwG;AACxG,MAAM,CAAC,MAAM,YAAY,GAAG,yCAAyC,CAAA"}
|
package/build/gtm.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ConsentCategory, ConsentOptions, ConsentRecord } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Push onto the tag-manager queue in the shape Google's own snippet uses.
|
|
4
|
+
*
|
|
5
|
+
* `arguments`, not an array literal. Both work with today's Consent API, but `arguments` is what
|
|
6
|
+
* `gtag.js` itself emits, and a page that carries both this and Google's snippet should not have
|
|
7
|
+
* two shapes in one queue.
|
|
8
|
+
*/
|
|
9
|
+
export declare function gtagConsent(..._args: unknown[]): void;
|
|
10
|
+
/** Every signal the configured categories drive, at its pre-consent value. */
|
|
11
|
+
export declare const consentDefaults: (categories?: ConsentCategory[]) => Record<string, 'granted' | 'denied'>;
|
|
12
|
+
/** Every signal, at the value this record implies. */
|
|
13
|
+
export declare const consentUpdate: (record: ConsentRecord, categories?: ConsentCategory[]) => Record<string, 'granted' | 'denied'>;
|
|
14
|
+
/**
|
|
15
|
+
* Declare what is denied, before any tag can act.
|
|
16
|
+
*
|
|
17
|
+
* Idempotent through a window flag, because the page may carry this call twice — once inline in
|
|
18
|
+
* the document head, once from whatever bundle mounts the dialog — and a second `default` after a
|
|
19
|
+
* tag has loaded is worse than none: it can widen what was already narrowed.
|
|
20
|
+
*/
|
|
21
|
+
export declare const pushConsentDefaults: (opts?: ConsentOptions) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Apply a decision: globals first, then the signal update, then any per-category events.
|
|
24
|
+
*
|
|
25
|
+
* The order is load-bearing. A tag that fires on the update event reads the globals in the same
|
|
26
|
+
* turn, so writing them afterwards would let the first firing see the previous answer.
|
|
27
|
+
*/
|
|
28
|
+
export declare const applyConsent: (record: ConsentRecord, opts?: ConsentOptions) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Whether a stored decision grants tracking at all — a signal-bearing category, not merely the
|
|
31
|
+
* required essential one.
|
|
32
|
+
*
|
|
33
|
+
* "Signal-bearing" is what a required category can never be: it exists to gate whether a LOADER
|
|
34
|
+
* runs, and a required category is disclosure, not a question — everyone gets it. Only a category
|
|
35
|
+
* that both is optional and drives at least one Consent Mode signal counts, which is exactly what
|
|
36
|
+
* `googleTagHeadScript`'s `'basic'` mode gates a Google tag behind.
|
|
37
|
+
*/
|
|
38
|
+
export declare const trackingGranted: (record: ConsentRecord | null, categories?: ConsentCategory[]) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The inline script a document stamps ABOVE its tag-manager snippet.
|
|
41
|
+
*
|
|
42
|
+
* It exists because the defaults have to be on the queue before `gtm.js` loads, and a React bundle
|
|
43
|
+
* cannot be: by the time an island mounts, the container has been running for hundreds of
|
|
44
|
+
* milliseconds and has already decided what it may do. Emitting it from the HTML is the only
|
|
45
|
+
* ordering that holds — and it reads the stored record too, so a returning visitor's tags are not
|
|
46
|
+
* denied for the first paint of every page.
|
|
47
|
+
*/
|
|
48
|
+
export declare const consentBootstrapScript: (opts?: ConsentOptions) => string;
|
|
49
|
+
/**
|
|
50
|
+
* Withhold a loader until a stored or later decision grants tracking.
|
|
51
|
+
*
|
|
52
|
+
* The counterpart to `consentBootstrapScript` for the `'basic'` gated loading mode
|
|
53
|
+
* (`@owlmeans/web-gtm`'s `GOOGLE_TAG_DEFAULT_MODE`): where the bootstrap always declares Consent
|
|
54
|
+
* Mode's defaults so a tag may load cookieless, this withholds the tag itself — the loader string
|
|
55
|
+
* is only ever reached once `trackingGranted` is true, either because a returning visitor's stored
|
|
56
|
+
* record already says so, or because `applyConsent` later dispatches {@link CONSENT_EVENT} with
|
|
57
|
+
* one that does. The listener removes itself on the first passing event, so the loader — which
|
|
58
|
+
* marks its own element and refuses to run twice — is asked to run at most once from here.
|
|
59
|
+
*
|
|
60
|
+
* `loaderExpr` is a complete statement or expression that runs the loader when reached — the
|
|
61
|
+
* self-invoking IIFE string `googleTagHeadScript`'s `gtagScript`/`gtmContainerScript` already
|
|
62
|
+
* produce, embedded here verbatim rather than called. It is placed inline exactly as
|
|
63
|
+
* `consentBootstrapScript`'s own output is: dynamic values (the storage key, the category shape,
|
|
64
|
+
* the event name) are JSON-encoded so nothing configured can break out of a string literal, and
|
|
65
|
+
* escaping the RESULT for HTML (`</script`, `<!--`) is left to whoever composes the final inline
|
|
66
|
+
* script — the same discipline `consentBootstrapScript` follows, and `googleTagHeadScript` already
|
|
67
|
+
* applies to the whole script it stamps.
|
|
68
|
+
*/
|
|
69
|
+
export declare const consentGateScript: (loaderExpr: string, opts?: ConsentOptions & {
|
|
70
|
+
categories?: ConsentCategory[];
|
|
71
|
+
}) => string;
|
|
72
|
+
//# sourceMappingURL=gtm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gtm.d.ts","sourceRoot":"","sources":["../src/gtm.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAMhF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAUrD;AAKD,8EAA8E;AAC9E,eAAO,MAAM,eAAe,gBACb,eAAe,EAAE,KAC7B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAMrC,CAAA;AAED,sDAAsD;AACtD,eAAO,MAAM,aAAa,WAChB,aAAa,eAAe,eAAe,EAAE,KACpD,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAWrC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,UAAW,cAAc,KAAG,IAO3D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,WAAY,aAAa,SAAS,cAAc,KAAG,IA6B3E,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,WAClB,aAAa,GAAG,IAAI,eAAc,eAAe,EAAE,KAC1D,OAEF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,UAAW,cAAc,KAAG,MA+B9D,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB,eAChB,MAAM,SAAS,cAAc,GAAG;IAAE,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,KAC7E,MAuBF,CAAA"}
|
package/build/gtm.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { CONSENT_EVENT, CONSENT_KEY, CONSENT_SETUP_FLAG, CONSENT_SIGNAL_DEFAULTS, DEFAULT_CONSENT_CATEGORIES, } from './consts.js';
|
|
2
|
+
import { consentLinkerScript } from './linker.js';
|
|
3
|
+
const global = () => typeof window !== 'undefined' ? window : null;
|
|
4
|
+
/**
|
|
5
|
+
* Push onto the tag-manager queue in the shape Google's own snippet uses.
|
|
6
|
+
*
|
|
7
|
+
* `arguments`, not an array literal. Both work with today's Consent API, but `arguments` is what
|
|
8
|
+
* `gtag.js` itself emits, and a page that carries both this and Google's snippet should not have
|
|
9
|
+
* two shapes in one queue.
|
|
10
|
+
*/
|
|
11
|
+
export function gtagConsent(..._args) {
|
|
12
|
+
const win = global();
|
|
13
|
+
if (win == null) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
win.dataLayer = win.dataLayer ?? [];
|
|
17
|
+
// `arguments`, not `_args`. The rest parameter is only here to type the call sites; what goes on
|
|
18
|
+
// the queue must be the arguments object itself.
|
|
19
|
+
// eslint-disable-next-line prefer-rest-params
|
|
20
|
+
win.dataLayer.push(arguments);
|
|
21
|
+
}
|
|
22
|
+
const categoriesOf = (opts) => opts?.categories ?? DEFAULT_CONSENT_CATEGORIES;
|
|
23
|
+
/** Every signal the configured categories drive, at its pre-consent value. */
|
|
24
|
+
export const consentDefaults = (categories) => {
|
|
25
|
+
const used = new Set((categories ?? DEFAULT_CONSENT_CATEGORIES)
|
|
26
|
+
.flatMap(category => category.signals ?? []));
|
|
27
|
+
return Object.fromEntries(Object.entries(CONSENT_SIGNAL_DEFAULTS)
|
|
28
|
+
.filter(([signal]) => used.has(signal)));
|
|
29
|
+
};
|
|
30
|
+
/** Every signal, at the value this record implies. */
|
|
31
|
+
export const consentUpdate = (record, categories) => {
|
|
32
|
+
const update = {};
|
|
33
|
+
for (const category of categories ?? DEFAULT_CONSENT_CATEGORIES) {
|
|
34
|
+
const granted = category.required === true || record[category.key] === true;
|
|
35
|
+
for (const signal of category.signals ?? []) {
|
|
36
|
+
// A signal named by two categories is granted only when every one of them is.
|
|
37
|
+
update[signal] = granted && update[signal] !== 'denied' ? 'granted' : 'denied';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return update;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Declare what is denied, before any tag can act.
|
|
44
|
+
*
|
|
45
|
+
* Idempotent through a window flag, because the page may carry this call twice — once inline in
|
|
46
|
+
* the document head, once from whatever bundle mounts the dialog — and a second `default` after a
|
|
47
|
+
* tag has loaded is worse than none: it can widen what was already narrowed.
|
|
48
|
+
*/
|
|
49
|
+
export const pushConsentDefaults = (opts) => {
|
|
50
|
+
const win = global();
|
|
51
|
+
if (win == null || opts?.silent === true || win[CONSENT_SETUP_FLAG] === true) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
gtagConsent('consent', 'default', consentDefaults(categoriesOf(opts)));
|
|
55
|
+
win[CONSENT_SETUP_FLAG] = true;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Apply a decision: globals first, then the signal update, then any per-category events.
|
|
59
|
+
*
|
|
60
|
+
* The order is load-bearing. A tag that fires on the update event reads the globals in the same
|
|
61
|
+
* turn, so writing them afterwards would let the first firing see the previous answer.
|
|
62
|
+
*/
|
|
63
|
+
export const applyConsent = (record, opts) => {
|
|
64
|
+
const win = global();
|
|
65
|
+
if (win == null || opts?.silent === true) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const categories = categoriesOf(opts);
|
|
69
|
+
for (const category of categories) {
|
|
70
|
+
if (category.globalVar != null) {
|
|
71
|
+
win[category.globalVar] = category.required === true || record[category.key] === true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
gtagConsent('consent', 'update', consentUpdate(record, categories));
|
|
75
|
+
for (const category of categories) {
|
|
76
|
+
if (category.event != null && (category.required === true || record[category.key] === true)) {
|
|
77
|
+
win.dataLayer = win.dataLayer ?? [];
|
|
78
|
+
win.dataLayer.push({ event: category.event });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// A gate script (`consentGateScript`) that already decided NOT to load, because nothing was
|
|
82
|
+
// granted at the time it ran, has no other way to hear this. Consent Mode itself speaks only on
|
|
83
|
+
// `dataLayer`, which a tag that never loaded is not listening to.
|
|
84
|
+
if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function'
|
|
85
|
+
&& typeof CustomEvent === 'function') {
|
|
86
|
+
window.dispatchEvent(new CustomEvent(CONSENT_EVENT, { detail: { record } }));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Whether a stored decision grants tracking at all — a signal-bearing category, not merely the
|
|
91
|
+
* required essential one.
|
|
92
|
+
*
|
|
93
|
+
* "Signal-bearing" is what a required category can never be: it exists to gate whether a LOADER
|
|
94
|
+
* runs, and a required category is disclosure, not a question — everyone gets it. Only a category
|
|
95
|
+
* that both is optional and drives at least one Consent Mode signal counts, which is exactly what
|
|
96
|
+
* `googleTagHeadScript`'s `'basic'` mode gates a Google tag behind.
|
|
97
|
+
*/
|
|
98
|
+
export const trackingGranted = (record, categories = DEFAULT_CONSENT_CATEGORIES) => record != null && categories.some(category => !category.required && (category.signals?.length ?? 0) > 0 && record[category.key] === true);
|
|
99
|
+
/**
|
|
100
|
+
* The inline script a document stamps ABOVE its tag-manager snippet.
|
|
101
|
+
*
|
|
102
|
+
* It exists because the defaults have to be on the queue before `gtm.js` loads, and a React bundle
|
|
103
|
+
* cannot be: by the time an island mounts, the container has been running for hundreds of
|
|
104
|
+
* milliseconds and has already decided what it may do. Emitting it from the HTML is the only
|
|
105
|
+
* ordering that holds — and it reads the stored record too, so a returning visitor's tags are not
|
|
106
|
+
* denied for the first paint of every page.
|
|
107
|
+
*/
|
|
108
|
+
export const consentBootstrapScript = (opts) => {
|
|
109
|
+
const defaults = JSON.stringify(consentDefaults(categoriesOf(opts)));
|
|
110
|
+
const categories = JSON.stringify(categoriesOf(opts).map(category => ({
|
|
111
|
+
key: category.key,
|
|
112
|
+
required: category.required === true,
|
|
113
|
+
signals: category.signals ?? [],
|
|
114
|
+
globalVar: category.globalVar ?? null,
|
|
115
|
+
})));
|
|
116
|
+
const storageKey = JSON.stringify(opts?.storageKey ?? CONSENT_KEY);
|
|
117
|
+
const flag = JSON.stringify(CONSENT_SETUP_FLAG);
|
|
118
|
+
// Right after the default is pushed and before EITHER storage read below (this one, and the
|
|
119
|
+
// `consentGateScript` that is concatenated after this whole IIFE) — an adopted decision must
|
|
120
|
+
// already be in storage by the time anything reads it, so `gtm.js`'s own `page_view` never sees
|
|
121
|
+
// the parameter and a granted decision loads the tag on arrival. A page with no `opts.linker`
|
|
122
|
+
// gets an empty string here, unchanged from before this existed.
|
|
123
|
+
const linker = opts?.linker != null ? `${consentLinkerScript(opts)};` : '';
|
|
124
|
+
return `(function(w,d){` +
|
|
125
|
+
`w.dataLayer=w.dataLayer||[];function g(){w.dataLayer.push(arguments)}` +
|
|
126
|
+
`if(w[${flag}])return;g('consent','default',${defaults});w[${flag}]=true;` +
|
|
127
|
+
linker +
|
|
128
|
+
`var raw=null;try{raw=w.localStorage.getItem(${storageKey})}catch(e){}` +
|
|
129
|
+
`if(!raw){var p=('; '+d.cookie).split('; '+${storageKey}+'=');` +
|
|
130
|
+
`if(p.length===2){raw=p.pop().split(';').shift()}}` +
|
|
131
|
+
`if(!raw)return;var r;try{r=JSON.parse(raw)}catch(e){return}` +
|
|
132
|
+
`var cs=${categories},u={};` +
|
|
133
|
+
`for(var i=0;i<cs.length;i++){var c=cs[i];var ok=c.required||r[c.key]===true;` +
|
|
134
|
+
`if(c.globalVar){w[c.globalVar]=ok}` +
|
|
135
|
+
`for(var j=0;j<c.signals.length;j++){var s=c.signals[j];` +
|
|
136
|
+
`u[s]=ok&&u[s]!=='denied'?'granted':'denied'}}` +
|
|
137
|
+
`g('consent','update',u)})(window,document)`;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Withhold a loader until a stored or later decision grants tracking.
|
|
141
|
+
*
|
|
142
|
+
* The counterpart to `consentBootstrapScript` for the `'basic'` gated loading mode
|
|
143
|
+
* (`@owlmeans/web-gtm`'s `GOOGLE_TAG_DEFAULT_MODE`): where the bootstrap always declares Consent
|
|
144
|
+
* Mode's defaults so a tag may load cookieless, this withholds the tag itself — the loader string
|
|
145
|
+
* is only ever reached once `trackingGranted` is true, either because a returning visitor's stored
|
|
146
|
+
* record already says so, or because `applyConsent` later dispatches {@link CONSENT_EVENT} with
|
|
147
|
+
* one that does. The listener removes itself on the first passing event, so the loader — which
|
|
148
|
+
* marks its own element and refuses to run twice — is asked to run at most once from here.
|
|
149
|
+
*
|
|
150
|
+
* `loaderExpr` is a complete statement or expression that runs the loader when reached — the
|
|
151
|
+
* self-invoking IIFE string `googleTagHeadScript`'s `gtagScript`/`gtmContainerScript` already
|
|
152
|
+
* produce, embedded here verbatim rather than called. It is placed inline exactly as
|
|
153
|
+
* `consentBootstrapScript`'s own output is: dynamic values (the storage key, the category shape,
|
|
154
|
+
* the event name) are JSON-encoded so nothing configured can break out of a string literal, and
|
|
155
|
+
* escaping the RESULT for HTML (`</script`, `<!--`) is left to whoever composes the final inline
|
|
156
|
+
* script — the same discipline `consentBootstrapScript` follows, and `googleTagHeadScript` already
|
|
157
|
+
* applies to the whole script it stamps.
|
|
158
|
+
*/
|
|
159
|
+
export const consentGateScript = (loaderExpr, opts) => {
|
|
160
|
+
const gate = JSON.stringify(categoriesOf(opts).map(category => ({
|
|
161
|
+
key: category.key,
|
|
162
|
+
required: category.required === true,
|
|
163
|
+
hasSignal: (category.signals ?? []).length > 0,
|
|
164
|
+
})));
|
|
165
|
+
const storageKey = JSON.stringify(opts?.storageKey ?? CONSENT_KEY);
|
|
166
|
+
const event = JSON.stringify(CONSENT_EVENT);
|
|
167
|
+
return `(function(w,d){` +
|
|
168
|
+
`function ok(r){if(!r)return false;var cs=${gate};` +
|
|
169
|
+
`for(var i=0;i<cs.length;i++){var c=cs[i];` +
|
|
170
|
+
`if(!c.required&&c.hasSignal&&r[c.key]===true)return true}return false}` +
|
|
171
|
+
`function load(){${loaderExpr}}` +
|
|
172
|
+
`var raw=null;try{raw=w.localStorage.getItem(${storageKey})}catch(e){}` +
|
|
173
|
+
`if(!raw){var p=('; '+d.cookie).split('; '+${storageKey}+'=');` +
|
|
174
|
+
`if(p.length===2){raw=p.pop().split(';').shift()}}` +
|
|
175
|
+
`var r=null;if(raw){try{r=JSON.parse(raw)}catch(e){r=null}}` +
|
|
176
|
+
`if(ok(r)){load();return}` +
|
|
177
|
+
`function h(ev){if(ok(ev&&ev.detail&&ev.detail.record)){` +
|
|
178
|
+
`w.removeEventListener(${event},h);load()}}` +
|
|
179
|
+
`w.addEventListener(${event},h)` +
|
|
180
|
+
`})(window,document)`;
|
|
181
|
+
};
|
|
182
|
+
//# sourceMappingURL=gtm.js.map
|
package/build/gtm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gtm.js","sourceRoot":"","sources":["../src/gtm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Налады файлаў cookie",
|
|
3
|
+
"description": "Мы выкарыстоўваем файлы cookie, каб палепшыць ваш досвед прагляду, паказваць персаналізаваную рэкламу ці змесціва і аналізаваць наш трафік. Націскаючы «Прыняць усе», вы пагаджаецеся на выкарыстанне файлаў cookie.",
|
|
4
|
+
"essential": "Неабходныя файлы cookie",
|
|
5
|
+
"essentialDesc": "Патрэбныя для працы сайта — уваход, бяспека і захаванне вашага выбару адносна файлаў cookie і мовы інтэрфейсу. Іх нельга адключыць.",
|
|
6
|
+
"analytics": "Аналітычныя файлы cookie",
|
|
7
|
+
"analyticsDesc": "Дапамагаюць нам паляпшаць наш вэб-сайт, збіраючы і паведамляючы інфармацыю пра яго выкарыстанне.",
|
|
8
|
+
"marketing": "Маркетынгавыя файлы cookie",
|
|
9
|
+
"marketingDesc": "Выкарыстоўваюцца для адсочвання наведвальнікаў на розных вэб-сайтах дзеля паказу актуальнай і цікавай рэкламы.",
|
|
10
|
+
"required": "Абавязкова",
|
|
11
|
+
"acceptAll": "Прыняць усе",
|
|
12
|
+
"acceptAndContinue": "Прыняць і працягнуць",
|
|
13
|
+
"savePreferences": "Захаваць налады",
|
|
14
|
+
"policyLink": "Палітыка файлаў cookie",
|
|
15
|
+
"openPreferences": "Налады файлаў cookie",
|
|
16
|
+
"close": "Закрыць",
|
|
17
|
+
"loginReason": "Уваход захоўвае сеансавы файл cookie. Прыміце неабходныя файлы cookie, каб працягнуць.",
|
|
18
|
+
"manage": "Змяніць налады",
|
|
19
|
+
"policyTitle": "Палітыка файлаў cookie",
|
|
20
|
+
"policyIntro": "Гэтая старонка апісвае файлы cookie і падобныя сховішчы, якія выкарыстоўвае гэтая праграма, і як імі кіраваць.",
|
|
21
|
+
"policyStorage": "Ваш выбар захоўваецца ў гэтым браўзеры пад ключом «{{key}}» — і ў лакальным сховішчы, і ў файле cookie.",
|
|
22
|
+
"policyRetention": "Запіс захоўваецца {{days}} дзён, пасля чаго мы спытаем зноў.",
|
|
23
|
+
"policyOperator": "Гэтая праграма належыць {{operator}}.",
|
|
24
|
+
"policyContact": "Азнаёмцеся з іх палітыкай прыватнасці і ўмовамі карыстання, каб даведацца, як яны апрацоўваюць вашы даныя.",
|
|
25
|
+
"policyServicesOf": "Сэрвісы — {{category}}",
|
|
26
|
+
"policyOtherServices": "Іншыя сэрвісы",
|
|
27
|
+
"policyOtherServicesDesc": "Сэрвісы, якія гэтая праграма выкарыстоўвае па-за пералічанымі вышэй катэгорыямі.",
|
|
28
|
+
"policyProvider": "Пастаўшчык",
|
|
29
|
+
"policyPurpose": "Мэта",
|
|
30
|
+
"policyCookies": "Файлы cookie",
|
|
31
|
+
"policyServicePrivacy": "Палітыка прыватнасці: {{provider}}",
|
|
32
|
+
"privacy": "Палітыка прыватнасці",
|
|
33
|
+
"terms": "Умовы карыстання",
|
|
34
|
+
"domains": "Гэты выбар распаўсюджваецца на {{domains}}.",
|
|
35
|
+
"policyDomains": "Гэты выбар распаўсюджваецца на наступныя дамены:"
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Cookie-Einstellungen",
|
|
3
|
+
"description": "Wir verwenden Cookies, um Ihr Surferlebnis zu verbessern, personalisierte Anzeigen oder Inhalte bereitzustellen und unseren Datenverkehr zu analysieren. Indem Sie auf „Alle akzeptieren\" klicken, stimmen Sie der Verwendung von Cookies zu.",
|
|
4
|
+
"essential": "Notwendige Cookies",
|
|
5
|
+
"essentialDesc": "Erforderlich, damit die Website funktioniert — Anmeldung, Sicherheit und das Speichern Ihrer Cookie-Auswahl und der Oberflächensprache. Sie lassen sich nicht abschalten.",
|
|
6
|
+
"analytics": "Analyse-Cookies",
|
|
7
|
+
"analyticsDesc": "Helfen uns, unsere Website zu verbessern, indem sie Informationen zur Nutzung sammeln und melden.",
|
|
8
|
+
"marketing": "Marketing-Cookies",
|
|
9
|
+
"marketingDesc": "Werden verwendet, um Besucher über Websites hinweg zu verfolgen und relevante, ansprechende Werbung anzuzeigen.",
|
|
10
|
+
"required": "Erforderlich",
|
|
11
|
+
"acceptAll": "Alle akzeptieren",
|
|
12
|
+
"acceptAndContinue": "Akzeptieren und fortfahren",
|
|
13
|
+
"savePreferences": "Einstellungen speichern",
|
|
14
|
+
"policyLink": "Cookie-Richtlinie",
|
|
15
|
+
"openPreferences": "Cookie-Einstellungen",
|
|
16
|
+
"close": "Schließen",
|
|
17
|
+
"loginReason": "Bei der Anmeldung wird ein Sitzungs-Cookie gespeichert. Akzeptieren Sie die notwendigen Cookies, um fortzufahren.",
|
|
18
|
+
"manage": "Einstellungen verwalten",
|
|
19
|
+
"policyTitle": "Cookie-Richtlinie",
|
|
20
|
+
"policyIntro": "Diese Seite beschreibt die Cookies und ähnliche Speicher, die diese Anwendung verwendet, und wie Sie sie steuern können.",
|
|
21
|
+
"policyStorage": "Ihre Auswahl wird in diesem Browser unter „{{key}}\" gespeichert — sowohl im lokalen Speicher als auch als Cookie.",
|
|
22
|
+
"policyRetention": "Der Eintrag wird {{days}} Tage aufbewahrt, danach werden Sie erneut gefragt.",
|
|
23
|
+
"policyOperator": "Diese Anwendung wird von {{operator}} betrieben.",
|
|
24
|
+
"policyContact": "Sehen Sie in deren Datenschutzerklärung und Bedingungen nach, wie Ihre Daten verarbeitet werden.",
|
|
25
|
+
"policyServicesOf": "Dienste — {{category}}",
|
|
26
|
+
"policyOtherServices": "Weitere Dienste",
|
|
27
|
+
"policyOtherServicesDesc": "Dienste, die diese Anwendung außerhalb der oben genannten Kategorien nutzt.",
|
|
28
|
+
"policyProvider": "Anbieter",
|
|
29
|
+
"policyPurpose": "Zweck",
|
|
30
|
+
"policyCookies": "Cookies",
|
|
31
|
+
"policyServicePrivacy": "Datenschutzerklärung von {{provider}}",
|
|
32
|
+
"privacy": "Datenschutzerklärung",
|
|
33
|
+
"terms": "Nutzungsbedingungen",
|
|
34
|
+
"domains": "Diese Wahl gilt für {{domains}}.",
|
|
35
|
+
"policyDomains": "Diese Wahl gilt für die folgenden Domains:"
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Cookie Preferences",
|
|
3
|
+
"description": "We use cookies to enhance your browsing experience, serve personalized ads or content, and analyze our traffic. By clicking \"Accept All\", you consent to our use of cookies.",
|
|
4
|
+
"essential": "Essential Cookies",
|
|
5
|
+
"essentialDesc": "Required for the site to work — signing in, security, and remembering your cookie choices and interface language. They cannot be switched off.",
|
|
6
|
+
"analytics": "Analytics Cookies",
|
|
7
|
+
"analyticsDesc": "Help us improve our website by collecting and reporting information on its usage.",
|
|
8
|
+
"marketing": "Marketing Cookies",
|
|
9
|
+
"marketingDesc": "Used to track visitors across websites to display relevant and engaging advertisements.",
|
|
10
|
+
"required": "Required",
|
|
11
|
+
"acceptAll": "Accept All",
|
|
12
|
+
"acceptAndContinue": "Accept & continue",
|
|
13
|
+
"savePreferences": "Save Preferences",
|
|
14
|
+
"policyLink": "Cookie Policy",
|
|
15
|
+
"openPreferences": "Cookie preferences",
|
|
16
|
+
"close": "Close",
|
|
17
|
+
"loginReason": "Signing in stores a session cookie. Accept essential cookies to continue.",
|
|
18
|
+
"manage": "Manage preferences",
|
|
19
|
+
"policyTitle": "Cookie Policy",
|
|
20
|
+
"policyIntro": "This page describes the cookies and similar storage this application uses, and how you can control them.",
|
|
21
|
+
"policyStorage": "Your choice is stored in this browser under \"{{key}}\", both in local storage and as a cookie.",
|
|
22
|
+
"policyRetention": "The record is kept for {{days}} days, after which you will be asked again.",
|
|
23
|
+
"policyOperator": "This application is operated by {{operator}}.",
|
|
24
|
+
"policyContact": "See their privacy policy and terms for how they handle your data.",
|
|
25
|
+
"policyServicesOf": "Services — {{category}}",
|
|
26
|
+
"policyOtherServices": "Other services",
|
|
27
|
+
"policyOtherServicesDesc": "Services this application runs outside the categories above.",
|
|
28
|
+
"policyProvider": "Provider",
|
|
29
|
+
"policyPurpose": "Purpose",
|
|
30
|
+
"policyCookies": "Cookies",
|
|
31
|
+
"policyServicePrivacy": "{{provider}} privacy policy",
|
|
32
|
+
"privacy": "Privacy Policy",
|
|
33
|
+
"terms": "Terms & Conditions",
|
|
34
|
+
"domains": "This choice applies to {{domains}}.",
|
|
35
|
+
"policyDomains": "This choice applies across these domains:"
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Preferencias de cookies",
|
|
3
|
+
"description": "Utilizamos cookies para mejorar tu experiencia de navegación, ofrecer anuncios o contenido personalizados y analizar nuestro tráfico. Al hacer clic en «Aceptar todo», aceptas nuestro uso de cookies.",
|
|
4
|
+
"essential": "Cookies esenciales",
|
|
5
|
+
"essentialDesc": "Necesarias para que el sitio funcione: inicio de sesión, seguridad y recordar tus decisiones sobre cookies y el idioma de la interfaz. No se pueden desactivar.",
|
|
6
|
+
"analytics": "Cookies analíticas",
|
|
7
|
+
"analyticsDesc": "Nos ayudan a mejorar nuestro sitio web recopilando e informando sobre su uso.",
|
|
8
|
+
"marketing": "Cookies de marketing",
|
|
9
|
+
"marketingDesc": "Se utilizan para rastrear a los visitantes en distintos sitios web y mostrar anuncios relevantes y atractivos.",
|
|
10
|
+
"required": "Obligatorias",
|
|
11
|
+
"acceptAll": "Aceptar todo",
|
|
12
|
+
"acceptAndContinue": "Aceptar y continuar",
|
|
13
|
+
"savePreferences": "Guardar preferencias",
|
|
14
|
+
"policyLink": "Política de cookies",
|
|
15
|
+
"openPreferences": "Preferencias de cookies",
|
|
16
|
+
"close": "Cerrar",
|
|
17
|
+
"loginReason": "Iniciar sesión guarda una cookie de sesión. Acepta las cookies esenciales para continuar.",
|
|
18
|
+
"manage": "Gestionar preferencias",
|
|
19
|
+
"policyTitle": "Política de cookies",
|
|
20
|
+
"policyIntro": "Esta página describe las cookies y el almacenamiento similar que utiliza esta aplicación, y cómo puedes controlarlos.",
|
|
21
|
+
"policyStorage": "Tu elección se guarda en este navegador bajo «{{key}}», tanto en el almacenamiento local como en una cookie.",
|
|
22
|
+
"policyRetention": "El registro se conserva {{days}} días, tras los cuales se te preguntará de nuevo.",
|
|
23
|
+
"policyOperator": "Esta aplicación está gestionada por {{operator}}.",
|
|
24
|
+
"policyContact": "Consulta su política de privacidad y sus términos para saber cómo tratan tus datos.",
|
|
25
|
+
"policyServicesOf": "Servicios — {{category}}",
|
|
26
|
+
"policyOtherServices": "Otros servicios",
|
|
27
|
+
"policyOtherServicesDesc": "Servicios que esta aplicación utiliza fuera de las categorías anteriores.",
|
|
28
|
+
"policyProvider": "Proveedor",
|
|
29
|
+
"policyPurpose": "Finalidad",
|
|
30
|
+
"policyCookies": "Cookies",
|
|
31
|
+
"policyServicePrivacy": "Política de privacidad de {{provider}}",
|
|
32
|
+
"privacy": "Política de privacidad",
|
|
33
|
+
"terms": "Términos y condiciones",
|
|
34
|
+
"domains": "Esta elección se aplica a {{domains}}.",
|
|
35
|
+
"policyDomains": "Esta elección se aplica a los siguientes dominios:"
|
|
36
|
+
}
|