@analitikisler/ai-tag-debugger 0.16.0 → 0.18.0

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/src/i18n.js CHANGED
@@ -1,6 +1,26 @@
1
1
  // Report language strings. Add a language by copying the "en" block.
2
+ //
3
+ // Each language has the same shape: severity labels, ui chrome, detail (the symptom sentence each
4
+ // check emits), gtm (why a tag did or did not fire), and the meaning layer: consequence and fix
5
+ // per check name, the check's category (money, legal, hygiene) and the verdict strings.
6
+ // Event, parameter, tag and trigger names are never translated or re-cased; quoted names are
7
+ // rendered as code by the report.
8
+
9
+ import { PROVIDERS } from "./parsers.js";
2
10
 
3
11
  const q = (s) => `"${s}"`;
12
+ /** The platform's label (GA4, Meta) for the key the checks pass (ga4, meta); anything else passes through. */
13
+ const label = (p) => PROVIDERS[p] ?? String(p);
14
+ // Parameters in one fixed order, so the same bug reads the same on every card: value, currency,
15
+ // transaction_id, items, then alphabetical. The plan's order would make two cards look like two problems.
16
+ const PARAM_ORDER = ["value", "currency", "transaction_id", "items"];
17
+ const rank = (p) => (PARAM_ORDER.includes(p) ? PARAM_ORDER.indexOf(p) : PARAM_ORDER.length);
18
+ const sortParams = (params) => [...params].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
19
+ /** "a, b and c" with the language's "and". */
20
+ const andList = (items, and) => (items.length <= 1 ? items.join("") : `${items.slice(0, -1).join(", ")} ${and} ${items[items.length - 1]}`);
21
+ /** The counts of a verdict: the object verdict() in metrics.js returns, or a bare counts object. */
22
+ const countsOf = (v) => v?.counts ?? v ?? {};
23
+ const journeysOf = (v) => v?.journeys ?? {};
4
24
 
