Show persistent for local-storage trackers

Local storage, IndexedDB, and cache storage have no expiry yet
persist until explicitly cleared, so a missing max-age should read
as "persistent", not "session" (the latter only fits cookies and
session storage, which end with the session or tab).

Thread the tracker type through humanizeSeconds (helpers) and
humanizeDuration (cookie-banner, with a localized persistent label)
and pass it at every console and banner call site. The consent
record query now selects trackerType so its duration column can
make the same distinction. This mirrors the Go HumanizedDuration
helper that already renders these types as persistent.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-19 10:35:19 +02:00
parent a4cc82441a
commit f1fc2dc0e0
7 changed files with 51 additions and 22 deletions

View File

@@ -52,8 +52,8 @@ export class ProboCookieList extends ProboElement {
if (!this.template) return;
const duration = cookie.max_age_seconds != null
? humanizeDuration(cookie.max_age_seconds, lang)
: humanizeDuration(0, lang);
? humanizeDuration(cookie.max_age_seconds, lang, cookie.tracker_type)
: humanizeDuration(0, lang, cookie.tracker_type);
const type = getTrackerTypeLabel(cookie.tracker_type);

View File

@@ -13,6 +13,16 @@
// PERFORMANCE OF THIS SOFTWARE.
import { interpolate } from "./i18n";
import type { TrackerType } from "./types";
// Tracker types whose data persists until explicitly cleared. When such a
// tracker has no max-age, its lifetime is "persistent" rather than "session"
// (cookies and session storage are cleared when the session/tab ends).
const PERSISTENT_TRACKER_TYPES: ReadonlySet<TrackerType> = new Set([
"LOCAL_STORAGE",
"INDEXED_DB",
"CACHE_STORAGE",
]);
interface DurationTexts {
[key: string]: string;
@@ -35,6 +45,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
duration_second_one: "{{count}} second",
duration_second_other: "{{count}} seconds",
duration_session: "session",
duration_persistent: "persistent",
},
fr: {
duration_year_one: "{{count}} an",
@@ -52,6 +63,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
duration_second_one: "{{count}} seconde",
duration_second_other: "{{count}} secondes",
duration_session: "session",
duration_persistent: "persistant",
},
de: {
duration_year_one: "{{count}} Jahr",
@@ -69,6 +81,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
duration_second_one: "{{count}} Sekunde",
duration_second_other: "{{count}} Sekunden",
duration_session: "Sitzung",
duration_persistent: "dauerhaft",
},
es: {
duration_year_one: "{{count}} año",
@@ -86,6 +99,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
duration_second_one: "{{count}} segundo",
duration_second_other: "{{count}} segundos",
duration_session: "sesión",
duration_persistent: "persistente",
},
};
@@ -107,9 +121,17 @@ const DURATION_UNITS: [number, string, number][] = [
[1, "duration_second", 0],
];
export function humanizeDuration(seconds: number, lang?: string): string {
export function humanizeDuration(
seconds: number,
lang?: string,
trackerType?: TrackerType,
): string {
const texts = getDurationTexts(lang);
if (seconds <= 0) return texts.duration_session;
if (seconds <= 0) {
return trackerType && PERSISTENT_TRACKER_TYPES.has(trackerType)
? texts.duration_persistent
: texts.duration_session;
}
let remaining = seconds;
const parts: string[] = [];