5
25
  const en = {
6
26
  name: "English",
@@ -12,18 +32,11 @@ const en = {
12
32
  eyebrow: "AI tracking audit",
13
33
  paramsShort: "params",
14
34
  noParameters: "No parameters.",
15
- pushMatchHint: "dataLayer pushes in this step with the same event name.",
16
35
  noMatchingPush: "No dataLayer push with this event name in this step. The tag may fire from a page view, a click trigger or a hardcoded pixel.",
17
- otherInStep: "Other dataLayer pushes and network calls",
18
36
  noHits: "No tracking events",
19
- hasProblems: "Has problems",
20
- stepsShort: "steps",
21
- eventsShort: "events",
22
- problemsShort: "problems",
23
37
  findings: "Findings",
24
38
  what: "What",
25
39
  where: "Where",
26
- causeAndFix: "Cause and fix",
27
40
  likelyCause: "Likely cause",
28
41
  gtmTags: "In your GTM container",
29
42
  gtmNoTag: "No tag in the container export sends this event.",
@@ -32,17 +45,15 @@ const en = {
32
45
  gtmBlockedBy: "Blocked by",
33
46
  gtmPaused: "Paused",
34
47
  gtmConsent: (types) => `Needs consent: ${types}`,
48
+ gtmWhy: (tag, verdict, why) => `Tag ${q(tag)}: ${verdict} — ${why}`,
49
+ gtmVerdict: { true: "should have fired", false: "did not fire", unknown: "could not be evaluated" },
35
50
  fix: "Fix",
36
51
  captured: "Captured",
37
52
  noProblems: "No problems found.",
38
- noExplanation: "Run without --no-ai for an explanation.",
39
- matchingPlan: "Matching the plan",
40
53
  timeline: "Journeys and event timeline",
41
- timelineLead: "Every step of every journey. Events are grouped by platform; open one to see its parameters, the dataLayer push behind it and the network request that carried it. Steps with problems are open.",
42
54
  completed: "Completed",
43
55
  notCompleted: "Not completed",
44
56
  step: "step",
45
- trackingHits: "Tracking hits",
46
57
  dataLayer: "dataLayer pushes",
47
58
  network: "Network calls",
48
59
  platform: "Platform",
@@ -52,55 +63,39 @@ const en = {
52
63
  method: "Method",
53
64
  status: "Status",
54
65
  vendor: "Vendor",
55
- blockedNote: "answered locally, not sent",
56
66
  clickedThrough: "clicked through an overlapping element",
57
67
  generatedBy: "Generated by AI Tag Debugger",
58
68
  nothing: "None",
59
69
  counts: (c) => `${c.broken} broken · ${c.risk} risk · ${c.warning} warning · ${c.info} info · ${c.ok} ok`,
70
+ accepted: "Accepted",
71
+ acceptedLine: (n) => (n === 1 ? "1 accepted finding from the baseline is not counted" : `${n} accepted findings from the baseline are not counted`),
60
72
  whereStep: (journey, viewport, step) => `${journey} (${viewport})${step ? `, step "${step}"` : ""}`,
61
73
  allGood: "All planned events fired correctly on every journey tested.",
62
- tabEvents: "Events",
63
- tabTimeline: "Timeline",
64
- timelineGtmEvent: "GTM event",
65
- timelineConsent: "Consent command",
66
- timelineNoEvent: "No GTM event (data only)",
67
- timelineNoHits: "No platform hit followed this push.",
68
- timelineOther: "Hits without a dataLayer push",
69
74
  bodyDropped: "Request body not stored: this audit reached its 2 MB limit for stored request bodies.",
70
75
  tabNetwork: "Network requests",
71
- filterBy: "Filter by source",
72
76
  all: "All",
73
77
  items: "Items",
74
78
  customItemParams: "Custom item parameters",
75
- itemsCount: (n) => `${n} ${n === 1 ? "item" : "items"}`,
76
79
  noItems: "no items",
77
80
  serverSideTag: "server-side",
78
81
  source: "Source",
79
82
  request: "Request",
80
- noRequest: "No matching request",
81
83
  noPushes: "No dataLayer pushes in this step.",
82
84
  noRequests: "No requests to the chosen providers in this step.",
83
85
  eventParams: "Event parameters",
84
86
  contextParams: "Session, device, page and consent parameters",
85
87
  rawRequest: "Raw request",
86
- rawJson: "Raw JSON",
87
88
  inBatch: (i, n) => `${i} of ${n} in one request`,
88
89
  impact: "Impact",
89
90
  evidence: "Evidence",
90
91
  fixSteps: "Fix",
91
92
  verify: "How to verify",
92
93
  owners: { developer: "Developer", gtm: "Tag Manager", marketing: "Marketing" },
93
- ownerHint: { developer: "Site code and dataLayer", gtm: "Tags, triggers and consent setup", marketing: "The measurement plan" },
94
94
  places: (n) => `${n} ${n === 1 ? "place" : "places"}`,
95
95
  alsoCheck: "Not in the findings list, but worth checking",
96
- providersChecked: "Providers checked:",
97
- outOfScope: (list, n) => `Out of scope: ${list} (${n} ${n === 1 ? "request" : "requests"} skipped)`,
98
96
  skippedInStep: (list, n) => `Only requests to the chosen providers are shown. ${n} out-of-scope ${n === 1 ? "request was" : "requests were"} skipped in this step: ${list}.`,
99
- tabOverview: "Overview",
100
97
  tabJourneys: "Journeys",
101
- tabScope: "Scope and plan",
102
98
  sections: "Report sections",
103
- headerMeta: (journeys, providers) => `${journeys} ${journeys === 1 ? "journey" : "journeys"} · ${providers} ${providers === 1 ? "provider" : "providers"}`,
104
99
  checkedAgainst: (id) => `Checked against plan ${id}`,
105
100
  planChangesLine: (pc) => (pc.same ? `Plan unchanged since ${pc.previous}` : `Plan changed since ${pc.previous}: ${pc.added.length} added, ${pc.removed.length} removed, ${pc.changed.length} changed`),
106
101
  planVsSite: "Plan vs. site",
@@ -112,17 +107,11 @@ const en = {
112
107
  noSavedRun: "No saved run for this site yet. Run `aitd run` first; each run is compared with the one before it.",
113
108
  routeChanged: (list) => `The AI took a different route through the site than in the last run: ${list}`,
114
109
  noPreviousRun: "This is the first saved run for this site, so there is nothing to compare yet.",
115
- planVsSiteLead: "Each planned event, and what the site actually sent",
116
110
  coverageLine: (c) => `Coverage ${c.implemented} of ${c.checkable}${c.percent === null ? "" : ` (${c.percent}%)`}${c.not_checked ? `, ${c.not_checked} ${c.not_checked === 1 ? "event" : "events"} not checked` : ""}`,
117
111
  bucket: { implemented: "Implemented", missing: "Missing", missing_params: "Missing params", wrong_values: "Wrong values", unplanned: "Unplanned", not_checked: "Not checked" },
118
112
  bucketCol: "Result",
119
- summaryLead: "AI summary, most important first",
120
- noSummary: "Run without --no-ai for an AI summary of the findings.",
121
- seeAllFindings: "See all findings →",
122
113
  journey: "Journey",
123
114
  stepsCol: "Steps",
124
- problemsCol: "Problems",
125
- scopeCard: "Scope",
126
115
  site: "Site",
127
116
  runCard: "This run",
128
117
  runPlatforms: "Platforms",
@@ -136,92 +125,602 @@ const en = {
136
125
  runViewports: "Viewports",
137
126
  runLanguage: "Language",
138
127
  runVersion: "Version",
128
+ junitJourney: "journey completed",
129
+ junitSite: "site",
139
130
  consentDefault: "Default",
140
131
  consentUpdate: "Update",
141
- consentLead: "Consent Mode commands the site sent, in order.",
142
- consentNone: "The site sent no Consent Mode commands on this journey.",
143
132
  scenarios: "Consent scenarios",
144
- scenariosLead: (cmp) => `The start page opened once per consent choice (answered through ${cmp === "banner" ? "the banner's buttons" : cmp}). Hits per tag after the choice; red ones should not have been sent.`,
145
133
  scenarioCols: { before: "No choice yet", accept: "Accept all", reject: "Reject all", analytics: "Analytics only" },
146
134
  scenarioNotRun: "Not supported here",
147
135
  scenarioTag: "Tag",
148
136
  skippedLine: (list, n) => `${n} out-of-scope ${n === 1 ? "request was" : "requests were"} skipped: ${list}.`,
149
- findingHint: "Open a finding to see its likely cause, evidence and fix steps.",
150
- spread: (j, s) => `${j} ${j === 1 ? "journey" : "journeys"}${s ? ` · ${s} ${s === 1 ? "step" : "steps"}` : ""}`,
151
137
  chooseJourney: "Choose a journey",
152
- stepsNav: "Steps",
153
- stepMeta: (t, events, pushes, skipped) => `${t} · ${events} ${events === 1 ? "event" : "events"} · ${pushes} dataLayer${skipped ? ` · ${skipped} out-of-scope ${skipped === 1 ? "request" : "requests"} skipped` : ""}`,
154
138
  noSteps: "This journey has no steps.",
155
139
  planCoverage: "Measurement plan coverage",
156
- planCoverageLead: "Whether each planned event was sent on each journey",
157
- coverageLegend: "✓ sent · ✗ not sent · – not expected on this journey",
158
140
  notChecked: "not checked",
159
141
  noPlan: "No measurement plan was given, so there is nothing to compare against.",
160
142
  providers: "Providers",
161
143
  inventoryTitle: "Tag inventory",
162
- inventoryLead: "Every vendor the site sent requests to, and where",
163
144
  vendorCol: "Vendor",
164
145
  partyCol: "Party",
165
146
  pagesCol: "Pages",
166
147
  cookiesCol: "Cookies",
167
148
  party: { first: "First party", third: "Third party", both: "Both" },
168
- morePages: (n) => `+${n} more`,
169
149
  otherCookies: "Other cookies",
170
150
  noInventory: "No vendor requests were recorded.",
171
151
  provider: "Provider",
172
- requestsCol: "Requests",
173
152
  checked: "Checked",
174
153
  outOfScopeStatus: "Out of scope",
175
154
  missing: "missing",
176
155
  requestTo: (p) => `${p} request`,
177
156
  by: "by",
178
157
  credit: (atd) => `This report was made with the open source ${atd}.`,
179
- timelineLeadV2: "Every step has three tabs: the events it sent, its dataLayer pushes and its network requests. Events and requests can be filtered by source. Steps with problems are open.",
158
+ // The run history (src/history.js): the trend lines after a run, in the alert and in `aitd history`.
159
+ history: {
160
+ noRuns: "No saved run for this site yet. Run `aitd run` first; the trend starts with the second run.",
161
+ oneRun: (id) => `One saved run (${id}); the trend starts with the next one.`,
162
+ direction: { improving: "improving", worsening: "worsening", flat: "flat" },
163
+ headline: (runs, first, last, dir, now, was, ago) => `Trend over ${runs} runs (${first} → ${last}): ${dir}. Now ${now}; ${ago} runs ago ${was}.`,
164
+ headlineShort: (runs, first, last, dir, now) => `Trend over ${runs} runs (${first} → ${last}): ${dir}. Now ${now}.`,
165
+ counts: (c) => `${c.broken} broken · ${c.risk} risk · ${c.warning} warning`,
166
+ openLongest: (items) => `Open longest: ${items.join("; ")}.`,
167
+ since: (label, day, runs) => `${label} since ${day} (${runs} ${runs === 1 ? "run" : "runs"})`,
168
+ flapping: (items) => `Flapping (seen, gone, back again): ${items.join("; ")}.`,
169
+ seenIn: (label, seen, runs) => `${label} in ${seen} of ${runs} runs`,
170
+ places: (n) => ` in ${n} places`,
171
+ unstable: (items) => `Journeys that do not finish every time: ${items.join("; ")}.`,
172
+ stillOpen: (runs, items) => `Still open after ${runs} runs: ${items.join("; ")}.`,
173
+ sparkline: (spark, dir, runs, first, last) => `${spark} ${dir} over ${runs} runs (${first} → ${last})`,
174
+ fixedWas: "was",
175
+ },
176
+ // The Markdown report (docs/design/md-report-spec.md): a verdict first, evidence second.
177
+ md: {
178
+ title: (site, headline) => `${site}: ${headline}`,
179
+ meta: (at, journeys, platforms, version) => `Generated ${at} · ${journeys} ${journeys === 1 ? "journey" : "journeys"} · ${platforms} · ai-tag-debugger ${version}`,
180
+ platformsSeen: (n, list) => (n ? `${n} ${n === 1 ? "platform" : "platforms"} seen (${list})` : "no platform seen"),
181
+ htmlReport: "HTML report",
182
+ noParams: "the hit carried no parameters",
183
+ score: (score) => `data quality score ${score}/100`,
184
+ // Fallback headlines per verdict key, used when the `verdict.headline` strings are not there.
185
+ headline: {
186
+ fail_broken: (what, scope) => `${what} is broken${scope}`,
187
+ fail_incomplete: (journey) => `the ${journey} journey did not finish, so its events could not be checked`,
188
+ attention_risk: (what, scope) => `${what} carries a consent or data risk${scope}`,
189
+ attention_warning: (what, scope) => `${what} needs attention${scope}`,
190
+ attention_no_hits: () => "no tracking hits were captured",
191
+ attention_nothing_tested: () => "nothing was tested",
192
+ pass: (events, journeys) => (events ? `all ${events} planned events fired correctly on ${journeys} ${journeys === 1 ? "journey" : "journeys"}` : `all checks passed on ${journeys} ${journeys === 1 ? "journey" : "journeys"}`),
193
+ },
194
+ tracking: (event) => `${event} tracking`,
195
+ setup: (check) => `the ${check} check`,
196
+ onViewport: (viewport) => ` on ${viewport}`,
197
+ onJourney: (journey) => ` on ${journey}`,
198
+ everywhere: " on every journey",
199
+ aiSummary: "AI summary",
200
+ stillOpen: "Still open",
201
+ priorities: "Priorities",
202
+ nothingToDo: (owner) => `${owner}: nothing to do.`,
203
+ allJourneys: (n) => `all ${n} journeys`,
204
+ step: (n) => `step ${n}`,
205
+ more: (n) => `+${n} more`,
206
+ also: "Also",
207
+ owner: "Owner",
208
+ link: "Link",
209
+ alsoFineOn: "Also fine on",
210
+ consent: "Consent",
211
+ consentMode: (present, total, onTime, v2) => `Consent Mode default set on ${present} of ${total} ${total === 1 ? "journey" : "journeys"}${onTime === false ? ", after the first Google hit" : onTime ? ", before the first Google hit" : ""}; v2 signals ${v2 ? "present" : "missing"}.`,
212
+ consentModeNone: "No Consent Mode default command was seen on any journey.",
213
+ consentNotChecked: "Consent was not checked in this run.",
214
+ cell: { ok: "✓", bad: "✗", cookieless: "cookieless", beforeConsent: "before consent", shouldNotSend: "should not send", adDenied: "ad storage denied", notRun: "–" },
215
+ consentLegend: "✓ allowed · ✗ should not have been sent · \"cookieless\" = Consent Mode ping with storage denied · – = that choice was not run",
216
+ whatPassed: "What passed",
217
+ coverage: (implemented, total, percent) => `Plan coverage: ${implemented} of ${total} planned events implemented${percent === null ? "" : ` (${percent}%)`}`,
218
+ purchaseOk: (platform, place, value, currency, id) => `${platform} purchase value and currency: ${place} (${value} ${currency}${id ? `, ${id}` : ""})`,
219
+ parityOk: (platforms, place) => `Purchase cross-platform match: ${platforms} agree on ${place}`,
220
+ noPii: "Personal data: no plain-text email or user data in any hit.",
221
+ viewport: "Viewport",
222
+ hits: "Hits",
223
+ yes: "yes",
224
+ no: "no",
225
+ flaggedSteps: "Flagged steps",
226
+ timelineNote: "Every hit, dataLayer push and request of every step is in timeline.csv.",
227
+ moreIn: (n, file) => `+${n} more in ${file}`,
228
+ notChecked: "Not checked",
229
+ noPlan: "Planned events: no measurement plan given (`plan` in the config).",
230
+ noGtm: "GTM tag names: no container export given (`gtm_container` in the config).",
231
+ noAnalyticsOnly: "Analytics-only consent: the banner has no such choice.",
232
+ noScenarios: "Consent scenarios: not run (no banner the tool can answer, or a replay).",
233
+ platformsSilent: (list, n) => `${list} ${n === 1 ? "was" : "were"} in scope but the site sent nothing ${n === 1 ? "to it" : "to them"}.`,
234
+ trend: "Trend",
235
+ },
236
+ // Lines the CLI prints after a run and in `aitd history`, in the report's language.
237
+ cli: {
238
+ verdict: (status, headline, score) => `Verdict: ${status} · ${headline}${score === null ? "" : ` · score ${score}/100`}`,
239
+ runCol: "Run",
240
+ scoreCol: "Score",
241
+ completeCol: "Complete",
242
+ historyNone: "No saved run for this site yet.",
243
+ },
180
244
  },
181
245
  detail: {
182
246
  journeyIncomplete: (note) => `The journey did not reach its goal${note ? `: ${note}` : "."} Events after that point could not be checked.`,
183
- missingEvent: (platform, name) => `Expected ${platform} event ${q(name)} was never sent during this journey.`,
184
- missingEventEarly: (name) => `${q(name)} was not seen, but the journey stopped early, so this may not be a tracking bug.`,
185
- missingParams: (name, params) => `${q(name)} was sent without ${params.map(q).join(", ")}.`,
186
- duplicate: (name, n) => `${q(name)} was sent ${n} times by one action, which inflates counts and conversions.`,
187
- purchaseValueZero: (platform, name, v) => `${platform} ${q(name)} was sent with value ${q(v)}. A purchase worth 0 or less adds no revenue, and ad platforms can't optimize bids on it.`,
188
- purchaseValueNaN: (platform, name, v) => `${platform} ${q(name)} was sent with value ${q(v)}, which is not a number, so the revenue is lost. Send a plain number such as 49.90, with no currency sign or thousands separator.`,
189
- purchaseCurrency: (platform, name, c) => `${platform} ${q(name)} was sent with currency ${q(c)}, which is not a 3-letter ISO 4217 code in capitals (such as USD, EUR or TRY). Platforms may drop the revenue or convert it wrongly.`,
190
- purchaseItemsEmpty: (platform, name) => `${platform} ${q(name)} was sent with no items, so product and item reports get nothing for this order.`,
191
- purchaseMismatch: (id, diffs) => `GA4 and Meta reported ${id ? `order ${q(id)}` : "the purchase"} differently: ${diffs.map(([f, g, m]) => `${f} ${q(g)} in GA4 and ${q(m)} in Meta`).join("; ")}. One of the tags reads the wrong data.`,
192
- serverSide: (platform, server, browser, unseen = []) => `${platform}: ${server.map(q).join(", ")} ${server.length === 1 ? "was" : "were"} sent to a server container on the site's own domain (server-side tagging)${browser.length ? `; ${browser.map(q).join(", ")} went directly from the browser` : ""}.${unseen.length ? ` Server-side copies of ${unseen.join(" and ")} events never pass through the browser, so they can't be seen here.` : ""}`,
247
+ missingEvent: (platform, name) => `${label(platform)} ${q(name)} was not sent on this journey, although the journey reached its goal. ${label(platform)} recorded nothing for it.`,
248
+ missingEventEarly: (name) => `${q(name)} was not seen, but the journey stopped early, so it is unverified rather than missing.`,
249
+ missingParams: (name, params) => `${q(name)} was sent without ${andList(sortParams(params).map(q), "and")}.`,
250
+ duplicate: (name, n) => `${q(name)} was sent ${n} times by one action. Every real one is counted ${n} times.`,
251
+ purchaseValueZero: (platform, name, v) => `${label(platform)} ${q(name)} was sent with value ${q(v)}. A purchase worth 0 or less adds no revenue, so ad platforms cannot optimise bids on it.`,
252
+ purchaseValueNaN: (platform, name, v) => `${label(platform)} ${q(name)} was sent with value ${q(v)}, which is not a number, so the revenue is lost. Send a plain number such as 49.90, with no currency sign or thousands separator.`,
253
+ purchaseCurrency: (platform, name, c) => `${label(platform)} ${q(name)} was sent with currency ${q(c)}, which is not a 3-letter ISO 4217 code in capitals (such as USD, EUR or TRY). Platforms drop the revenue or convert it at the wrong rate.`,
254
+ purchaseItemsEmpty: (platform, name) => `${label(platform)} ${q(name)} was sent with no items, so product and item reports get nothing for this order.`,
255
+ purchaseMismatch: (id, diffs) => `${id ? `Order ${q(id)}` : "The purchase"} was reported differently: ${diffs.map(([f, g, m]) => `${f} ${q(g)} in GA4 and ${q(m)} in Meta`).join("; ")}. One tag reads the wrong number, so Google and Meta bid on different revenue.`,
256
+ serverSide: (platform, server, browser, unseen = []) => `${label(platform)}: ${server.map(q).join(", ")} ${server.length === 1 ? "was" : "were"} sent to a server container on the site's own domain (server-side tagging)${browser.length ? `; ${browser.map(q).join(", ")} went directly from the browser` : ""}.${unseen.length ? ` Server-side copies of ${unseen.join(" and ")} events never pass through the browser, so they can't be seen here.` : ""}`,
193
257
  serverEndpoint: (hosts) => `A server container answers on ${hosts.map(q).join(", ")} (server-side GTM or a similar setup). Events it forwards to ad platforms never pass through the browser, so they can't be seen here.`,
194
- eventIdReused: (platform, id, names, n) => `${platform} event ID ${q(id)} was used for ${n} different events (${names.map(q).join(", ")}). ${platform} keeps the first and drops the rest as duplicates. Give each event its own ID.`,
258
+ eventIdReused: (platform, id, names, n) => `${label(platform)} event ID ${q(id)} was used for ${n} different events (${names.map(q).join(", ")}). ${label(platform)} keeps the first and drops the rest as duplicates. Give each event its own ID.`,
195
259
  eventIdNotForwarded: (name, ga4Name) => `Meta ${q(name)} has an event ID, but the GA4 ${q(ga4Name)} hit sent to the server container has no event_id. If the server container builds the Conversions API event from that hit, Meta can't match it with the pixel event and counts it twice. Send the same ID as an event_id parameter on the GA4 event.`,
196
260
  eventIdMismatch: (name, metaId, ga4Name, ga4Id) => `Meta ${q(name)} has event ID ${q(metaId)}, but the GA4 ${q(ga4Name)} hit sent to the server container carries event_id ${q(ga4Id)}. A Conversions API event built from that hit won't match the pixel event, so Meta counts it twice. Use one ID for both.`,
197
- dedupIdMissing: (platform, name) => `${platform} ${q(name)} was sent from the browser without an event ID, while a server-side setup was seen on this site. If the server also sends this event, ${platform} can't match the two and counts it twice.`,
261
+ dedupIdMissing: (platform, name) => `${label(platform)} ${q(name)} was sent from the browser without an event ID, while a server-side setup was seen on this site. If the server also sends this event, ${label(platform)} cannot match the two and counts it twice.`,
198
262
  campaignLost: (names, finalUrl) => `The landing address carried ${names.join(", ")}, but the page ended up on ${finalUrl} without ${names.length === 1 ? "it" : "them"}. A redirect dropped ${names.length === 1 ? "the parameter" : "the parameters"}, so ad clicks lose their attribution.`,
199
- campaignMissingInHits: (platform, name, names) => `${platform} ${q(name)} on the landing page reports a page address without ${names.join(", ")}, though the page still had ${names.length === 1 ? "it" : "them"}. The visit can be credited to direct traffic instead of the campaign.`,
263
+ campaignMissingInHits: (platform, name, names) => `${label(platform)} ${q(name)} on the landing page reports a page address without ${names.join(", ")}, though the page still had ${names.length === 1 ? "it" : "them"}. The visit can be credited to direct traffic instead of the campaign.`,
200
264
  campaignOk: (names) => `${names.join(", ")} survived the landing page's redirects and reached the hits.`,
201
265
  consentDefaultMissing: () => "No Google Consent Mode default command (gtag('consent', 'default', ...)) was found in the dataLayer.",
202
266
  consentDefaultLate: () => "The Consent Mode default was set after the first tracking hit was sent.",
203
- beforeConsentNever: (platform, name) => `${platform} ${q(name)} fired although consent was never given in this journey.`,
204
- beforeConsent: (platform, name) => `${platform} ${q(name)} fired before the visitor accepted cookies.`,
267
+ beforeConsentNever: (platform, name) => `${label(platform)} ${q(name)} was sent although the visitor never made a consent choice on this journey. ${label(platform)} receives data from visitors who have not agreed.`,
268
+ beforeConsent: (platform, name) => `${label(platform)} ${q(name)} was sent before the visitor made a consent choice. ${label(platform)} receives data from visitors who have not agreed.`,
205
269
  consentV2Missing: (keys) => `The Consent Mode default does not set ${keys.map(q).join(" and ")}. Consent Mode v2 needs them for Google ads in the EEA and UK; without them ad personalization and conversion measurement can stop.`,
206
- consentV2Update: (keys) => `After the visitor accepted, the Consent Mode update did not grant ${keys.map(q).join(" and ")}. Ad conversions and remarketing may stay off.`,
270
+ consentV2Update: (keys) => `After the visitor accepted, the Consent Mode update did not grant ${keys.map(q).join(" and ")}. They stayed denied, so Google Ads conversions and remarketing stay off even for consenting visitors.`,
207
271
  rejectIgnored: () => "A Consent Mode update granted consent after the visitor rejected cookies, so the reject button does not stick.",
208
272
  rejectButtonFailed: () => "The cookie banner was still showing after the reject button was clicked.",
209
- afterReject: (platform, name) => `${platform} ${q(name)} was still sent after the visitor rejected cookies.`,
273
+ afterReject: (platform, name) => `${label(platform)} ${q(name)} was still sent after the visitor rejected cookies.`,
210
274
  cookiesBeforeConsent: (names) => `Tracking cookies were set before the visitor accepted: ${names.join(", ")}.`,
211
275
  cookiesNeverConsent: (names) => `Tracking cookies were set although consent was never given in this journey: ${names.join(", ")}.`,
212
276
  cookiesAfterReject: (names) => `Tracking cookies were set after the visitor rejected cookies: ${names.join(", ")}.`,
213
- pii: (platform, name) => `An email address was sent in plain text with ${q(name)} to ${platform}.`,
277
+ pii: (platform, name) => `An email address was sent in plain text with ${q(name)} to ${label(platform)}.`,
214
278
  unplanned: (list) => `Events sent that are not in the measurement plan: ${list.join(", ")}.`,
215
- piiUnhashed: (platform, name, fields) => `${q(name)} sent user data to ${platform} without hashing it (${fields.join(", ")}). Ad platforms expect these values SHA-256 hashed.`,
279
+ piiUnhashed: (platform, name, fields) => `${q(name)} sent user data to ${label(platform)} without hashing it (${fields.join(", ")}). Ad platforms expect these values SHA-256 hashed.`,
216
280
  piiDataLayer: (fields) => `The dataLayer holds user data that isn't hashed (${fields.join(", ")}). This is a valid setup for Google enhanced conversions, but every other tag and script on the page can read it too. Check that only the tags that should use it do.`,
217
- plannedOutOfScope: (list) => `${list.length} planned ${list.length === 1 ? "event was" : "events were"} not checked because the provider wasn't selected: ${list.join(", ")}. Add the provider with --providers to check ${list.length === 1 ? "it" : "them"}.`,
281
+ plannedOutOfScope: (list) => `${list.length} planned ${list.length === 1 ? "event was" : "events were"} not checked because ${list.length === 1 ? "its platform was" : "their platforms were"} not selected for this run: ${list.join(", ")}.`,
218
282
  matches: (list) => `${list.length} planned events matched the plan: ${list.join(", ")}.`,
219
283
  },
284
+ // Why a GTM tag did or did not fire, judged from the captured dataLayer.
285
+ gtm: {
286
+ paused: "the tag is paused",
287
+ noTrigger: "the tag has no firing trigger",
288
+ missingTrigger: (trigger) => `trigger ${trigger} is not in the container export`,
289
+ noPush: (event) => `no dataLayer push with event ${q(event)} in this journey`,
290
+ noPageEvent: (trigger, event) => `trigger ${q(trigger)} waits for GTM's ${event} event, which never reached the dataLayer; the container may not have loaded`,
291
+ pageTrigger: (trigger, kind) => `${kind} triggers such as ${q(trigger)} are evaluated by GTM in the page, not from the dataLayer`,
292
+ unknownType: (trigger, type) => `trigger ${q(trigger)} is of type ${type}, which can't be evaluated from the dataLayer`,
293
+ condition: (trigger, field, op, want, had, negate) => `trigger ${q(trigger)} needs ${field} ${negate ? "not " : ""}${op} ${want}, the push had ${had}`,
294
+ conditionUnknown: (trigger, variable) => `trigger ${q(trigger)} checks ${q(variable)}, which can't be read from the dataLayer`,
295
+ conditionType: (trigger, type) => `trigger ${q(trigger)} has a ${type} condition, which can't be evaluated from the dataLayer`,
296
+ ops: { EQUALS: "equal to", CONTAINS: "containing", STARTS_WITH: "starting with", ENDS_WITH: "ending with", MATCH_REGEX: "matching", LESS: "less than", LESS_OR_EQUALS: "at most", GREATER: "greater than", GREATER_OR_EQUALS: "at least" },
297
+ nothing: "no value",
298
+ triggerFired: (trigger, event) => `trigger ${q(trigger)} fired on the ${q(event)} push`,
299
+ blocked: (trigger) => `blocked by exception trigger ${q(trigger)}`,
300
+ consentDenied: (type) => `needs ${type}, which was denied when the event fired`,
301
+ fired: "the trigger fired and consent was granted; the tag itself or its configuration (measurement id, event name) may be wrong",
302
+ },
303
+ // One sentence per check on what the finding costs, for the reader who owns the budget rather than the
304
+ // tag. No placeholders: the finding's detail names the event, parameter and place.
305
+ consequence: {
306
+ missing_event: "The platform never received this event, so this step of the funnel is invisible and a conversion here cannot be counted or bid on.",
307
+ missing_params: "The event is counted but its required details are missing, so revenue, currency or items never reach the reports.",
308
+ duplicate: "One action is counted several times, so counts and conversion totals are inflated and ad platforms bid on sales that did not happen.",
309
+ purchase_value_invalid: "The purchase value is not a usable number, so revenue is recorded as nothing and return-on-ad-spend reports and value bidding are wrong.",
310
+ purchase_currency_invalid: "The currency is not a code platforms recognise, so the revenue is dropped or converted at the wrong rate.",
311
+ purchase_items_empty: "The purchase has no products, so product reports, catalogue feeds and dynamic remarketing see no sales.",
312
+ purchase_mismatch: "Google and Meta received different revenue for the same order, so one of them reports and bids on the wrong number.",
313
+ campaign_lost_redirect: "A redirect strips the campaign parameters from the landing address, so paid clicks arrive as direct traffic and campaigns get no credit.",
314
+ campaign_missing_in_hits: "The landing page kept the campaign parameters but the hit did not report them, so the visit is credited to direct traffic.",
315
+ campaign_params: "Campaign parameters survived the landing redirect and reached the hits, so paid clicks are credited to their campaigns.",
316
+ event_id_reused: "Two different events share one event ID, so the platform keeps the first and silently drops the other: a real event is lost.",
317
+ event_id_mismatch: "The browser and server copies of this event carry different IDs, so Meta cannot pair them and counts the conversion twice.",
318
+ event_id_not_forwarded: "The GA4 hit that feeds the server container has no event ID, so a server copy cannot be paired with the pixel and is counted twice.",
319
+ dedup_id_missing: "The event left the browser with no event ID while the site uses a server container; if the server also sends it, it is counted twice.",
320
+ server_side: "Some events go through the site's own server; this report sees what the browser sent, not what the server forwarded.",
321
+ server_endpoint: "A server container answers on the site's domain; the events it forwards to ad platforms are not visible here.",
322
+ consent_default_missing: "Google tags run with no consent default, so they behave as if every visitor had consented before being asked.",
323
+ consent_default_late: "The first Google hit went out before the consent default was set, so that hit ignored the visitor's choice.",
324
+ consent_v2_missing: "The consent default lacks the v2 signals, so for EEA and UK traffic Google can stop ad personalisation and conversion measurement.",
325
+ consent_v2_update: "After the visitor accepted, the v2 signals stayed denied, so Google Ads conversions and remarketing stay off even for consenting visitors.",
326
+ before_consent: "The event was sent before the visitor made a choice, so the platform receives data from visitors who have not agreed.",
327
+ after_reject: "The event was still sent after the visitor clicked Reject, so tracking continues against an explicit refusal.",
328
+ reject_ignored: "After Reject, a consent update granted everything anyway, so the reject button is cosmetic.",
329
+ reject_button_failed: "The reject button did nothing and the banner stayed open, so the visitor cannot refuse tracking.",
330
+ cookies_before_consent: "Tracking cookies were written before the visitor chose, so cookies are set without consent.",
331
+ cookies_after_reject: "Tracking cookies were written after the visitor refused them, so the refusal is not honoured.",
332
+ pii: "Personal data left the site unprotected: an email address or ad-matching data in plain text is now in the platform's logs.",
333
+ pii_datalayer: "The dataLayer holds unhashed personal data, so every script on the page can read it, not only the tag that needs it.",
334
+ journey_incomplete: "The browser could not finish this journey, so every event after that point, including any conversion, is unverified.",
335
+ unplanned_events: "Events fire that are not in the plan: either the plan is out of date or tags send things nobody asked for.",
336
+ planned_out_of_scope: "Some planned events are on platforms this run did not check, so their status is unknown.",
337
+ matches_plan: "These planned events fired with everything the plan requires.",
338
+ },
339
+ // The generic direction of the fix for each check. The finding's detail and GTM section carry the exact
340
+ // event, parameter, tag and trigger; the AI's fix steps replace this text when a model ran.
341
+ fix: {
342
+ missing_event: "Check that the tag for this event exists, fires on the right trigger and is not blocked by consent or an exception; the GTM section names the tag when a container export was given. Then re-run this journey.",
343
+ missing_params: "Add the missing parameters to the dataLayer push behind this event, on this page and viewport, and make sure the tag reads them. Re-run the journey to confirm.",
344
+ duplicate: "Find the second sender: two pushes, two tags or a trigger that fires twice. Remove one, or add a once-per-event guard on the tag.",
345
+ purchase_value_invalid: "Send the value as a plain number such as 49.90, with no currency sign or thousands separator, and never 0 for a paid order.",
346
+ purchase_currency_invalid: "Send the currency from the order data as a 3-letter ISO 4217 code in capitals, such as USD, EUR or TRY.",
347
+ purchase_items_empty: "Fill the items list in the purchase push from the order's line items (id, name, price, quantity) and map it in the tag.",
348
+ purchase_mismatch: "Make both tags read the same order fields; usually one reads a subtotal or a different currency. Then compare one order in both platforms.",
349
+ campaign_lost_redirect: "Make the redirect keep the query string, or land paid traffic on the final address directly.",
350
+ campaign_missing_in_hits: "Make the tag report the full landing address, parameters included; look for a page URL override or a page_location variable that fills in late.",
351
+ campaign_params: "Nothing to fix. Check the redirects again when the landing pages change.",
352
+ event_id_reused: "Generate a new event ID for every event, not once per page load.",
353
+ event_id_mismatch: "Use one event ID for the pixel event and the server copy: pass the same value to both tags.",
354
+ event_id_not_forwarded: "Add an event_id parameter, carrying the pixel's ID, to the GA4 event the server container turns into the Conversions API event.",
355
+ dedup_id_missing: "Add an event ID to the browser event and send the same ID from the server, so the platform can pair the two.",
356
+ server_side: "Nothing to fix in the browser. Verify the events the server forwards in the platform's own test tools.",
357
+ server_endpoint: "Nothing to fix in the browser. Check what the server container forwards in its own preview mode.",
358
+ consent_default_missing: "Set a Consent Mode default (all denied where the law requires it) before any Google tag loads, usually in the consent banner's script or a GTM template.",
359
+ consent_default_late: "Move the consent default ahead of the Google tag: it must run before gtm.js or gtag.js loads.",
360
+ consent_v2_missing: "Add ad_user_data and ad_personalization to the consent default, next to ad_storage and analytics_storage.",
361
+ consent_v2_update: "Grant ad_user_data and ad_personalization in the consent update the banner sends after the visitor accepts.",
362
+ before_consent: "Give the tag a consent check (its consent settings or an additional consent trigger), or fire it only after the banner's choice.",
363
+ after_reject: "Make the tag respect the refusal: a consent check on the tag, or a blocking trigger when consent is denied.",
364
+ reject_ignored: "Fix the banner's reject handler so it sends an update with every signal denied and never grants afterwards.",
365
+ reject_button_failed: "Fix the banner's reject button so it closes the banner and stores the refusal; look for a script error on the click.",
366
+ cookies_before_consent: "Load the tags that set these cookies only after consent, or run them in cookieless mode until the visitor chooses.",
367
+ cookies_after_reject: "Block the tags that set these cookies when consent is denied, and delete cookies already set when the visitor refuses.",
368
+ pii: "Take the email or user data out of the hit, or hash it with SHA-256 before the tag sends it; check the URLs and form fields the tag reads.",
369
+ pii_datalayer: "Push user data to the dataLayer already hashed, or restrict it to the tag that needs it and clear it afterwards.",
370
+ journey_incomplete: "Read the note for why the browser stopped (a blocked click, a missing element, an error), fix the page or the step, and re-run the journey.",
371
+ unplanned_events: "Add the events to the measurement plan if they are wanted, or remove the tags that send them.",
372
+ planned_out_of_scope: "Include their platforms in the next run's providers so they are checked.",
373
+ matches_plan: "Nothing to fix.",
374
+ },
375
+ // What kind of damage a check reports: money (revenue, ad spend, attribution), legal (consent, personal
376
+ // data) or hygiene (scope and coverage). Identical in every language; the label block translates it.
377
+ category: {
378
+ missing_event: "money",
379
+ missing_params: "money",
380
+ duplicate: "money",
381
+ purchase_value_invalid: "money",
382
+ purchase_currency_invalid: "money",
383
+ purchase_items_empty: "money",
384
+ purchase_mismatch: "money",
385
+ campaign_lost_redirect: "money",
386
+ campaign_missing_in_hits: "money",
387
+ campaign_params: "money",
388
+ event_id_reused: "money",
389
+ event_id_mismatch: "money",
390
+ event_id_not_forwarded: "money",
391
+ dedup_id_missing: "money",
392
+ server_side: "hygiene",
393
+ server_endpoint: "hygiene",
394
+ consent_default_missing: "legal",
395
+ consent_default_late: "legal",
396
+ consent_v2_missing: "legal",
397
+ consent_v2_update: "money",
398
+ before_consent: "legal",
399
+ after_reject: "legal",
400
+ reject_ignored: "legal",
401
+ reject_button_failed: "legal",
402
+ cookies_before_consent: "legal",
403
+ cookies_after_reject: "legal",
404
+ pii: "legal",
405
+ pii_datalayer: "legal",
406
+ journey_incomplete: "hygiene",
407
+ unplanned_events: "hygiene",
408
+ planned_out_of_scope: "hygiene",
409
+ matches_plan: "hygiene",
410
+ },
411
+ categoryLabel: { money: "Money", legal: "Legal", hygiene: "Hygiene" },
412
+ // The deterministic verdict. Each headline takes the object verdict() in metrics.js returns
413
+ // ({ status, headline, counts, journeys, problems }); a bare counts object also works.
414
+ verdict: {
415
+ status: { pass: "Pass", fail: "Fail", attention: "Needs attention" },
416
+ headline: {
417
+ fail_broken: (v) => {
418
+ const c = countsOf(v);
419
+ const risks = c.risk ? `; ${c.risk} consent or personal-data ${c.risk === 1 ? "risk sits" : "risks sit"} with Tag Manager` : "";
420
+ return `${c.broken} broken ${c.broken === 1 ? "finding needs" : "findings need"} a fix before the numbers can be trusted${risks}.`;
421
+ },
422
+ fail_incomplete: (v) => {
423
+ const j = journeysOf(v);
424
+ const where = j.total ? `${j.incomplete} of ${j.total} journeys did not finish` : "A journey did not finish";
425
+ return `${where}, so every event after the stop, including any conversion, is unverified.`;
426
+ },
427
+ attention_risk: (v) => {
428
+ const c = countsOf(v);
429
+ const warnings = c.warning ? `, and ${c.warning} ${c.warning === 1 ? "warning distorts" : "warnings distort"} the counts` : "";
430
+ return `Nothing is broken, but ${c.risk} consent or personal-data ${c.risk === 1 ? "risk needs" : "risks need"} fixing in Tag Manager${warnings}.`;
431
+ },
432
+ attention_warning: (v) => {
433
+ const c = countsOf(v);
434
+ return `Nothing is broken, but ${c.warning} ${c.warning === 1 ? "warning distorts" : "warnings distort"} counts or coverage and ${c.warning === 1 ? "needs" : "need"} tidying.`;
435
+ },
436
+ attention_no_hits: (v) => {
437
+ const j = journeysOf(v);
438
+ return `${j.total ? `${j.total} ${j.total === 1 ? "journey" : "journeys"} ran` : "The journeys ran"}, but no tracking request was captured, so nothing could be checked.`;
439
+ },
440
+ attention_nothing_tested: () => "No journey ran and nothing was captured, so there is nothing to judge yet.",
441
+ pass: (v) => {
442
+ const j = journeysOf(v);
443
+ return `Every planned event fired correctly on ${j.total ? `all ${j.total} ${j.total === 1 ? "journey" : "journeys"}` : "every journey tested"}; nothing to fix.`;
444
+ },
445
+ },
446
+ },
447
+ html: {
448
+ skip: "Skip to the report",
449
+ eyebrow: "Tracking audit",
450
+ countsLabel: "Findings by severity",
451
+ themeAuto: "Theme: follows your system",
452
+ themeLight: "Theme: light",
453
+ themeDark: "Theme: dark",
454
+ newTab: "opens in a new tab",
455
+ aiSummary: "AI summary",
456
+ platformsChecked: "Platforms checked",
457
+ campaignCheck: "Campaign check",
458
+ consentModeSeen: "Consent Mode commands seen",
459
+ cmpBanner: "banner",
460
+ scenariosRun: (n) => `${n} ${n === 1 ? "scenario" : "scenarios"}`,
461
+ runsAllCompleted: (n) => `${n} ${n === 1 ? "run" : "runs"}, all completed`,
462
+ runsCompleted: (done, n) => `${done} of ${n} runs completed`,
463
+ moreInFindings: (n) => `${n} more in Findings →`,
464
+ working: "What is working",
465
+ whatWasChecked: "What was checked",
466
+ nothingVerified: "Nothing was verified in this run.",
467
+ allRuns: (n) => `all ${n} runs`,
468
+ onlyOn: (places) => `${places.join(", ")} only`,
469
+ viewport: "Viewport",
470
+ hitsCol: "Hits",
471
+ trend: "Trend",
472
+ trendDirection: { improving: "Improving", worsening: "Getting worse", flat: "No change" },
473
+ trendSpan: (n, first, last) => `over ${n} runs, ${first} to ${last}`,
474
+ runIncomplete: "a journey did not finish",
475
+ flapping: "Seen, gone, back again",
476
+ seenIn: (n, total) => `seen in ${n} of ${total} runs`,
477
+ openLongest: "Open longest",
478
+ openSince: (date, runs, places) => `open since ${date} · ${runs} ${runs === 1 ? "run" : "runs"} · ${places} ${places === 1 ? "place" : "places"}`,
479
+ score: "Data quality score",
480
+ noModel: "No model explanation in this run. The evidence, owner and verify links below do not need one.",
481
+ accepted: "Accepted",
482
+ acceptedNote: "This finding is in the baseline: it is known and does not count against the run.",
483
+ pageUnknown: "page: unknown",
484
+ alsoFineOn: "Also fine on",
485
+ findingLink: "Finding →",
486
+ consentMode: "Consent Mode",
487
+ consentTiming: (ms, before, v2, missing) => `Default set ${ms === null ? "on the first page" : `${Math.round(ms)} ms into the first page`}${before === null ? "" : before ? ", before the first Google hit" : ", after the first Google hit"} · v2 signals ${v2 ? "present" : `missing: ${missing.join(", ")}`}.`,
488
+ cellCookieless: "cookieless",
489
+ cellAdDenied: "ad storage denied",
490
+ cellWithCookies: "with cookies",
491
+ cellProblem: "problem:",
492
+ cellShouldNot: "should not have been sent",
493
+ timeCol: "Time",
494
+ hitsInStep: "Hits in this step",
495
+ blockedBy: (platform) => `blocked by the audit: answered locally, never reached ${platform}`,
496
+ problems: (n) => `${n} ${n === 1 ? "problem" : "problems"}`,
497
+ funnel: "Expected vs observed",
498
+ funnelLead: (observed, expected, ok) => `${observed} of ${expected} expected events fired, ${ok} with every required parameter.`,
499
+ conversion: "conversion",
500
+ sent: "sent",
501
+ notSent: "not sent",
502
+ page: "Page",
503
+ params: (list) => `without ${list.join(", ")}`,
504
+ paramsShort: "params",
505
+ parity: "Platform parity",
506
+ perPlatform: "Per platform",
507
+ agree: "agree",
508
+ mismatch: { count: "count differs", value: "value differs", currency: "currency differs", value_missing: "value on one side only" },
509
+ detailsCol: "Details",
510
+ needs: (list) => `needs ${list.join(", ")}`,
511
+ notExpected: "not expected",
512
+ conversions: "Conversion integrity",
513
+ conversionsLead: (passed, total) => `${passed} of ${total} purchases pass every check.`,
514
+ conversionChecks: { value: "Value", currency: "Currency", transactionId: "Order ID", items: "Items", duplicate: "Not duplicated", parity: "Parity", eventId: "Event ID" },
515
+ orderId: "Order",
516
+ value: "Value",
517
+ pass: "pass",
518
+ fail: "fail",
519
+ notApplicable: "not applicable",
520
+ checkedNotSeen: "Checked, not seen",
521
+ neverSeen: (list) => `Checked but never seen: ${list}. No request reached them on any page.`,
522
+ timing: "Timing",
523
+ duration: "Duration",
524
+ medianFirstHit: "Median first hit",
525
+ slowestHit: "Slowest hit",
526
+ planChanges: "Plan changes",
527
+
528
+ // ---- html.shell: top bar, rail, section tools and footer of src/html/shell.js ----
529
+ shell: {
530
+ skip: "Skip to the report",
531
+ eyebrow: "Tracking audit",
532
+ heading: (site) => `Tracking audit of ${site}`,
533
+ railLabel: "Report sections",
534
+ sections: { summary: "Summary", findings: "Findings", journeys: "Journeys", consent: "Consent", coverage: "Coverage", inventory: "Inventory", run: "Run" },
535
+ railTip: (label, token) => `${label} · ${token}`,
536
+ collapse: "Collapse sidebar",
537
+ expand: "Expand sidebar",
538
+ expandAll: "Expand all",
539
+ collapseAll: "Collapse all",
540
+ clear: "Clear",
541
+ filterCount: (shown, total) => `${shown} of ${total}`,
542
+ score: "Score",
543
+ outOf: "/100",
544
+ scoreLabel: (score, verdict) => `Score ${score} of 100, verdict ${verdict}`,
545
+ noScore: (verdict) => `No score, verdict ${verdict}`,
546
+ themeAuto: "Theme: follows your system",
547
+ themeLight: "Theme: light",
548
+ themeDark: "Theme: dark",
549
+ },
550
+ // ---- end of html.shell ----
551
+
552
+ // ---- html.summary: strings of src/html/summary.js, edited only by its builder ----
553
+ summary: {
554
+ about: "About this run",
555
+ events: "Events checked",
556
+ noPlan: "No measurement plan given",
557
+ plannedSeen: (planned, seen) => `${planned} <small>planned</small> · ${seen} <small>seen</small>`,
558
+ withParams: (percent) => `${percent}% with required parameters`,
559
+ seen: "seen",
560
+ notSeen: "not seen",
561
+ seenOf: (seen, total) => `${seen} of ${total} seen`,
562
+ noRuns: "No journey ran",
563
+ runState: { completed: "completed", incomplete: "incomplete" },
564
+ steps: (n) => `${n} ${n === 1 ? "step" : "steps"}`,
565
+ hits: (n) => `${n} ${n === 1 ? "hit" : "hits"}`,
566
+ problems: (n) => `${n} ${n === 1 ? "problem" : "problems"}`,
567
+ completedOf: (done, total) => `${done} of ${total} completed`,
568
+ stoppedAt: (step) => `stopped at step ${step}`,
569
+ // chip is HTML (the consent state chip, with its leading space); range is "27–53" or null when no timing was seen.
570
+ defaultAt: (chip, range) => `Default${chip}${range === null ? "" : ` <small>at ${range} ms</small>`}`,
571
+ noDefault: "No default seen",
572
+ mixed: "mixed",
573
+ noBanner: "No banner found",
574
+ bannerNotReached: "Banner not reached",
575
+ scripted: "Scripted, no model",
576
+ deterministic: "Deterministic checks only",
577
+ explains: "Explains causes and fixes",
578
+ seconds: (s) => `${s} s`,
579
+ runs: (n) => `${n} ${n === 1 ? "run" : "runs"}`,
580
+ more: (n) => `${n} more`,
581
+ moreInFindings: (n) => `${n} more in Findings`,
582
+ // The score's cost lines; event and params arrive as HTML (<code>).
583
+ cost: {
584
+ missingParams: (event, params) => `${event} sent without ${params}`,
585
+ missingParamsAny: (event) => `${event} sent without its required parameters`,
586
+ beforeConsent: (event) => `${event} sent before consent`,
587
+ afterReject: (event) => `${event} sent after reject all`,
588
+ duplicate: (event, n) => (n ? `${event} sent ${n} times per click` : `${event} sent more than once per click`),
589
+ missingEvent: (event) => `${event} never sent`,
590
+ stopped: (step, runs) => (step === null ? "Journeys stopped early" : `${runs === 1 ? "Journey" : "Journeys"} stopped at step ${step}`),
591
+ noConsentDefault: "No Consent Mode default",
592
+ },
593
+ },
594
+ // ---- end of html.summary ----
595
+
596
+ // ---- html.findings: strings of src/html/findings.js, edited only by its builder ----
597
+ findings: {
598
+ severity: "Severity",
599
+ filterLabel: "Filter findings",
600
+ search: "Search",
601
+ searchLabel: "Search findings: event, parameter, page",
602
+ filters: "Filters",
603
+ groups: { failed: "Failed", unverified: "Unverified", passed: "Passed" },
604
+ empty: "No finding matches these filters.",
605
+ details: (title) => `${title}: details`,
606
+ runs: (n) => `${n} runs`,
607
+ showWhere: "Show where",
608
+ // Short row titles, at most eight words; event and parameter names are quoted and shown as code.
609
+ title: {
610
+ missing_params: (event, params) => `${q(event)} was sent without ${andList(sortParams(params).map(q), "and")}`,
611
+ duplicate: (event, n) => `${q(event)} was sent ${n} times per action`,
612
+ before_consent: (event) => `${q(event)} was sent before the consent choice`,
613
+ after_reject: (event) => `${q(event)} was still sent after Reject all`,
614
+ missing_event: (event) => `${q(event)} was not sent`,
615
+ consent_default_missing: () => "No Consent Mode default was set",
616
+ journey_incomplete: (step) => (step === null ? "The journey did not finish" : `The journey stopped at step ${step}`),
617
+ },
618
+ notReached: "not reached",
619
+ passed: {
620
+ campaign: "Campaign parameters survived the redirect",
621
+ coverage: (done, total, percent) => `${done} of ${total} planned events complete${percent === null ? "" : ` (${percent}%)`}`,
622
+ consentDefault: (onTime) => (onTime ? "Consent Mode default set before the first Google hit" : "Consent Mode default set"),
623
+ conversion: (event, amount) => `${q(event)} carried ${amount}`,
624
+ },
625
+ fineOn: (place, amount) => `Fine on ${place}${amount ? `: ${amount}` : ""}`,
626
+ pushLacks: (params) => `The dataLayer push already lacks ${andList(sortParams(params).map(q), "and")}.`,
627
+ pushCarries: "The dataLayer push carries them; the tag drops them.",
628
+ pushRaw: "dataLayer push",
629
+ perAction: (n) => `${n} ${n === 1 ? "time" : "times"} per action`,
630
+ noSignal: "no signal",
631
+ stillSent: "still sent",
632
+ stopped: "stopped",
633
+ stoppedAt: "Stopped at",
634
+ error: "Error",
635
+ defaultIn: (present, total) => `default in ${present} of ${total} runs`,
636
+ replay: "Replay",
637
+ ai: "The model's explanation",
638
+ noModel: "No model explanation in this run.",
639
+ },
640
+ // ---- end of html.findings ----
641
+
642
+ // ---- html.journeys: strings of src/html/journeys.js, edited only by its builder ----
643
+ journeys: {
644
+ steps: "Steps",
645
+ pickViewport: "Viewport",
646
+ dataLayer: "Data Layer",
647
+ startStep: "Before the first step",
648
+ stats: (steps, hits, secs) => `${steps} ${steps === 1 ? "step" : "steps"} · ${hits} ${hits === 1 ? "hit" : "hits"}${secs === null ? "" : ` · ${secs} s`}`,
649
+ noRuns: "No journey was run.",
650
+ nothing: "Nothing was sent in this step.",
651
+ noSignal: "no signal",
652
+ noSignalTitle: "No consent signal on this hit",
653
+ signals: { ad_storage: "ad", analytics_storage: "an", ad_user_data: "ud", ad_personalization: "ap" },
654
+ blocked: "blocked",
655
+ clear: "clear",
656
+ colUrl: "URL",
657
+ colId: "ID",
658
+ colName: "Name",
659
+ colPrice: "Price",
660
+ colQty: "Qty",
661
+ itemMore: "Item parameters",
662
+ hitsOf: (count, platform) => `${count} ${platform} ${count === 1 ? "hit" : "hits"}`,
663
+ stepOf: (i) => `Step ${i}`,
664
+ },
665
+ // ---- end of html.journeys ----
666
+
667
+ // ---- html.consent: strings of src/html/consent.js, edited only by its builder ----
668
+ consent: {
669
+ statusLabel: "Consent status per platform",
670
+ cols: { before: "No choice", accept: "Accept", reject: "Reject", analytics: "Analytics only" },
671
+ state: { denied: "denied", granted: "granted", none: "no signal" },
672
+ tally: (n, state) => `${n} · ${state}`,
673
+ scenarios: "Scenarios",
674
+ notSupported: "not supported",
675
+ sent: "Sent",
676
+ problems: (n) => `${n} ${n === 1 ? "problem" : "problems"}`,
677
+ noProblems: "No problems",
678
+ defaultFirst: "Default first",
679
+ defaultLate: "Default late",
680
+ noDefault: "No default",
681
+ v2: "v2",
682
+ v2Missing: "v2 missing",
683
+ timing: (lo, hi) => (lo === hi ? `${lo} ms` : `${lo}–${hi} ms`),
684
+ runsWith: (k, n) => (k === n ? `${n} ${n === 1 ? "run" : "runs"}` : `${k} of ${n} runs`),
685
+ resent: (n) => `Sent again on ${n} more page ${n === 1 ? "load" : "loads"}`,
686
+ },
687
+ // ---- end of html.consent ----
688
+
689
+ // ---- html.scope: strings of src/html/scope.js, edited only by its builder ----
690
+ scope: {
691
+ ofEvents: (n, total) => `${n} of ${total}`,
692
+ problemPoints: "Problem points",
693
+ barLabel: (ok, params, missing) => `${ok} complete, ${params} with a missing parameter, ${missing} not sent`,
694
+ missingParams: (n) => `${n} missing params`,
695
+ notSentCount: (n) => `${n} not sent`,
696
+ notCheckedCount: (n) => `${n} not checked`,
697
+ searchEvents: "Search events",
698
+ needs: "Needs",
699
+ keyOk: "sent with its parameters",
700
+ keyParams: "a required parameter is missing",
701
+ keyMissing: "never sent",
702
+ keyNotExpected: "not expected on this journey",
703
+ purchasesPass: (n, total) => `${n} of ${total} pass`,
704
+ purchasesFail: (n) => `${n} fail`,
705
+ seen: "Seen",
706
+ hosts: "Hosts",
707
+ noneSeen: "None seen",
708
+ requests: (n, num = n) => `${num} ${n === 1 ? "request" : "requests"}`,
709
+ pages: (n, num = n) => `${num} ${n === 1 ? "page" : "pages"}`,
710
+ cookies: (n, num = n) => `${num} ${n === 1 ? "cookie" : "cookies"}`,
711
+ generated: "Generated",
712
+ runs: "Runs",
713
+ files: "Files",
714
+ firstHit: (range) => `first hit ${range} ms`,
715
+ runCol: "Run",
716
+ },
717
+ // ---- end of html.scope ----
718
+ },
220
719
  };
221
720
 
222
721
  const tr = {
223
722
  name: "Turkish",
224
- severity: { broken: "Bozuk", risk: "Risk", warning: "Uyarı", info: "Bilgi", ok: "Tamam" },
723
+ severity: { broken: "Bozuk", risk: "Risk", warning: "Uyarı", info: "Bilgi", ok: "Sorunsuz" },
225
724
  ui: {
226
725
  title: (host) => `AI Tag Debugger raporu: ${host}`,
227
726
  generated: (at) => `${at} tarihinde ai-tag-debugger ile oluşturuldu · Analitik İşler`,
@@ -229,18 +728,11 @@ const tr = {
229
728
  eyebrow: "Yapay zekâ ile izleme denetimi",
230
729
  paramsShort: "parametre",
231
730
  noParameters: "Parametre yok.",
232
- pushMatchHint: "Bu adımda aynı olay adına sahip dataLayer gönderimleri.",
233
731
  noMatchingPush: "Bu adımda bu olay adıyla bir dataLayer gönderimi yok. Etiket bir sayfa görüntüleme, tıklama tetikleyicisi ya da sayfaya gömülü bir pikselden tetikleniyor olabilir.",
234
- otherInStep: "Diğer dataLayer gönderimleri ve ağ çağrıları",
235
732
  noHits: "İzleme olayı yok",
236
- hasProblems: "Sorun var",
237
- stepsShort: "adım",
238
- eventsShort: "olay",
239
- problemsShort: "sorun",
240
733
  findings: "Bulgular",
241
734
  what: "Ne",
242
735
  where: "Nerede",
243
- causeAndFix: "Neden ve çözüm",
244
736
  likelyCause: "Olası neden",
245
737
  gtmTags: "GTM container'ınızda",
246
738
  gtmNoTag: "Container dışa aktarımındaki hiçbir etiket bu olayı göndermiyor.",
@@ -249,17 +741,15 @@ const tr = {
249
741
  gtmBlockedBy: "Engelleyen:",
250
742
  gtmPaused: "Duraklatıldı",
251
743
  gtmConsent: (types) => `İzin gerekiyor: ${types}`,
744
+ gtmWhy: (tag, verdict, why) => `${q(tag)} etiketi: ${verdict} — ${why}`,
745
+ gtmVerdict: { true: "tetiklenmiş olmalıydı", false: "tetiklenmedi", unknown: "değerlendirilemedi" },
252
746
  fix: "Çözüm",
253
747
  captured: "Yakalanan",
254
748
  noProblems: "Sorun bulunamadı.",
255
- noExplanation: "Açıklama için --no-ai olmadan çalıştırın.",
256
- matchingPlan: "Plana uygun olanlar",
257
749
  timeline: "Yolculuklar ve olay zaman çizelgesi",
258
- timelineLead: "Her yolculuğun her adımı. Olaylar platforma göre gruplanır; bir olayı açınca parametrelerini, arkasındaki dataLayer gönderimini ve onu taşıyan ağ isteğini görürsünüz. Sorunlu adımlar açık gösterilir.",
259
750
  completed: "Tamamlandı",
260
751
  notCompleted: "Tamamlanmadı",
261
752
  step: "adım",
262
- trackingHits: "İzleme istekleri",
263
753
  dataLayer: "dataLayer gönderimleri",
264
754
  network: "Ağ çağrıları",
265
755
  platform: "Platform",
@@ -269,55 +759,39 @@ const tr = {
269
759
  method: "Metot",
270
760
  status: "Durum",
271
761
  vendor: "Sağlayıcı",
272
- blockedNote: "yerel olarak yanıtlandı, gönderilmedi",
273
762
  clickedThrough: "üstündeki bir öğeye rağmen tıklandı",
274
763
  generatedBy: "AI Tag Debugger ile oluşturuldu",
275
764
  nothing: "Yok",
276
- counts: (c) => `${c.broken} bozuk · ${c.risk} risk · ${c.warning} uyarı · ${c.info} bilgi · ${c.ok} tamam`,
765
+ counts: (c) => `${c.broken} bozuk · ${c.risk} risk · ${c.warning} uyarı · ${c.info} bilgi · ${c.ok} sorunsuz`,
766
+ accepted: "Kabul edildi",
767
+ acceptedLine: (n) => `Temel dosyada kabul edilen ${n} bulgu sayılmadı`,
277
768
  whereStep: (journey, viewport, step) => `${journey} (${viewport})${step ? `, "${step}" adımı` : ""}`,
278
769
  allGood: "Test edilen tüm yolculuklarda plandaki tüm olaylar doğru çalıştı.",
279
- tabEvents: "Olaylar",
280
- tabTimeline: "Zaman çizelgesi",
281
- timelineGtmEvent: "GTM olayı",
282
- timelineConsent: "İzin komutu",
283
- timelineNoEvent: "GTM olayı yok (yalnızca veri)",
284
- timelineNoHits: "Bu gönderimin ardından platform isteği gelmedi.",
285
- timelineOther: "dataLayer gönderimi olmayan istekler",
286
770
  bodyDropped: "İstek gövdesi saklanmadı: bu denetim, saklanan istek gövdeleri için 2 MB sınırına ulaştı.",
287
771
  tabNetwork: "Ağ istekleri",
288
- filterBy: "Kaynağa göre filtrele",
289
772
  all: "Tümü",
290
773
  items: "Ürünler",
291
774
  customItemParams: "Özel ürün parametreleri",
292
- itemsCount: (n) => `${n} ürün`,
293
775
  noItems: "ürün yok",
294
776
  serverSideTag: "sunucu tarafı",
295
777
  source: "Kaynak",
296
778
  request: "İstek",
297
- noRequest: "Eşleşen istek yok",
298
779
  noPushes: "Bu adımda dataLayer gönderimi yok.",
299
780
  noRequests: "Bu adımda seçilen sağlayıcılara istek yok.",
300
781
  eventParams: "Olay parametreleri",
301
782
  contextParams: "Oturum, cihaz, sayfa ve onay parametreleri",
302
783
  rawRequest: "Ham istek",
303
- rawJson: "Ham JSON",
304
784
  inBatch: (i, n) => `tek istekte ${n} olaydan ${i}.`,
305
785
  impact: "Etkisi",
306
786
  evidence: "Kanıt",
307
787
  fixSteps: "Çözüm",
308
788
  verify: "Nasıl doğrulanır",
309
789
  owners: { developer: "Yazılımcı", gtm: "Tag Manager", marketing: "Pazarlama" },
310
- ownerHint: { developer: "Site kodu ve dataLayer", gtm: "Etiketler, tetikleyiciler ve onay ayarları", marketing: "Ölçüm planı" },
311
790
  places: (n) => `${n} yer`,
312
791
  alsoCheck: "Bulgu listesinde olmayan, ama kontrol edilmesi gerekenler",
313
- providersChecked: "Kontrol edilen sağlayıcılar:",
314
- outOfScope: (list, n) => `Kapsam dışı: ${list} (${n} istek atlandı)`,
315
792
  skippedInStep: (list, n) => `Yalnızca seçilen sağlayıcıların istekleri gösterilir. Bu adımda kapsam dışı ${n} istek atlandı: ${list}.`,
316
- tabOverview: "Genel bakış",
317
793
  tabJourneys: "Yolculuklar",
318
- tabScope: "Kapsam ve plan",
319
794
  sections: "Rapor bölümleri",
320
- headerMeta: (journeys, providers) => `${journeys} yolculuk · ${providers} sağlayıcı`,
321
795
  checkedAgainst: (id) => `${id} planına göre kontrol edildi`,
322
796
  planChangesLine: (pc) => (pc.same ? `Plan ${pc.previous} sürümünden bu yana değişmedi` : `Plan ${pc.previous} sürümünden bu yana değişti: ${pc.added.length} eklendi, ${pc.removed.length} çıkarıldı, ${pc.changed.length} değişti`),
323
797
  planVsSite: "Plan ve site",
@@ -329,17 +803,11 @@ const tr = {
329
803
  noSavedRun: "Bu site için henüz kayıtlı çalıştırma yok. Önce `aitd run` çalıştırın; her çalıştırma bir öncekiyle karşılaştırılır.",
330
804
  routeChanged: (list) => `Yapay zeka siteyi son çalıştırmadan farklı bir yoldan gezdi: ${list}`,
331
805
  noPreviousRun: "Bu site için kayıtlı ilk çalıştırma bu, karşılaştırılacak bir şey henüz yok.",
332
- planVsSiteLead: "Plandaki her olay ve sitenin gerçekte gönderdiği",
333
806
  coverageLine: (c) => `Kapsam ${c.checkable} olaydan ${c.implemented}${c.percent === null ? "" : ` (%${c.percent})`}${c.not_checked ? `, ${c.not_checked} olay kontrol edilmedi` : ""}`,
334
807
  bucket: { implemented: "Uygulanmış", missing: "Eksik", missing_params: "Eksik parametre", wrong_values: "Hatalı değer", unplanned: "Planda yok", not_checked: "Kontrol edilmedi" },
335
808
  bucketCol: "Sonuç",
336
- summaryLead: "Yapay zekâ özeti, öncelik sırasıyla",
337
- noSummary: "Bulguların yapay zekâ özeti için --no-ai olmadan çalıştırın.",
338
- seeAllFindings: "Tüm bulguları gör →",
339
809
  journey: "Yolculuk",
340
810
  stepsCol: "Adım",
341
- problemsCol: "Sorun",
342
- scopeCard: "Kapsam",
343
811
  site: "Site",
344
812
  runCard: "Bu çalıştırma",
345
813
  runPlatforms: "Platformlar",
@@ -353,87 +821,585 @@ const tr = {
353
821
  runViewports: "Ekranlar",
354
822
  runLanguage: "Dil",
355
823
  runVersion: "Sürüm",
824
+ junitJourney: "yolculuk tamamlandı",
825
+ junitSite: "site",
356
826
  consentDefault: "Varsayılan",
357
827
  consentUpdate: "Güncelleme",
358
- consentLead: "Sitenin gönderdiği Consent Mode komutları, sırasıyla.",
359
- consentNone: "Site bu yolculukta Consent Mode komutu göndermedi.",
360
828
  scenarios: "Onay senaryoları",
361
- scenariosLead: (cmp) => `Başlangıç sayfası her onay seçimi için ayrı açıldı (${cmp === "banner" ? "banner butonlarıyla" : cmp} üzerinden yanıtlandı). Seçimden sonra etiket başına istek sayısı; kırmızılar gönderilmemeliydi.`,
362
829
  scenarioCols: { before: "Henüz seçim yok", accept: "Tümünü kabul", reject: "Tümünü reddet", analytics: "Yalnızca analitik" },
363
830
  scenarioNotRun: "Burada desteklenmiyor",
364
831
  scenarioTag: "Etiket",
365
832
  skippedLine: (list, n) => `Kapsam dışı ${n} istek atlandı: ${list}.`,
366
- findingHint: "Bir bulguyu açınca olası neden, kanıt ve çözüm adımları görünür.",
367
- spread: (j, s) => `${j} yolculuk${s ? ` · ${s} adım` : ""}`,
368
833
  chooseJourney: "Yolculuk seç",
369
- stepsNav: "Adımlar",
370
- stepMeta: (t, events, pushes, skipped) => `${t} · ${events} olay · ${pushes} dataLayer${skipped ? ` · kapsam dışı ${skipped} istek atlandı` : ""}`,
371
834
  noSteps: "Bu yolculukta adım yok.",
372
835
  planCoverage: "Ölçüm planı kapsamı",
373
- planCoverageLead: "Plandaki her olayın her yolculukta gönderilip gönderilmediği",
374
- coverageLegend: "✓ gönderildi · ✗ gönderilmedi · – bu yolculukta beklenmiyor",
375
836
  notChecked: "kontrol edilmedi",
376
837
  noPlan: "Ölçüm planı verilmediği için karşılaştırılacak bir şey yok.",
377
838
  providers: "Sağlayıcılar",
378
839
  inventoryTitle: "Etiket envanteri",
379
- inventoryLead: "Sitenin istek gönderdiği tüm sağlayıcılar ve hangi sayfalarda",
380
840
  vendorCol: "Sağlayıcı",
381
841
  partyCol: "Taraf",
382
842
  pagesCol: "Sayfalar",
383
843
  cookiesCol: "Çerezler",
384
844
  party: { first: "Birinci taraf", third: "Üçüncü taraf", both: "İkisi de" },
385
- morePages: (n) => `+${n} daha`,
386
845
  otherCookies: "Diğer çerezler",
387
846
  noInventory: "Sağlayıcıya giden istek kaydedilmedi.",
388
847
  provider: "Sağlayıcı",
389
- requestsCol: "İstek",
390
848
  checked: "Kontrol edildi",
391
849
  outOfScopeStatus: "Kapsam dışı",
392
850
  missing: "yok",
393
851
  requestTo: (p) => `${p} isteği`,
394
852
  by: "by",
395
853
  credit: (atd) => `Bu rapor açık kaynak ${atd} ile oluşturuldu.`,
396
- timelineLeadV2: "Her adımda üç sekme var: o adımda gönderilen olaylar, dataLayer gönderimleri ve ağ istekleri. Olayları ve istekleri kaynağa göre filtreleyebilirsiniz. Sorunlu adımlar açık gösterilir.",
854
+ history: {
855
+ noRuns: "Bu site için henüz kayıtlı çalıştırma yok. Önce `aitd run` çalıştırın; eğilim ikinci çalıştırmayla başlar.",
856
+ oneRun: (id) => `Tek kayıtlı çalıştırma var (${id}); eğilim bir sonrakiyle başlar.`,
857
+ direction: { improving: "iyileşiyor", worsening: "kötüleşiyor", flat: "değişmiyor" },
858
+ headline: (runs, first, last, dir, now, was, ago) => `${runs} çalıştırmada eğilim (${first} → ${last}): ${dir}. Şimdi ${now}; ${ago} çalıştırma önce ${was}.`,
859
+ headlineShort: (runs, first, last, dir, now) => `${runs} çalıştırmada eğilim (${first} → ${last}): ${dir}. Şimdi ${now}.`,
860
+ counts: (c) => `${c.broken} bozuk · ${c.risk} risk · ${c.warning} uyarı`,
861
+ openLongest: (items) => `En uzun süredir açık: ${items.join("; ")}.`,
862
+ since: (label, day, runs) => `${label} ${day} tarihinden beri (${runs} çalıştırma)`,
863
+ flapping: (items) => `Gelip giden (görüldü, kayboldu, yine görüldü): ${items.join("; ")}.`,
864
+ seenIn: (label, seen, runs) => `${label} ${runs} çalıştırmanın ${seen} tanesinde`,
865
+ places: (n) => ` ${n} yerde`,
866
+ unstable: (items) => `Her seferinde tamamlanmayan yolculuklar: ${items.join("; ")}.`,
867
+ stillOpen: (runs, items) => `${runs} çalıştırmadır hâlâ açık: ${items.join("; ")}.`,
868
+ sparkline: (spark, dir, runs, first, last) => `${spark} ${runs} çalıştırmada ${dir} (${first} → ${last})`,
869
+ fixedWas: "önceki durum",
870
+ },
871
+ md: {
872
+ title: (site, headline) => `${site}: ${headline}`,
873
+ meta: (at, journeys, platforms, version) => `${at} tarihinde oluşturuldu · ${journeys} yolculuk · ${platforms} · ai-tag-debugger ${version}`,
874
+ platformsSeen: (n, list) => (n ? `${n} platform görüldü (${list})` : "hiçbir platform görülmedi"),
875
+ htmlReport: "HTML rapor",
876
+ noParams: "istek hiç parametre taşımıyordu",
877
+ score: (score) => `veri kalitesi puanı ${score}/100`,
878
+ headline: {
879
+ fail_broken: (what, scope) => `${what} bozuk${scope}`,
880
+ fail_incomplete: (journey) => `${journey} yolculuğu tamamlanmadı, bu yüzden olayları kontrol edilemedi`,
881
+ attention_risk: (what, scope) => `${what} bir onay veya veri riski taşıyor${scope}`,
882
+ attention_warning: (what, scope) => `${what} dikkat gerektiriyor${scope}`,
883
+ attention_no_hits: () => "hiç izleme isteği yakalanmadı",
884
+ attention_nothing_tested: () => "hiçbir şey test edilmedi",
885
+ pass: (events, journeys) => (events ? `plandaki ${events} olayın tümü ${journeys} yolculukta doğru çalıştı` : `${journeys} yolculukta tüm kontroller geçti`),
886
+ },
887
+ tracking: (event) => `${event} izlemesi`,
888
+ setup: (check) => `${check} kontrolü`,
889
+ onViewport: (viewport) => ` (${viewport})`,
890
+ onJourney: (journey) => ` (${journey})`,
891
+ everywhere: " (her yolculukta)",
892
+ aiSummary: "Yapay zekâ özeti",
893
+ stillOpen: "Hâlâ açık",
894
+ priorities: "Öncelikler",
895
+ nothingToDo: (owner) => `${owner}: yapılacak bir şey yok.`,
896
+ allJourneys: (n) => `${n} yolculuğun tümü`,
897
+ step: (n) => `adım ${n}`,
898
+ more: (n) => `+${n} daha`,
899
+ also: "Ayrıca",
900
+ owner: "Sorumlu",
901
+ link: "Bağlantı",
902
+ alsoFineOn: "Şurada sorunsuz",
903
+ consent: "Onay",
904
+ consentMode: (present, total, onTime, v2) => `Consent Mode varsayılanı ${total} yolculuğun ${present} tanesinde ayarlandı${onTime === false ? ", ilk Google isteğinden sonra" : onTime ? ", ilk Google isteğinden önce" : ""}; v2 sinyalleri ${v2 ? "var" : "eksik"}.`,
905
+ consentModeNone: "Hiçbir yolculukta Consent Mode varsayılan komutu görülmedi.",
906
+ consentNotChecked: "Bu çalıştırmada onay kontrol edilmedi.",
907
+ cell: { ok: "✓", bad: "✗", cookieless: "çerezsiz", beforeConsent: "onaydan önce", shouldNotSend: "gönderilmemeliydi", adDenied: "reklam depolaması reddedilmiş", notRun: "–" },
908
+ consentLegend: "✓ izinli · ✗ gönderilmemeliydi · \"çerezsiz\" = depolama reddedilmişken Consent Mode pingi · – = bu seçim çalıştırılmadı",
909
+ whatPassed: "Sorunsuz olanlar",
910
+ coverage: (implemented, total, percent) => `Plan kapsamı: plandaki ${total} olaydan ${implemented} tanesi uygulanmış${percent === null ? "" : ` (%${percent})`}`,
911
+ purchaseOk: (platform, place, value, currency, id) => `${platform} satın alma değeri ve para birimi: ${place} (${value} ${currency}${id ? `, ${id}` : ""})`,
912
+ parityOk: (platforms, place) => `Satın alma platformlar arasında tutarlı: ${platforms} ${place} için aynı değeri bildirdi`,
913
+ noPii: "Kişisel veri: hiçbir istekte düz metin e-posta veya kullanıcı verisi yok.",
914
+ viewport: "Görünüm",
915
+ hits: "İstek",
916
+ yes: "evet",
917
+ no: "hayır",
918
+ flaggedSteps: "Sorunlu adımlar",
919
+ timelineNote: "Her adımın tüm istekleri, dataLayer gönderimleri ve ağ çağrıları timeline.csv dosyasında.",
920
+ moreIn: (n, file) => `+${n} daha ${file} dosyasında`,
921
+ notChecked: "Kontrol edilmeyenler",
922
+ noPlan: "Plandaki olaylar: ölçüm planı verilmedi (yapılandırmada `plan`).",
923
+ noGtm: "GTM etiket adları: container dışa aktarımı verilmedi (yapılandırmada `gtm_container`).",
924
+ noAnalyticsOnly: "Yalnızca analitik onayı: banner'da böyle bir seçenek yok.",
925
+ noScenarios: "Onay senaryoları: çalıştırılmadı (yanıtlanabilir banner yok ya da tekrar oynatma).",
926
+ platformsSilent: (list, n) => `${list} kapsamdaydı ama site ${n === 1 ? "ona" : "onlara"} hiçbir şey göndermedi.`,
927
+ trend: "Eğilim",
928
+ },
929
+ cli: {
930
+ verdict: (status, headline, score) => `Sonuç: ${status} · ${headline}${score === null ? "" : ` · puan ${score}/100`}`,
931
+ runCol: "Çalıştırma",
932
+ scoreCol: "Puan",
933
+ completeCol: "Tamamlandı",
934
+ historyNone: "Bu site için henüz kayıtlı çalıştırma yok.",
935
+ },
397
936
  },
398
937
  detail: {
399
938
  journeyIncomplete: (note) => `Yolculuk hedefine ulaşmadı${note ? `: ${note}` : "."} Bu noktadan sonraki olaylar kontrol edilemedi.`,
400
- missingEvent: (platform, name) => `Beklenen ${platform} olayı ${q(name)} bu yolculuk boyunca hiç gönderilmedi.`,
401
- missingEventEarly: (name) => `${q(name)} görülmedi, ancak yolculuk erken durduğu için bu bir izleme hatası olmayabilir.`,
402
- missingParams: (name, params) => `${q(name)} olayı ${params.map(q).join(", ")} ${params.length > 1 ? "parametreleri" : "parametresi"} olmadan gönderildi.`,
403
- duplicate: (name, n) => `${q(name)} tek bir işlemde ${n} kez gönderildi; bu da sayıları ve dönüşümleri şişirir.`,
404
- purchaseValueZero: (platform, name, v) => `${platform} ${q(name)} ${q(v)} değeriyle gönderildi. 0 veya daha düşük değerli bir satın alma gelire eklenmez ve reklam platformları teklifleri buna göre optimize edemez.`,
405
- purchaseValueNaN: (platform, name, v) => `${platform} ${q(name)} sayı olmayan ${q(v)} değeriyle gönderildi, bu yüzden gelir kayboluyor. Para birimi işareti veya binlik ayırıcı olmadan 49.90 gibi düz bir sayı gönderin.`,
406
- purchaseCurrency: (platform, name, c) => `${platform} ${q(name)} ${q(c)} para birimiyle gönderildi; bu, büyük harfli 3 harfli bir ISO 4217 kodu değil (USD, EUR veya TRY gibi). Platformlar geliri atabilir veya yanlış çevirebilir.`,
407
- purchaseItemsEmpty: (platform, name) => `${platform} ${q(name)} ürün listesi boş gönderildi, bu yüzden ürün raporları bu siparişten veri alamaz.`,
408
- purchaseMismatch: (id, diffs) => `GA4 ve Meta ${id ? `${q(id)} siparişini` : "satın almayı"} farklı bildirdi: ${diffs.map(([f, g, m]) => `${f === "value" ? "değer" : "para birimi"} GA4'te ${q(g)}, Meta'da ${q(m)}`).join("; ")}. Etiketlerden biri yanlış veriyi okuyor.`,
409
- serverSide: (platform, server, browser, unseen = []) => `${platform}: ${server.map(q).join(", ")} sitenin kendi alan adındaki bir sunucu konteynerine gönderildi (sunucu tarafı etiketleme)${browser.length ? `; ${browser.map(q).join(", ")} doğrudan tarayıcıdan gitti` : ""}.${unseen.length ? ` ${unseen.join(" ve ")} olaylarının sunucu tarafı kopyaları tarayıcıdan geçmez, bu yüzden burada görünmez.` : ""}`,
410
- serverEndpoint: (hosts) => `${hosts.map(q).join(", ")} adresinde bir sunucu konteyneri yanıt veriyor (sunucu tarafı GTM veya benzeri bir kurulum). Reklam platformlarına ilettiği olaylar tarayıcıdan geçmez, bu yüzden burada görünmez.`,
411
- eventIdReused: (platform, id, names, n) => `${platform} olay kimliği ${q(id)} ${n} farklı olay için kullanıldı (${names.map(q).join(", ")}). ${platform} ilkini tutar, diğerlerini kopya sayıp atar. Her olaya kendi kimliğini verin.`,
412
- eventIdNotForwarded: (name, ga4Name) => `Meta ${q(name)} olay kimliği taşıyor, ancak sunucu konteynerine giden GA4 ${q(ga4Name)} isteğinde event_id yok. Sunucu konteyneri Conversions API olayını bu istekten üretiyorsa Meta onu piksel olayıyla eşleştiremez ve iki kez sayar. Aynı kimliği GA4 olayına event_id parametresi olarak ekleyin.`,
413
- eventIdMismatch: (name, metaId, ga4Name, ga4Id) => `Meta ${q(name)} olay kimliği ${q(metaId)}, ancak sunucu konteynerine giden GA4 ${q(ga4Name)} isteği event_id ${q(ga4Id)} taşıyor. Bu istekten üretilen Conversions API olayı piksel olayıyla eşleşmez, Meta onu iki kez sayar. İkisi için tek bir kimlik kullanın.`,
414
- dedupIdMissing: (platform, name) => `${platform} ${q(name)} tarayıcıdan olay kimliği olmadan gönderildi; sitede sunucu tarafı bir kurulum görüldü. Sunucu da bu olayı gönderiyorsa ${platform} ikisini eşleştiremez ve iki kez sayar.`,
939
+ missingEvent: (platform, name) => `${label(platform)} ${q(name)} bu yolculukta gönderilmedi; oysa yolculuk hedefine ulaştı. ${label(platform)} bu olay için hiçbir veri almadı.`,
940
+ missingEventEarly: (name) => `${q(name)} görülmedi; ancak yolculuk erken durduğu için bu olay eksik değil, doğrulanmamış sayılır.`,
941
+ missingParams: (name, params) => `${q(name)} olayı ${andList(sortParams(params).map(q), "ve")} ${params.length > 1 ? "parametreleri" : "parametresi"} olmadan gönderildi.`,
942
+ duplicate: (name, n) => `${q(name)} tek bir işlemde ${n} kez gönderildi. Her gerçek olay ${n} kez sayılıyor.`,
943
+ purchaseValueZero: (platform, name, v) => `${label(platform)} ${q(name)} ${q(v)} değeriyle gönderildi. 0 veya daha düşük değerli bir satın alma gelire eklenmez; reklam platformları teklifleri buna göre optimize edemez.`,
944
+ purchaseValueNaN: (platform, name, v) => `${label(platform)} ${q(name)} sayı olmayan ${q(v)} değeriyle gönderildi, bu yüzden gelir kayboluyor. Para birimi işareti veya binlik ayırıcı olmadan 49.90 gibi düz bir sayı gönderin.`,
945
+ purchaseCurrency: (platform, name, c) => `${label(platform)} ${q(name)} ${q(c)} para birimiyle gönderildi; bu, büyük harfli 3 harfli bir ISO 4217 kodu değil (USD, EUR veya TRY gibi). Platformlar geliri düşürür ya da yanlış kurdan çevirir.`,
946
+ purchaseItemsEmpty: (platform, name) => `${label(platform)} ${q(name)} ürün listesi boş gönderildi, bu yüzden ürün raporları bu siparişten veri alamaz.`,
947
+ purchaseMismatch: (id, diffs) => `GA4 ve Meta ${id ? `${q(id)} siparişini` : "satın almayı"} farklı bildirdi: ${diffs.map(([f, g, m]) => `${f === "value" ? "değer" : "para birimi"} GA4'te ${q(g)}, Meta'da ${q(m)}`).join("; ")}. Etiketlerden biri yanlış sayıyı okuyor; Google ve Meta farklı gelir üzerinden teklif veriyor.`,
948
+ serverSide: (platform, server, browser, unseen = []) => `${label(platform)}: ${server.map(q).join(", ")} sitenin kendi alan adındaki bir sunucu container'ına gönderildi (sunucu tarafı etiketleme)${browser.length ? `; ${browser.map(q).join(", ")} doğrudan tarayıcıdan gitti` : ""}.${unseen.length ? ` ${unseen.join(" ve ")} olaylarının sunucu tarafı kopyaları tarayıcıdan geçmez, bu yüzden burada görünmez.` : ""}`,
949
+ serverEndpoint: (hosts) => `${hosts.map(q).join(", ")} adresinde bir sunucu container'ı yanıt veriyor (sunucu tarafı GTM veya benzeri bir kurulum). Reklam platformlarına ilettiği olaylar tarayıcıdan geçmez, bu yüzden burada görünmez.`,
950
+ eventIdReused: (platform, id, names, n) => `${label(platform)} olay kimliği ${q(id)} ${n} farklı olay için kullanıldı (${names.map(q).join(", ")}). ${label(platform)} ilkini tutar, diğerlerini kopya sayıp atar. Her olaya kendi kimliğini verin.`,
951
+ eventIdNotForwarded: (name, ga4Name) => `Meta ${q(name)} olay kimliği taşıyor, ancak sunucu container'ına giden GA4 ${q(ga4Name)} isteğinde event_id yok. Sunucu container'ı Conversions API olayını bu istekten üretiyorsa Meta onu piksel olayıyla eşleştiremez ve iki kez sayar. Aynı kimliği GA4 olayına event_id parametresi olarak ekleyin.`,
952
+ eventIdMismatch: (name, metaId, ga4Name, ga4Id) => `Meta ${q(name)} olay kimliği ${q(metaId)}, ancak sunucu container'ına giden GA4 ${q(ga4Name)} isteği event_id ${q(ga4Id)} taşıyor. Bu istekten üretilen Conversions API olayı piksel olayıyla eşleşmez, Meta onu iki kez sayar. İkisi için tek bir kimlik kullanın.`,
953
+ dedupIdMissing: (platform, name) => `${label(platform)} ${q(name)} tarayıcıdan olay kimliği olmadan gönderildi; sitede sunucu tarafı bir kurulum görüldü. Sunucu da bu olayı gönderiyorsa ${label(platform)} ikisini eşleştiremez ve iki kez sayar.`,
415
954
  campaignLost: (names, finalUrl) => `Açılış adresi ${names.join(", ")} taşıyordu, ancak sayfa ${finalUrl} adresinde ${names.length === 1 ? "onsuz" : "onlarsız"} sonlandı. Bir yönlendirme ${names.length === 1 ? "parametreyi" : "parametreleri"} düşürdü, bu yüzden reklam tıklamalarının atfı kaybolur.`,
416
- campaignMissingInHits: (platform, name, names) => `Açılış sayfasındaki ${platform} ${q(name)} isteği ${names.join(", ")} olmadan bir sayfa adresi bildiriyor, oysa sayfada ${names.length === 1 ? "o" : "onlar"} hâlâ vardı. Ziyaret kampanyaya değil doğrudan trafiğe yazılabilir.`,
955
+ campaignMissingInHits: (platform, name, names) => `Açılış sayfasındaki ${label(platform)} ${q(name)} isteği ${names.join(", ")} olmadan bir sayfa adresi bildiriyor, oysa sayfada ${names.length === 1 ? "o" : "onlar"} hâlâ vardı. Ziyaret kampanyaya değil doğrudan trafiğe yazılabilir.`,
417
956
  campaignOk: (names) => `${names.join(", ")} açılış sayfasının yönlendirmelerinden geçti ve isteklere ulaştı.`,
418
957
  consentDefaultMissing: () => "dataLayer'da Google Consent Mode varsayılan komutu (gtag('consent', 'default', ...)) bulunamadı.",
419
958
  consentDefaultLate: () => "Consent Mode varsayılanı, ilk izleme isteği gönderildikten sonra ayarlandı.",
420
- beforeConsentNever: (platform, name) => `${platform} ${q(name)}, bu yolculukta hiç onay verilmemesine rağmen tetiklendi.`,
421
- beforeConsent: (platform, name) => `${platform} ${q(name)}, ziyaretçi çerezleri kabul etmeden önce tetiklendi.`,
959
+ beforeConsentNever: (platform, name) => `${label(platform)} ${q(name)}, ziyaretçi bu yolculukta hiç onay seçimi yapmadığı hâlde gönderildi. ${label(platform)}, onay vermemiş ziyaretçilerden veri alıyor.`,
960
+ beforeConsent: (platform, name) => `${label(platform)} ${q(name)}, ziyaretçi onay seçimini yapmadan önce gönderildi. ${label(platform)}, onay vermemiş ziyaretçilerden veri alıyor.`,
422
961
  consentV2Missing: (keys) => `Consent Mode varsayılanı ${keys.map(q).join(" ve ")} değerlerini ayarlamıyor. Consent Mode v2, AEA ve Birleşik Krallık'taki Google reklamları için bunları ister; yoksa reklam kişiselleştirme ve dönüşüm ölçümü durabilir.`,
423
- consentV2Update: (keys) => `Ziyaretçi kabul ettikten sonra Consent Mode güncellemesi ${keys.map(q).join(" ve ")} iznini vermedi. Reklam dönüşümleri ve yeniden pazarlama kapalı kalabilir.`,
962
+ consentV2Update: (keys) => `Ziyaretçi kabul ettikten sonra Consent Mode güncellemesi ${keys.map(q).join(" ve ")} iznini vermedi. Bu sinyaller reddedilmiş kaldı; onay veren ziyaretçiler için bile Google Ads dönüşümleri ve yeniden pazarlama kapalı kalıyor.`,
424
963
  rejectIgnored: () => "Ziyaretçi çerezleri reddettikten sonra bir Consent Mode güncellemesi izin verdi; reddet düğmesi işe yaramıyor.",
425
964
  rejectButtonFailed: () => "Reddet düğmesine tıklandıktan sonra çerez bandı hâlâ görünüyordu.",
426
- afterReject: (platform, name) => `${platform} ${q(name)}, ziyaretçi çerezleri reddettikten sonra da gönderildi.`,
965
+ afterReject: (platform, name) => `${label(platform)} ${q(name)}, ziyaretçi çerezleri reddettikten sonra da gönderildi.`,
427
966
  cookiesBeforeConsent: (names) => `Ziyaretçi kabul etmeden önce izleme çerezleri yazıldı: ${names.join(", ")}.`,
428
967
  cookiesNeverConsent: (names) => `Bu yolculukta hiç onay verilmemesine rağmen izleme çerezleri yazıldı: ${names.join(", ")}.`,
429
968
  cookiesAfterReject: (names) => `Ziyaretçi çerezleri reddettikten sonra izleme çerezleri yazıldı: ${names.join(", ")}.`,
430
- pii: (platform, name) => `${q(name)} ile birlikte ${platform} platformuna düz metin olarak bir e-posta adresi gönderildi.`,
969
+ pii: (platform, name) => `${q(name)} ile birlikte ${label(platform)} platformuna düz metin olarak bir e-posta adresi gönderildi.`,
431
970
  unplanned: (list) => `Ölçüm planında olmayan olaylar gönderildi: ${list.join(", ")}.`,
432
- piiUnhashed: (platform, name, fields) => `${q(name)}, ${platform} platformuna kullanıcı verisini karma (hash) yapmadan gönderdi (${fields.join(", ")}). Reklam platformları bu değerleri SHA-256 ile karma yapılmış bekler.`,
971
+ piiUnhashed: (platform, name, fields) => `${q(name)}, ${label(platform)} platformuna kullanıcı verisini karma (hash) yapmadan gönderdi (${fields.join(", ")}). Reklam platformları bu değerleri SHA-256 ile karma yapılmış bekler.`,
433
972
  piiDataLayer: (fields) => `dataLayer karma (hash) yapılmamış kullanıcı verisi içeriyor (${fields.join(", ")}). Bu, Google gelişmiş dönüşümleri için geçerli bir kurulum, ancak sayfadaki diğer tüm etiketler ve betikler de bu veriyi okuyabilir. Yalnızca kullanması gereken etiketlerin kullandığını kontrol edin.`,
434
- plannedOutOfScope: (list) => `Plandaki ${list.length} olay, sağlayıcısı seçilmediği için kontrol edilmedi: ${list.join(", ")}. Kontrol etmek için sağlayıcıyı --providers ile ekleyin.`,
973
+ plannedOutOfScope: (list) => `Plandaki ${list.length} olay, platformu bu çalıştırmada seçilmediği için kontrol edilmedi: ${list.join(", ")}.`,
435
974
  matches: (list) => `Plandaki ${list.length} olay plana uygun çalıştı: ${list.join(", ")}.`,
436
975
  },
976
+ // Bir GTM etiketinin neden çalışıp çalışmadığı, yakalanan dataLayer'dan okunur. Cümleler
977
+ // "X etiketi: tetiklenmedi — ..." kalıbının devamı olarak küçük harfle başlar.
978
+ gtm: {
979
+ paused: "etiket duraklatılmış",
980
+ noTrigger: "etiketin hiçbir tetikleyicisi yok",
981
+ missingTrigger: (trigger) => `${trigger} tetikleyicisi container dışa aktarımında bulunmuyor`,
982
+ noPush: (event) => `bu yolculukta ${q(event)} olayını taşıyan bir dataLayer gönderimi yok`,
983
+ noPageEvent: (trigger, event) => `${q(trigger)} tetikleyicisi GTM'nin ${event} olayını bekliyor, ancak bu olay dataLayer'a hiç gelmedi; container yüklenmemiş olabilir`,
984
+ pageTrigger: (trigger, kind) => `${q(trigger)} gibi ${kind} türü tetikleyicileri GTM dataLayer'dan değil, sayfanın içinde değerlendirir`,
985
+ unknownType: (trigger, type) => `${q(trigger)} tetikleyicisi ${type} türünde; bu tür dataLayer'dan değerlendirilemez`,
986
+ condition: (trigger, field, op, want, had, negate) => `${q(trigger)} tetikleyicisi ${field} için ${want} ${op}${negate ? " olmayan" : ""} bir değer bekliyor; gönderimde ise ${had} vardı`,
987
+ conditionUnknown: (trigger, variable) => `${q(trigger)} tetikleyicisi ${q(variable)} değişkenine bakıyor; bu değişken dataLayer'dan okunamaz`,
988
+ conditionType: (trigger, type) => `${q(trigger)} tetikleyicisinde ${type} türünde bir koşul var; bu koşul dataLayer'dan değerlendirilemez`,
989
+ ops: { EQUALS: "değerine eşit", CONTAINS: "içeren", STARTS_WITH: "ile başlayan", ENDS_WITH: "ile biten", MATCH_REGEX: "ile eşleşen", LESS: "değerinden küçük", LESS_OR_EQUALS: "veya daha küçük", GREATER: "değerinden büyük", GREATER_OR_EQUALS: "veya daha büyük" },
990
+ nothing: "değer yok",
991
+ triggerFired: (trigger, event) => `${q(trigger)} tetikleyicisi ${q(event)} gönderiminde çalıştı`,
992
+ blocked: (trigger) => `${q(trigger)} istisna tetikleyicisi tarafından engellendi`,
993
+ consentDenied: (type) => `${type} izni gerekiyor; olay tetiklendiğinde bu izin reddedilmiş durumdaydı`,
994
+ fired: "tetikleyici çalıştı ve izin verilmişti; etiketin kendisi veya yapılandırması (ölçüm kimliği, olay adı) hatalı olabilir",
995
+ },
996
+ consequence: {
997
+ missing_event: "Platform bu olayı hiç almadı; hunideki bu adım görünmez ve bir dönüşümse reklam platformu onu sayamaz, teklif de veremez.",
998
+ missing_params: "Olay sayılıyor ama zorunlu ayrıntıları eksik; gelir, para birimi veya ürünler raporlara hiç ulaşmıyor.",
999
+ duplicate: "Tek bir işlem birkaç kez sayılıyor; sayılar ve dönüşüm toplamları şişiyor, reklam platformları olmayan satışlara teklif veriyor.",
1000
+ purchase_value_invalid: "Satın alma değeri kullanılabilir bir sayı değil; gelir sıfır yazılıyor, ROAS raporları ve değere dayalı teklifler yanlış.",
1001
+ purchase_currency_invalid: "Para birimi platformların tanıdığı bir kod değil; gelir ya düşüyor ya da yanlış kurdan çevriliyor.",
1002
+ purchase_items_empty: "Satın almada ürün yok; ürün raporları, katalog akışları ve dinamik yeniden pazarlama hiç satış görmüyor.",
1003
+ purchase_mismatch: "Google ve Meta aynı sipariş için farklı gelir aldı; biri yanlış sayıyı raporluyor ve ona göre teklif veriyor.",
1004
+ campaign_lost_redirect: "Bir yönlendirme kampanya parametrelerini siliyor; ücretli tıklamalar doğrudan trafik sayılıyor, kampanyalar sattıklarının karşılığını alamıyor.",
1005
+ campaign_missing_in_hits: "Açılış sayfası kampanya parametrelerini korudu ama istek onları bildirmedi; ziyaret doğrudan trafiğe yazılıyor.",
1006
+ campaign_params: "Kampanya parametreleri açılış yönlendirmesinden geçip isteklere ulaştı; ücretli tıklamalar kendi kampanyalarına yazılıyor.",
1007
+ event_id_reused: "İki farklı olay tek bir olay kimliğini paylaşıyor; platform ilkini tutup diğerini sessizce atıyor, gerçek bir olay kayboluyor.",
1008
+ event_id_mismatch: "Bu olayın tarayıcı ve sunucu kopyaları farklı kimlik taşıyor; Meta ikisini eşleştiremiyor ve dönüşümü iki kez sayıyor.",
1009
+ event_id_not_forwarded: "Sunucu container'ını besleyen GA4 isteğinde olay kimliği yok; sunucu kopyası pikselle eşleşemiyor ve iki kez sayılıyor.",
1010
+ dedup_id_missing: "Olay tarayıcıdan olay kimliği olmadan çıktı ve site bir sunucu container'ı kullanıyor; sunucu da gönderirse iki kez sayılır.",
1011
+ server_side: "Bazı olaylar sitenin kendi sunucusundan geçiyor; bu rapor tarayıcının gönderdiğini görür, sunucunun ilettiğini değil.",
1012
+ server_endpoint: "Sitenin alan adında bir sunucu container'ı yanıt veriyor; reklam platformlarına ilettiği olaylar burada görünmez.",
1013
+ consent_default_missing: "Google etiketleri onay varsayılanı olmadan çalışıyor; her ziyaretçi sorulmadan onay vermiş gibi davranıyorlar.",
1014
+ consent_default_late: "İlk Google isteği onay varsayılanı ayarlanmadan çıktı; o istek ziyaretçinin seçimini yok saydı.",
1015
+ consent_v2_missing: "Onay varsayılanında v2 sinyalleri yok; AEA ve Birleşik Krallık trafiğinde Google reklam kişiselleştirmeyi ve dönüşüm ölçümünü durdurabilir.",
1016
+ consent_v2_update: "Ziyaretçi kabul ettikten sonra v2 sinyalleri reddedilmiş kaldı; onay verenler için bile Google Ads dönüşümleri ve yeniden pazarlama kapalı.",
1017
+ before_consent: "Olay ziyaretçi seçim yapmadan gönderildi; platform, onay vermemiş ziyaretçilerden veri alıyor.",
1018
+ after_reject: "Olay ziyaretçi Reddet'e tıkladıktan sonra da gönderildi; izleme açık bir redde rağmen sürüyor.",
1019
+ reject_ignored: "Reddetmenin ardından bir onay güncellemesi her şeye yine izin verdi; reddet düğmesi göstermelik.",
1020
+ reject_button_failed: "Reddet düğmesi hiçbir şey yapmadı ve banner açık kaldı; ziyaretçi izlemeyi reddedemiyor.",
1021
+ cookies_before_consent: "İzleme çerezleri ziyaretçi seçim yapmadan yazıldı; çerezler onay alınmadan kuruluyor.",
1022
+ cookies_after_reject: "İzleme çerezleri ziyaretçi reddettikten sonra yazıldı; red kararına uyulmuyor.",
1023
+ pii: "Kişisel veri siteden korunmasız çıktı: düz metin bir e-posta adresi veya reklam eşleştirme verisi artık platformun kayıtlarında.",
1024
+ pii_datalayer: "dataLayer karma yapılmamış kişisel veri tutuyor; sayfadaki her betik bunu okuyabilir, yalnızca ihtiyaç duyan etiket değil.",
1025
+ journey_incomplete: "Tarayıcı bu yolculuğu bitiremedi; o noktadan sonraki her olay, dönüşüm dâhil, doğrulanmamış durumda.",
1026
+ unplanned_events: "Planda olmayan olaylar gönderiliyor: ya plan güncel değil ya da etiketler kimsenin istemediği şeyler gönderiyor.",
1027
+ planned_out_of_scope: "Plandaki bazı olaylar bu çalıştırmanın kontrol etmediği platformlarda; durumları bilinmiyor.",
1028
+ matches_plan: "Plandaki bu olaylar, planın istediği her şeyle birlikte gönderildi.",
1029
+ },
1030
+ fix: {
1031
+ missing_event: "Bu olayın etiketinin var olduğunu, doğru tetikleyiciyle çalıştığını ve onay ya da bir istisna tarafından engellenmediğini kontrol edin; container dışa aktarımı verildiyse GTM bölümü etiketi adıyla gösterir. Sonra bu yolculuğu yeniden çalıştırın.",
1032
+ missing_params: "Eksik parametreleri bu sayfa ve ekranda olayın arkasındaki dataLayer gönderimine ekleyin ve etiketin onları okuduğundan emin olun. Doğrulamak için yolculuğu yeniden çalıştırın.",
1033
+ duplicate: "İkinci göndereni bulun: iki gönderim, iki etiket ya da iki kez çalışan bir tetikleyici. Birini kaldırın veya etikete olay başına bir kez çalışma koruması ekleyin.",
1034
+ purchase_value_invalid: "Değeri 49.90 gibi düz bir sayı olarak gönderin; para birimi işareti ve binlik ayırıcı olmasın, ücretli bir sipariş için asla 0 olmasın.",
1035
+ purchase_currency_invalid: "Para birimini sipariş verisinden, USD, EUR veya TRY gibi büyük harfli 3 harfli bir ISO 4217 kodu olarak gönderin.",
1036
+ purchase_items_empty: "Satın alma gönderimindeki ürün listesini siparişin kalemlerinden (id, ad, fiyat, adet) doldurun ve etikette eşleyin.",
1037
+ purchase_mismatch: "İki etiketin de aynı sipariş alanlarını okumasını sağlayın; genellikle biri ara toplamı ya da farklı bir para birimini okur. Sonra bir siparişi iki platformda da karşılaştırın.",
1038
+ campaign_lost_redirect: "Yönlendirmenin sorgu dizesini korumasını sağlayın ya da ücretli trafiği doğrudan son adrese indirin.",
1039
+ campaign_missing_in_hits: "Etiketin açılış adresini parametreleriyle birlikte eksiksiz bildirmesini sağlayın; bir sayfa URL'si geçersiz kılması ya da geç dolan bir page_location değişkeni olup olmadığına bakın.",
1040
+ campaign_params: "Düzeltilecek bir şey yok. Açılış sayfaları değiştiğinde yönlendirmeleri yeniden kontrol edin.",
1041
+ event_id_reused: "Her olay için yeni bir olay kimliği üretin; sayfa yüklemesi başına bir kez değil.",
1042
+ event_id_mismatch: "Piksel olayı ve sunucu kopyası için tek bir olay kimliği kullanın: aynı değeri iki etikete de verin.",
1043
+ event_id_not_forwarded: "Sunucu container'ının Conversions API olayına dönüştürdüğü GA4 olayına, pikselin kimliğini taşıyan bir event_id parametresi ekleyin.",
1044
+ dedup_id_missing: "Tarayıcı olayına bir olay kimliği ekleyin ve aynı kimliği sunucudan da gönderin; platform ikisini böyle eşleştirir.",
1045
+ server_side: "Tarayıcı tarafında düzeltilecek bir şey yok. Sunucunun ilettiği olayları platformun kendi test araçlarında doğrulayın.",
1046
+ server_endpoint: "Tarayıcı tarafında düzeltilecek bir şey yok. Sunucu container'ının neyi ilettiğini kendi önizleme modunda kontrol edin.",
1047
+ consent_default_missing: "Herhangi bir Google etiketi yüklenmeden önce, genellikle onay banner'ının betiğinde veya bir GTM şablonunda, bir Consent Mode varsayılanı ayarlayın (yasanın gerektirdiği yerde hepsi reddedilmiş).",
1048
+ consent_default_late: "Onay varsayılanını Google etiketinin önüne alın: gtm.js veya gtag.js yüklenmeden önce çalışmalı.",
1049
+ consent_v2_missing: "Onay varsayılanına ad_storage ve analytics_storage'ın yanına ad_user_data ve ad_personalization sinyallerini ekleyin.",
1050
+ consent_v2_update: "Banner'ın ziyaretçi kabul ettikten sonra gönderdiği onay güncellemesinde ad_user_data ve ad_personalization iznini verin.",
1051
+ before_consent: "Etikete bir onay kontrolü ekleyin (etiketin onay ayarları ya da ek bir onay tetikleyicisi) veya onu yalnızca banner seçiminden sonra çalıştırın.",
1052
+ after_reject: "Etiketin reddi dikkate almasını sağlayın: etikette bir onay kontrolü ya da onay reddedildiğinde devreye giren bir engelleme tetikleyicisi.",
1053
+ reject_ignored: "Banner'ın reddet işleyicisini düzeltin: her sinyali reddedilmiş olarak güncellesin ve sonrasında asla izin vermesin.",
1054
+ reject_button_failed: "Banner'ın reddet düğmesini düzeltin: banner'ı kapatmalı ve reddi saklamalı; tıklamada bir betik hatası olup olmadığına bakın.",
1055
+ cookies_before_consent: "Bu çerezleri yazan etiketleri yalnızca onaydan sonra yükleyin ya da ziyaretçi seçim yapana kadar çerezsiz modda çalıştırın.",
1056
+ cookies_after_reject: "Onay reddedildiğinde bu çerezleri yazan etiketleri engelleyin ve ziyaretçi reddettiğinde önceden yazılmış çerezleri silin.",
1057
+ pii: "E-postayı veya kullanıcı verisini istekten çıkarın ya da etiket göndermeden önce SHA-256 ile karma yapın; etiketin okuduğu URL'leri ve form alanlarını kontrol edin.",
1058
+ pii_datalayer: "Kullanıcı verisini dataLayer'a karma yapılmış olarak gönderin ya da yalnızca ihtiyaç duyan etiketle sınırlayıp ardından temizleyin.",
1059
+ journey_incomplete: "Tarayıcının neden durduğunu nottan okuyun (engellenen bir tıklama, bulunamayan bir öğe, bir hata), sayfayı ya da adımı düzeltin ve yolculuğu yeniden çalıştırın.",
1060
+ unplanned_events: "İstenen olayları ölçüm planına ekleyin ya da onları gönderen etiketleri kaldırın.",
1061
+ planned_out_of_scope: "Kontrol edilmeleri için platformlarını bir sonraki çalıştırmanın sağlayıcılarına ekleyin.",
1062
+ matches_plan: "Düzeltilecek bir şey yok.",
1063
+ },
1064
+ // Dil fark etmeksizin aynı: kategori bir etiket, çevirisi categoryLabel'da.
1065
+ category: {
1066
+ missing_event: "money",
1067
+ missing_params: "money",
1068
+ duplicate: "money",
1069
+ purchase_value_invalid: "money",
1070
+ purchase_currency_invalid: "money",
1071
+ purchase_items_empty: "money",
1072
+ purchase_mismatch: "money",
1073
+ campaign_lost_redirect: "money",
1074
+ campaign_missing_in_hits: "money",
1075
+ campaign_params: "money",
1076
+ event_id_reused: "money",
1077
+ event_id_mismatch: "money",
1078
+ event_id_not_forwarded: "money",
1079
+ dedup_id_missing: "money",
1080
+ server_side: "hygiene",
1081
+ server_endpoint: "hygiene",
1082
+ consent_default_missing: "legal",
1083
+ consent_default_late: "legal",
1084
+ consent_v2_missing: "legal",
1085
+ consent_v2_update: "money",
1086
+ before_consent: "legal",
1087
+ after_reject: "legal",
1088
+ reject_ignored: "legal",
1089
+ reject_button_failed: "legal",
1090
+ cookies_before_consent: "legal",
1091
+ cookies_after_reject: "legal",
1092
+ pii: "legal",
1093
+ pii_datalayer: "legal",
1094
+ journey_incomplete: "hygiene",
1095
+ unplanned_events: "hygiene",
1096
+ planned_out_of_scope: "hygiene",
1097
+ matches_plan: "hygiene",
1098
+ },
1099
+ categoryLabel: { money: "Para", legal: "Hukuki", hygiene: "Veri kalitesi" },
1100
+ verdict: {
1101
+ status: { pass: "Geçti", fail: "Kaldı", attention: "Dikkat gerekiyor" },
1102
+ headline: {
1103
+ fail_broken: (v) => {
1104
+ const c = countsOf(v);
1105
+ const risks = c.risk ? `; ${c.risk} onay veya kişisel veri riski Tag Manager'ın sorumluluğunda` : "";
1106
+ return `${c.broken} bozuk bulgu düzeltilmeden sayılara güvenilemez${risks}.`;
1107
+ },
1108
+ fail_incomplete: (v) => {
1109
+ const j = journeysOf(v);
1110
+ const where = j.total ? `${j.total} yolculuktan ${j.incomplete} tanesi tamamlanmadı` : "Bir yolculuk tamamlanmadı";
1111
+ return `${where}; durduğu noktadan sonraki olaylar, dönüşüm dâhil, doğrulanmadı.`;
1112
+ },
1113
+ attention_risk: (v) => {
1114
+ const c = countsOf(v);
1115
+ const warnings = c.warning ? `; ${c.warning} uyarı da sayıları bozuyor` : "";
1116
+ return `Bozuk bir şey yok, ancak ${c.risk} onay veya kişisel veri riskinin Tag Manager'da düzeltilmesi gerekiyor${warnings}.`;
1117
+ },
1118
+ attention_warning: (v) => {
1119
+ const c = countsOf(v);
1120
+ return `Bozuk bir şey yok, ancak ${c.warning} uyarı sayıları veya kapsamı bozuyor; düzeltilmesi gerekiyor.`;
1121
+ },
1122
+ attention_no_hits: (v) => {
1123
+ const j = journeysOf(v);
1124
+ return `${j.total ? `${j.total} yolculuk çalıştı` : "Yolculuklar çalıştı"}, ancak hiç izleme isteği yakalanmadı; bu yüzden hiçbir şey kontrol edilemedi.`;
1125
+ },
1126
+ attention_nothing_tested: () => "Hiç yolculuk çalışmadı ve hiçbir şey yakalanmadı; henüz değerlendirilecek bir şey yok.",
1127
+ pass: (v) => {
1128
+ const j = journeysOf(v);
1129
+ return `Plandaki tüm olaylar ${j.total ? `${j.total} yolculuğun tamamında` : "test edilen her yolculukta"} doğru çalıştı; düzeltilecek bir şey yok.`;
1130
+ },
1131
+ },
1132
+ },
1133
+ html: {
1134
+ skip: "Rapora atla",
1135
+ eyebrow: "İzleme denetimi",
1136
+ countsLabel: "Önem derecesine göre bulgular",
1137
+ themeAuto: "Tema: sistemi izler",
1138
+ themeLight: "Tema: açık",
1139
+ themeDark: "Tema: koyu",
1140
+ newTab: "yeni sekmede açılır",
1141
+ aiSummary: "Yapay zekâ özeti",
1142
+ platformsChecked: "Kontrol edilen platformlar",
1143
+ campaignCheck: "Kampanya kontrolü",
1144
+ consentModeSeen: "Consent Mode komutları görüldü",
1145
+ cmpBanner: "banner",
1146
+ scenariosRun: (n) => `${n} senaryo`,
1147
+ runsAllCompleted: (n) => `${n} çalıştırma, hepsi tamamlandı`,
1148
+ runsCompleted: (done, n) => `${n} çalıştırmadan ${done} tanesi tamamlandı`,
1149
+ moreInFindings: (n) => `Bulgular'da ${n} tane daha →`,
1150
+ working: "Çalışanlar",
1151
+ whatWasChecked: "Neler kontrol edildi",
1152
+ nothingVerified: "Bu çalıştırmada hiçbir şey doğrulanmadı.",
1153
+ allRuns: (n) => `${n} çalıştırmanın hepsi`,
1154
+ onlyOn: (places) => `yalnızca ${places.join(", ")}`,
1155
+ viewport: "Görünüm",
1156
+ hitsCol: "İstekler",
1157
+ trend: "Eğilim",
1158
+ trendDirection: { improving: "İyileşiyor", worsening: "Kötüleşiyor", flat: "Değişmedi" },
1159
+ trendSpan: (n, first, last) => `${n} çalıştırmada, ${first} – ${last}`,
1160
+ runIncomplete: "bir yolculuk tamamlanmadı",
1161
+ flapping: "Görüldü, gitti, geri geldi",
1162
+ seenIn: (n, total) => `${total} çalıştırmanın ${n} tanesinde görüldü`,
1163
+ openLongest: "En uzun süredir açık",
1164
+ openSince: (date, runs, places) => `${date} tarihinden beri açık · ${runs} çalıştırma · ${places} yer`,
1165
+ score: "Veri kalitesi puanı",
1166
+ noModel: "Bu çalıştırmada model açıklaması yok. Aşağıdaki kanıt, sorumlu ve doğrulama bağlantıları için gerekmiyor.",
1167
+ accepted: "Kabul edildi",
1168
+ acceptedNote: "Bu bulgu temel çizgide: biliniyor ve çalıştırmanın aleyhine sayılmıyor.",
1169
+ pageUnknown: "sayfa: bilinmiyor",
1170
+ alsoFineOn: "Şurada sorunsuz:",
1171
+ findingLink: "Bulgu →",
1172
+ consentMode: "Consent Mode",
1173
+ consentTiming: (ms, before, v2, missing) => `Varsayılan ${ms === null ? "ilk sayfada" : `ilk sayfanın ${Math.round(ms)}. milisaniyesinde`} ayarlandı${before === null ? "" : before ? ", ilk Google isteğinden önce" : ", ilk Google isteğinden sonra"} · v2 sinyalleri ${v2 ? "mevcut" : `eksik: ${missing.join(", ")}`}.`,
1174
+ cellCookieless: "cookieless",
1175
+ cellAdDenied: "ad storage reddedilmiş",
1176
+ cellWithCookies: "çerezlerle",
1177
+ cellProblem: "sorun:",
1178
+ cellShouldNot: "gönderilmemeliydi",
1179
+ timeCol: "Zaman",
1180
+ hitsInStep: "Bu adımdaki istekler",
1181
+ blockedBy: (platform) => `denetim engelledi: yerel olarak yanıtlandı, ${platform} platformuna hiç ulaşmadı`,
1182
+ problems: (n) => `${n} sorun`,
1183
+ funnel: "Beklenen ve gözlenen",
1184
+ funnelLead: (observed, expected, ok) => `Beklenen ${expected} olaydan ${observed} tanesi tetiklendi, ${ok} tanesi tüm zorunlu parametrelerle.`,
1185
+ conversion: "dönüşüm",
1186
+ sent: "gönderildi",
1187
+ notSent: "gönderilmedi",
1188
+ page: "Sayfa",
1189
+ params: (list) => `${list.join(", ")} olmadan`,
1190
+ paramsShort: "param.",
1191
+ parity: "Platform tutarlılığı",
1192
+ perPlatform: "Platforma göre",
1193
+ agree: "uyumlu",
1194
+ mismatch: { count: "sayı farklı", value: "değer farklı", currency: "para birimi farklı", value_missing: "değer yalnızca bir tarafta" },
1195
+ detailsCol: "Ayrıntılar",
1196
+ needs: (list) => `gerekli: ${list.join(", ")}`,
1197
+ notExpected: "beklenmiyor",
1198
+ conversions: "Dönüşüm bütünlüğü",
1199
+ conversionsLead: (passed, total) => `${total} satın almadan ${passed} tanesi tüm kontrolleri geçiyor.`,
1200
+ conversionChecks: { value: "Değer", currency: "Para birimi", transactionId: "Sipariş no", items: "Ürünler", duplicate: "Tekrarsız", parity: "Tutarlılık", eventId: "Olay kimliği" },
1201
+ orderId: "Sipariş",
1202
+ value: "Değer",
1203
+ pass: "geçti",
1204
+ fail: "başarısız",
1205
+ notApplicable: "uygulanamaz",
1206
+ checkedNotSeen: "Kontrol edildi, görülmedi",
1207
+ neverSeen: (list) => `Kontrol edildi ama hiç görülmedi: ${list}. Hiçbir sayfada onlara istek gitmedi.`,
1208
+ timing: "Zamanlama",
1209
+ duration: "Süre",
1210
+ medianFirstHit: "Ortanca ilk istek",
1211
+ slowestHit: "En yavaş istek",
1212
+ planChanges: "Plan değişiklikleri",
1213
+
1214
+ // ---- html.shell: top bar, rail, section tools and footer of src/html/shell.js ----
1215
+ shell: {
1216
+ skip: "Rapora atla",
1217
+ eyebrow: "İzleme denetimi",
1218
+ heading: (site) => `${site} izleme denetimi`,
1219
+ railLabel: "Rapor bölümleri",
1220
+ sections: { summary: "Özet", findings: "Bulgular", journeys: "Yolculuklar", consent: "Onay", coverage: "Kapsam", inventory: "Envanter", run: "Çalıştırma" },
1221
+ railTip: (label, token) => `${label} · ${token}`,
1222
+ collapse: "Kenar çubuğunu daralt",
1223
+ expand: "Kenar çubuğunu genişlet",
1224
+ expandAll: "Tümünü aç",
1225
+ collapseAll: "Tümünü kapat",
1226
+ clear: "Temizle",
1227
+ filterCount: (shown, total) => `${shown} / ${total} gösteriliyor`,
1228
+ score: "Puan",
1229
+ outOf: "/100",
1230
+ scoreLabel: (score, verdict) => `Puan 100 üzerinden ${score}, sonuç: ${verdict}`,
1231
+ noScore: (verdict) => `Puan yok, sonuç: ${verdict}`,
1232
+ themeAuto: "Tema: sistemi izler",
1233
+ themeLight: "Tema: açık",
1234
+ themeDark: "Tema: koyu",
1235
+ },
1236
+ // ---- end of html.shell ----
1237
+
1238
+ // ---- html.summary: strings of src/html/summary.js, edited only by its builder ----
1239
+ summary: {
1240
+ about: "Bu çalıştırma hakkında",
1241
+ events: "Kontrol edilen olaylar",
1242
+ noPlan: "Ölçüm planı verilmedi",
1243
+ plannedSeen: (planned, seen) => `${planned} <small>planlı</small> · ${seen} <small>görülen</small>`,
1244
+ withParams: (percent) => `%${percent} gerekli parametreleriyle`,
1245
+ seen: "görüldü",
1246
+ notSeen: "görülmedi",
1247
+ seenOf: (seen, total) => `Görülen: ${seen} / ${total}`,
1248
+ noRuns: "Hiç yolculuk çalışmadı",
1249
+ runState: { completed: "tamamlandı", incomplete: "tamamlanmadı" },
1250
+ steps: (n) => `${n} adım`,
1251
+ hits: (n) => `${n} istek`,
1252
+ problems: (n) => `${n} sorun`,
1253
+ completedOf: (done, total) => `${done}/${total} tamamlandı`,
1254
+ stoppedAt: (step) => `${step}. adımda durdu`,
1255
+ defaultAt: (chip, range) => `Varsayılan${chip}${range === null ? "" : ` <small>${range} ms'de</small>`}`,
1256
+ noDefault: "Varsayılan görülmedi",
1257
+ mixed: "karışık",
1258
+ noBanner: "Banner bulunamadı",
1259
+ bannerNotReached: "Banner'a ulaşılamadı",
1260
+ scripted: "Betikle, model olmadan",
1261
+ deterministic: "Yalnızca kural tabanlı kontroller",
1262
+ explains: "Nedenleri ve düzeltmeleri açıklar",
1263
+ seconds: (s) => `${s} sn`,
1264
+ runs: (n) => `${n} çalıştırma`,
1265
+ more: (n) => `${n} tane daha`,
1266
+ moreInFindings: (n) => `Bulgular'da ${n} tane daha`,
1267
+ cost: {
1268
+ missingParams: (event, params) => `${event} içinde ${params} yok`,
1269
+ missingParamsAny: (event) => `${event} içinde gerekli parametreler yok`,
1270
+ beforeConsent: (event) => `${event} onaydan önce gönderildi`,
1271
+ afterReject: (event) => `${event} tümü reddedildikten sonra gönderildi`,
1272
+ duplicate: (event, n) => (n ? `${event} tıklama başına ${n} kez gönderildi` : `${event} tıklama başına birden çok kez gönderildi`),
1273
+ missingEvent: (event) => `${event} hiç gönderilmedi`,
1274
+ stopped: (step, runs) => (step === null ? "Yolculuklar erken durdu" : `${runs === 1 ? "Yolculuk" : "Yolculuklar"} ${step}. adımda durdu`),
1275
+ noConsentDefault: "Consent Mode varsayılanı yok",
1276
+ },
1277
+ },
1278
+ // ---- end of html.summary ----
1279
+
1280
+ // ---- html.findings: strings of src/html/findings.js, edited only by its builder ----
1281
+ findings: {
1282
+ severity: "Önem",
1283
+ filterLabel: "Bulguları filtrele",
1284
+ search: "Ara",
1285
+ searchLabel: "Bulgularda ara: olay, parametre, sayfa",
1286
+ filters: "Filtreler",
1287
+ groups: { failed: "Başarısız", unverified: "Doğrulanmadı", passed: "Geçti" },
1288
+ empty: "Bu filtrelere uyan bulgu yok.",
1289
+ details: (title) => `${title}: ayrıntılar`,
1290
+ runs: (n) => `${n} çalıştırma`,
1291
+ showWhere: "Nerede olduğunu göster",
1292
+ // Kısa satır başlıkları, en fazla sekiz kelime; olay ve parametre adları tırnak içinde, kod olarak gösterilir.
1293
+ title: {
1294
+ missing_params: (event, params) => `${q(event)} olayı ${andList(sortParams(params).map(q), "ve")} olmadan gönderildi`,
1295
+ duplicate: (event, n) => `${q(event)} tek işlemde ${n} kez gönderildi`,
1296
+ before_consent: (event) => `${q(event)} onay seçiminden önce gönderildi`,
1297
+ after_reject: (event) => `${q(event)} "Tümünü reddet"ten sonra da gönderildi`,
1298
+ missing_event: (event) => `${q(event)} gönderilmedi`,
1299
+ consent_default_missing: () => "Consent Mode varsayılanı ayarlanmadı",
1300
+ journey_incomplete: (step) => (step === null ? "Yolculuk tamamlanmadı" : `Yolculuk ${step}. adımda durdu`),
1301
+ },
1302
+ notReached: "ulaşılmadı",
1303
+ passed: {
1304
+ campaign: "Kampanya parametreleri yönlendirmeden geçti",
1305
+ coverage: (done, total, percent) => `Planlanan ${total} olaydan ${done} tanesi eksiksiz${percent === null ? "" : ` (%${percent})`}`,
1306
+ consentDefault: (onTime) => (onTime ? "Consent Mode varsayılanı ilk Google isteğinden önce ayarlandı" : "Consent Mode varsayılanı ayarlandı"),
1307
+ conversion: (event, amount) => `${q(event)} ${amount} ile gönderildi`,
1308
+ },
1309
+ fineOn: (place, amount) => `${place} sorunsuz${amount ? `: ${amount}` : ""}`,
1310
+ pushLacks: (params) => `dataLayer gönderiminde ${andList(sortParams(params).map(q), "ve")} zaten yok.`,
1311
+ pushCarries: "dataLayer gönderimi bunları taşıyor; etiket onları düşürüyor.",
1312
+ pushRaw: "dataLayer gönderimi",
1313
+ perAction: (n) => `işlem başına ${n} kez`,
1314
+ noSignal: "sinyal yok",
1315
+ stillSent: "yine gönderildi",
1316
+ stopped: "durdu",
1317
+ stoppedAt: "Durduğu yer",
1318
+ error: "Hata",
1319
+ defaultIn: (present, total) => `${total} çalıştırmanın ${present} tanesinde varsayılan var`,
1320
+ replay: "Yeniden oynatma",
1321
+ ai: "Modelin açıklaması",
1322
+ noModel: "Bu çalıştırmada model açıklaması yok.",
1323
+ },
1324
+ // ---- end of html.findings ----
1325
+
1326
+ // ---- html.journeys: strings of src/html/journeys.js, edited only by its builder ----
1327
+ journeys: {
1328
+ steps: "Adımlar",
1329
+ pickViewport: "Görünüm",
1330
+ dataLayer: "Veri katmanı",
1331
+ startStep: "İlk adımdan önce",
1332
+ stats: (steps, hits, secs) => `${steps} adım · ${hits} istek${secs === null ? "" : ` · ${secs} sn`}`,
1333
+ noRuns: "Hiç yolculuk çalıştırılmadı.",
1334
+ nothing: "Bu adımda hiçbir şey gönderilmedi.",
1335
+ noSignal: "sinyal yok",
1336
+ noSignalTitle: "Bu istekte onay sinyali yok",
1337
+ signals: { ad_storage: "ad", analytics_storage: "an", ad_user_data: "ud", ad_personalization: "ap" },
1338
+ blocked: "engellendi",
1339
+ clear: "temizleme",
1340
+ colUrl: "URL",
1341
+ colId: "Kimlik",
1342
+ colName: "Ad",
1343
+ colPrice: "Fiyat",
1344
+ colQty: "Adet",
1345
+ itemMore: "Ürün parametreleri",
1346
+ hitsOf: (count, platform) => `${count} ${platform} isteği`,
1347
+ stepOf: (i) => `Adım ${i}`,
1348
+ },
1349
+ // ---- end of html.journeys ----
1350
+
1351
+ // ---- html.consent: strings of src/html/consent.js, edited only by its builder ----
1352
+ consent: {
1353
+ statusLabel: "Platform başına onay durumu",
1354
+ cols: { before: "Seçim yok", accept: "Kabul", reject: "Reddet", analytics: "Yalnızca analitik" },
1355
+ state: { denied: "reddedildi", granted: "verildi", none: "sinyal yok" },
1356
+ tally: (n, state) => `${n} · ${state}`,
1357
+ scenarios: "Senaryolar",
1358
+ notSupported: "desteklenmiyor",
1359
+ sent: "Gönderildi",
1360
+ problems: (n) => `${n} sorun`,
1361
+ noProblems: "Sorun yok",
1362
+ defaultFirst: "Varsayılan önce",
1363
+ defaultLate: "Varsayılan geç",
1364
+ noDefault: "Varsayılan yok",
1365
+ v2: "v2",
1366
+ v2Missing: "v2 eksik",
1367
+ timing: (lo, hi) => (lo === hi ? `${lo} ms` : `${lo}–${hi} ms`),
1368
+ runsWith: (k, n) => (k === n ? `${n} çalıştırma` : `${n} çalıştırmadan ${k} tanesi`),
1369
+ resent: (n) => `${n} sayfa yüklemesinde daha yeniden gönderildi`,
1370
+ },
1371
+ // ---- end of html.consent ----
1372
+
1373
+ // ---- html.scope: strings of src/html/scope.js, edited only by its builder ----
1374
+ scope: {
1375
+ ofEvents: (n, total) => `${total} olaydan ${n}`,
1376
+ problemPoints: "Sorun puanı",
1377
+ barLabel: (ok, params, missing) => `${ok} eksiksiz, ${params} olayda zorunlu parametre eksik, ${missing} olay gönderilmedi`,
1378
+ missingParams: (n) => `${n} olayda eksik parametre`,
1379
+ notSentCount: (n) => `${n} olay gönderilmedi`,
1380
+ notCheckedCount: (n) => `${n} olay kontrol edilmedi`,
1381
+ searchEvents: "Olay ara",
1382
+ needs: "Gerekenler",
1383
+ keyOk: "parametreleriyle gönderildi",
1384
+ keyParams: "zorunlu bir parametre eksik",
1385
+ keyMissing: "hiç gönderilmedi",
1386
+ keyNotExpected: "bu yolculukta beklenmiyor",
1387
+ purchasesPass: (n, total) => `${total} satın almadan ${n} geçti`,
1388
+ purchasesFail: (n) => `${n} başarısız`,
1389
+ seen: "Görüldü",
1390
+ hosts: "Alan adları",
1391
+ noneSeen: "Görülmedi",
1392
+ requests: (n, num = n) => `${num} istek`,
1393
+ pages: (n, num = n) => `${num} sayfa`,
1394
+ cookies: (n, num = n) => `${num} çerez`,
1395
+ generated: "Oluşturulma",
1396
+ runs: "Çalıştırmalar",
1397
+ files: "Dosyalar",
1398
+ firstHit: (range) => `ilk istek ${range} ms`,
1399
+ runCol: "Çalıştırma",
1400
+ },
1401
+ // ---- end of html.scope ----
1402
+ },
437
1403
  };
438
1404
 
439
1405
  export const LANGUAGES = { en, tr };