diff --git a/client/messages/cs.json b/client/messages/cs.json index 4e82b7fc4..fa79344e6 100644 --- a/client/messages/cs.json +++ b/client/messages/cs.json @@ -25,6 +25,7 @@ "+x2FLn": "přehrávání", "+zImMB": "", "/+XPOf": "", + "/0J7Xq": "Nepodařilo se zpracovat obrázek ikony", "/0TOL5": "Částka", "/0tjRq": "", "/20q2D": "Tvé heslo bylo úspěšně obnoveno.", @@ -49,6 +50,7 @@ "/clOBU": "Týdně", "/dxF+k": "Heslo musí mít alespoň 8 znaků", "/eULaT": "Výkon podle stránky", + "/g8hj9": "ASN Type", "/jyLDY": "", "/kZg9i": "Kliknutí na tlačítko", "/liMUf": "Trychtýř úspěšně smazán", @@ -68,6 +70,7 @@ "0VV/sK": "", "0WpQtp": "Nepodařilo se načíst data o retenci", "0cEOKu": "", + "0cOykX": "ASN Org", "0eYAOP": "Not run", "0eoYsu": "Míra okamžitého opuštění", "0fs/5S": "Zahájeno", @@ -95,6 +98,7 @@ "1bNJRY": "Vlastnosti uživatele", "1bqKv/": "ID uživatele:", "1fe3VI": "Klíče API jsou dostupné v plánech Standard a Pro.", + "1fzzQF": "Crawler", "1gYD1y": "Informace o relaci", "1hGB1j": "Chyba při přihlašování", "1iEPTM": "", @@ -121,7 +125,8 @@ "2YpSsF": "", "2ZpsMl": "Žádní členové", "2eRO6y": "Typ zařízení", - "2tOWr2": "", + "2rUVsU": "Aplikace", + "2tOWr2": "Obsahuje", "34eVnl": "např. Dokončení registrace", "34g6zC": "Upravit vlastnosti", "39AHJm": "Registrace", @@ -172,6 +177,7 @@ "4l6vz1": "Kopírovat", "4nfQk6": "Zachytávej JavaScript chyby a výjimky z tvého webu", "4v7I1t": "", + "4vQ9fL": "Sledování chyb zachytává chyby a výjimky z tvé aplikace.", "5/6deG": "Při načítání trychtýřů nastal problém. Zkus to prosím později.", "52UMW3": "Celé období", "54Ob9x": "", @@ -182,12 +188,14 @@ "5QiCvx": "", "5U2wOH": "", "5WuADX": "Přidat cíl", + "5Zmqv6": "Nahrát ikonu", "5bo+CW": "", "5dX7fM": "Pokročilé funkce pro firemní týmy", "5elgIe": "Chyba:", "5iGg8I": "", "5jeq8P": "Neznámý", "5kpx3b": "", + "5qOEz+": "dokumentaci Flutter SDK", "5sg7KC": "Heslo", "5y2qWO": "Připojuji...", "5yUsUe": "Přehrávání", @@ -196,6 +204,7 @@ "62nsdy": "Zkusit znovu", "65uDP/": "Provoz od známých botů a crawlerů nebude sledován", "66kGA0": "", + "67Q/sm": "Neplatný formát domény", "68djwb": "Zatím žádné týmy", "69jzMr": "Doména úspěšně aktualizována", "6ErXbc": "", @@ -207,6 +216,7 @@ "6RB7ZL": "API Playground", "6RlEDq": "", "6S3VIt": "Nepodařilo se uložit trychtýř: {message}", + "6TFjfy": "Typ společnosti", "6V/E/f": "Nepodařilo se smazat web", "6VzeBI": "Ve zvoleném období se žádné relace na tento cíl nekonvertovaly.", "6Z97iS": "Organizaci nelze smazat", @@ -236,7 +246,7 @@ "7GCMTs": "", "7HeAWw": "Dobré skóre LCP je 2,5 sekundy nebo méně.", "7IM0G6": "Zobrazovaný název tohoto webu", - "7KEQvc": "", + "7KEQvc": "Výstupní stránka", "7L86Z5": "Člen", "7LjSrf": "Sledování chyb vypnuto", "7O/+X2": "Uživatelé online za posledních 5 minut", @@ -249,9 +259,10 @@ "7h4/R2": "Pro tvůj účet nebyly nalezeny žádné vlastnosti Google Search Console.", "7kRLeH": "", "7mL9QE": "Styl", - "7nUCu9": "", + "7nUCu9": "Časové pásmo", "7pXrnq": "Den", "7q/cDS": "Týden {index}", + "7qesNi": "Doména společnosti", "7ufubd": "Měří interaktivitu. INP hodnotí odezvu měřením času od interakce uživatele po další vykreslení.", "7x+EYZ": "{count} událostí/měsíc", "81yKPX": "Na základě tvého aktuálního vzorce využití", @@ -289,7 +300,7 @@ "99HM2b": "", "99Lkvz": "", "99yMMd": "Chyba konzole", - "9B1UYV": "", + "9B1UYV": "Neobsahuje", "9CMwLr": "Měří vnímanou rychlost načítání. FCP označuje okamžik, kdy se první obsahový prvek stane viditelným.", "9CmkoI": "Zkus upravit časový rozsah nebo filtry", "9E7Zda": "", @@ -301,7 +312,7 @@ "9YazHG": "", "9YyR5G": "Tento člen bude mít přístup k vybraným webům a také k webům uděleným prostřednictvím jeho týmů ({teams}).", "9ZPMMu": "", - "9a9+ww": "", + "9a9+ww": "Název", "9ctGsY": "", "9d/EEp": "Produktová analytika", "9gkUnn": "Zruší se na konci období", @@ -354,6 +365,7 @@ "BffFao": "Sledování chyb zachytává JavaScript chyby a výjimky z tvé aplikace.", "BhbATb": "", "BhlBsq": "Organizace byla úspěšně smazána", + "BiyUAo": "Přidej Rybbit SDK do své aplikace:", "BkOSF2": "Parametry", "C2EM7h": "E-mail vlastníka", "C2iTEH": "", @@ -364,6 +376,7 @@ "CHR+o1": "Zobrazeno {shown} z {total} událostí", "CHTM5p": "Nepodařilo se odeslat pozvánku", "CHzo7t": "", + "CLGask": "Ikona aplikace", "CNXQN5": "Kopírovat kód", "CRAx66": "ID uživatelů budou solena s denně rotovaným klíčem pro lepší soukromí", "CRjhOx": "8× více", @@ -371,7 +384,7 @@ "CXI0EW": "", "CZmGBZ": "", "Cf6LHR": "Název trychtýře", - "CfjnxD": "", + "CfjnxD": "Rovná se", "ClLijo": "Chybí povinný parametr cesty: {param}", "Cm1ioF": "Core Web Vital", "Cn4/rZ": "", @@ -399,7 +412,7 @@ "Dfc9/a": "Pamatuješ si heslo?", "Dg2v6W": "", "DiWAe1": "Zkus vybrat jiný časový rozsah nebo se ujisti, že máš v systému sledovací data.", - "DmLxpf": "", + "DmLxpf": "Nerovná se", "Dmxerh": "", "Dn82AL": "", "DoskyC": "Všechny týmy", @@ -410,6 +423,7 @@ "E/O9LL": "", "E/tVTI": "Sledování interakcí s formuláři vypnuto", "E2rh1p": "Tlačítka", + "E3X5ej": "Vstupní obrazovky", "E3oB+t": "Prohlížeč", "E4L2LT": "Odpadlo ({count})", "E9m5xj": "Identifikovaný uživatel", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} nových událostí", "G1YoMt": "Nepodařilo se smazat import. Zkus to prosím znovu.", "G2HskF": "", + "G7U5Ai": "Neodpovídá regex", "G7giEo": "Průměr 30d", "GBIt8l": "Cesty končící na .pdf, .doc nebo .docx", "GDgEmC": "Žádné události nenalezeny", @@ -557,6 +572,7 @@ "J6C2uX": "", "JCIgkj": "Uživatelské jméno", "JD2pwH": "Organizace", + "JEsxDw": "Nahrávání...", "JIOFW/": "5 min", "JIbDU1": "Poslední kanál", "JKxOsh": "Měsíční události", @@ -616,6 +632,7 @@ "L44v6R": "", "L5X96Z": "URL vzory pro vyloučení ze sledování (jeden na řádek)", "L8DzHm": "Název týmu je povinný", + "L9CtJP": "Návody pro platformy", "LAwI3R": "Jméno nemůže být prázdné", "LB/Amg": "Tvá zkušební verze vypršela. Pro pokračování sledování návštěv se přihlas k plánu.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "Sledování URL parametrů vypnuto", "LONGFB": "Vybrat plán", "LQ46NL": "Souřadnice", + "LUJTwh": "Ikona nahrána", "LUrJwE": "Kanály", "LaV9M1": "Vyber časové pásmo", "LeFOAk": "Filtr není aktivní pro tuto stránku", @@ -651,11 +669,13 @@ "MPAiyo": "Máš aktivní probíhající import. Počkej prosím na jeho dokončení před zahájením nového importu.", "MPQ0kS": "Vyber organizaci", "MQQbnY": "Přehrávání", + "MUaUWe": "Načítám API klíče...", + "MVEvjb": "Odstranit ikonu", "MYsZNO": "", "MZNJkb": "Filtrovat podle názvu...", "MZjNWA": "Nepodařilo se aktualizovat nastavení emailových reportů", "Ma/p91": "", - "MbeLHv": "", + "MbeLHv": "Lat", "MePbA9": "Připoj se k našemu affiliate programu", "MgdnPi": "Odpověď", "Mh+9Tg": "Den {index}", @@ -715,8 +735,9 @@ "OlRI+i": "", "OtO9EF": "Přidej štítky pro organizaci a filtrování tvých webů.", "OtSSs+": "{count} webů", - "Otefae": "", + "Otefae": "Dimenze", "OvzONl": "Vypnuto", + "OwQmnm": "Verze aplikace", "P3H0J/": "Zobrazit více ({count} dalších)", "P3YB9A": "Minulý týden", "P6RGqE": "Hostnames", @@ -730,8 +751,10 @@ "PWxvnC": "", "PZOOdj": "", "PbRfhj": "", + "Pbtcej": "Zobrazení obrazovek", "Pc+tM3": "Generate", "Pe0ogR": "", + "PfUTPz": "VPN", "Pgj6mf": "", "Pgv4+O": "Zatím žádné trychtýře", "PkGCG1": "", @@ -763,6 +786,7 @@ "QgMK2h": "UTM Source", "QiKKFz": "Změnit heslo", "QirE3M": "Datum začátku", + "QitbBm": "Zobrazení obrazovky", "QkTyho": "Postranní panel Rybbit", "Qp/Bnx": "", "Qp1beM": "Cesta", @@ -794,6 +818,7 @@ "Rqzsq/": "Přidat filtr", "RsY5rI": "Konvertované relace", "RumeRz": "Regiony", + "RvxWt3": "Vstupní obrazovka:", "RwzAp9": "Typy událostí", "RzSw2z": "", "S1utKH": "", @@ -801,6 +826,7 @@ "S4zxrU": "", "S5IFjk": "Vytvořte tým pro seskupení webů a správu přístupu členů.", "S7U3Nx": "Blížíš se k měsíčnímu limitu událostí. Zvaž upgrade na placený plán pro vyšší limity.", + "S9jCu8": "Ikona odstraněna", "SB3+JE": "Žádná data trychtýře k dispozici", "SBUMrd": "", "SBoJg8": "", @@ -812,6 +838,7 @@ "SWvHLr": "Tento týden", "SX126d": "Exportovat data", "Sb7oIl": "Výkon podle operačního systému", + "ShHw0c": "Verze aplikace:", "Sjo1P4": "Vlastní", "SnIGkx": "", "SoySgJ": "Odkazující stránka:", @@ -822,15 +849,15 @@ "T7eyOH": "Relace zahájena", "TBMqfx": "", "TBovrx": "Měsíc", - "TE4fIS": "", + "TE4fIS": "Město", "TFVlrR": "", "TH+8LD": "Změna vstupu", "TI5GPL": "", - "TJo5E6": "", + "TJo5E6": "Náhled", "TLsfTQ": "Načítám data o výkonu...", "TOuHNM": "Zadej název trychtýře", "TUwf1V": "", - "TVHfKA": "", + "TVHfKA": "Je", "Tb0/AL": "Přehrávání relací zvětší analytický skript", "TbPEUO": "Tato chyba mohla nastat mimo aktuální časový rozsah.", "Tbo377": "Fakturace", @@ -907,6 +934,7 @@ "VqI0Nz": "Poloha a zařízení", "VtLIgh": "", "Vwukkb": "", + "VxUjLr": "Přidat aplikaci", "VyvTDF": "Obrazovka:", "VzzYJk": "Vytvořit", "W/V6+Y": "Sbalit", @@ -927,6 +955,7 @@ "WnQ/AZ": "Kontaktuj nás", "WuGkfJ": "", "WuHoZ9": "Konverze", + "X0cqyR": "Verze operačního systému", "X0ha1a": "", "X0ppsC": "", "X2PZ7D": "Vytvořit cíl", @@ -1045,7 +1074,7 @@ "cDqj91": "identifikuješ uživatele", "cEIr3W": "Tento člen bude mít přístup pouze k vybraným webům.", "cGNlRB": "Upgraduj na Pro pro přidání více webů", - "cOkfeK": "", + "cOkfeK": "neobsahuje", "cPbI/I": "Nemáte oprávnění k zobrazení nastavení týmu.", "cQyKR1": "Endpointy", "cSzyIV": "Analyzuji trychtýř...", @@ -1062,6 +1091,7 @@ "cfiGPy": "Nový parametr", "ciDaNU": "Automaticky zaznamenat první zobrazení stránky při načtení skriptu", "cj34Wi": "Nebylo vráceno ID organizace", + "ckAea8": "ASN Domain", "ckBJVF": "Tuto akci nelze vrátit zpět. Tím trvale smažete web \"{siteName}\" a veškerá jeho analytická data.", "cmDzrA": "Při načítání událostí nastal problém. Zkus to prosím později.", "cmGAVy": "Velikost stránky:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "", "dv4SpB": "Zatím žádné importy", "e/U0Xt": "Konverzní poměr", + "e/WYt2": "Model zařízení", "eA5EH7": "Hledej organizace...", "eDqstJ": "Události (24h)", "eGIKNJ": "Nejnavštěvovanější stránky", @@ -1099,18 +1130,20 @@ "eKEL/g": "Čekající", "eOj4Bs": "Název události", "eQnbde": "Odpojit Google Search Console?", + "eXWZOl": "Verze aplikace", "eYX7hS": "Aktualizuj svou emailovou adresu pro oznámení účtu", "ebEqgQ": "Máte aktivní předplatné. Před smazáním organizace prosím zrušte předplatné.", "ekXood": "Neomezený", "ekfOaV": "Zpět", "enuxmm": "Sledování kopírování vypnuto", "erYzJH": "", - "f19YrE": "", + "f19YrE": "obsahuje", "f3ocee": "Vyloučení provozu z konkrétních zemí. Události z těchto zemí nebudou sledovány ve tvé analytice.", "f3v0b6": "Sběr Web Vitals je vypnutý", "f5efCD": "", "f7uwif": "Organizujte weby do týmů a kontrolujte, kteří členové k nim mají přístup.", "fB09El": "Cesty začínající /blog/", + "fCvg+B": "Odpovídá regex", "fDjNLX": "Chystáš se importovat velký soubor ({size}). Zpracování může trvat několik minut. Opravdu chceš pokračovat?", "fGblXC": "", "fItAD9": "Kohorta", @@ -1120,7 +1153,7 @@ "fQsGgo": "", "fTWodj": "Název organizace", "fWblDI": "Úvodní zobrazení stránky", - "fXdkiI": "", + "fXdkiI": "je", "fcj5H7": "Veřejná analytika", "fe0rMF": "Klíčové slovo", "fepi00": "Neznámý prohlížeč", @@ -1132,6 +1165,7 @@ "fobMp1": "Dobré skóre TTFB je 800 ms nebo méně.", "fr4eNi": "Testovací předplatné za $1 pro vývoj", "frN4vy": "Naposledy viděn", + "fsUN9Y": "Min. zobrazení obrazovek", "fx99AL": "Slabé", "fz0z4c": "Plán", "g12Dke": "", @@ -1195,7 +1229,7 @@ "i/DU33": "", "i1G+Ml": "Kontaktuj prosím vlastníka organizace pro upgrade.", "i7B/4+": "Trvale smazat tuto organizaci a veškerá její data", - "i9WP0T": "", + "i9WP0T": "Verze prohlížeče", "iBImEo": "", "iBRQen": "Předplatit", "iEFpLb": "", @@ -1224,6 +1258,7 @@ "isUnnK": "Sběr Web Vitals zapnut", "itC9lG": "", "iv7EVe": "Všechny weby", + "iwp2S0": "Obrazovky", "izvHMl": "", "j3ehbn": "Použij prosím silnější heslo", "j5kA5A": "Při načítání chyb nastal problém. Zkus to prosím později.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Zkusit znovu", "jt9BUv": "", "jvo0vs": "Uložit", + "jxsBGE": "Zobrazuje se jako favicon v postranním panelu, přepínači webů a kartách webů. Přijímá jakýkoli formát obrázku — automaticky zmenšeno na 128×128 PNG. Max 50 KB.", "k+kHlN": "Název týmu", "k/CG+N": "", "k1sXXZ": "Failed to generate query", @@ -1299,6 +1335,7 @@ "lILp7K": "Blokování botů vypnuto", "lKv8ex": "", "lRnHfd": "Události neprošly validací", + "lTavFx": "Výstupní obrazovky", "lVP3yI": "", "lXZjmr": "Pozvěte nového člena do této organizace.", "lYB8jy": "{count} uživatelů", @@ -1346,6 +1383,7 @@ "nGQuNF": "Stránek na relaci", "nIXcfs": "", "nIe+Wv": "Vytvořit nového uživatele", + "nJNzqM": "Nepodařilo se nahrát ikonu", "nJuUfX": "Hledat podle názvu, slugu, domény nebo e-mailu člena...", "nL6Jl0": "Retence", "nVYMBG": "Vytvořit uživatele", @@ -1362,11 +1400,12 @@ "nwqt7T": "", "nxIEYS": "Min. délka (s)", "nzStJU": "Všechna přehrávání načtena", - "o/XNk9": "", + "o/XNk9": "Vstupní stránka", "o0jcye": "OS", "o5Jj28": "Účet nelze smazat", "o5XOA3": "", "o7g8s9": "např. ^/blog/.*", + "o9wmBB": "Volitelné. Změněno na 128x128 PNG.", "oA0ogD": "", "oAFgOq": "Doména", "oCBPQ4": "", @@ -1422,6 +1461,7 @@ "q0NdNV": "", "q2ZLFN": "", "q4S4cE": "Protokol konzole", + "qAktpY": "Obrazovky za relaci", "qB5AJG": "Nahraj CSV soubor pro začátek", "qDwvZ4": "Neznámá chyba", "qFQkeR": "Založ novou organizaci pro přidání webů a uživatelů", @@ -1444,7 +1484,7 @@ "qhbBD7": "Odebrat člena", "qib0lb": "", "qj1uhz": "Odpojit", - "qj71j1": "", + "qj71j1": "Dotaz", "qkaC6i": "", "qo2JCK": "{count} skupin", "qpwIG+": "Chyba při načítání trychtýře:", @@ -1486,6 +1526,7 @@ "scPXsn": "", "sd2ieZ": "Týden", "siqsij": "Předplatné může spravovat pouze vlastník organizace.", + "smsDJm": "Nepodařilo se odstranit ikonu", "snh6R6": "Popisek (volitelný)", "soeWeh": "Nastavení organizace", "sy+pv5": "E-mail", @@ -1499,6 +1540,7 @@ "tGAYL2": "Vyber možnost", "tGWccD": "Přidej velká písmena, malá písmena, čísla nebo symboly pro silnější heslo", "tGuKrF": "", + "tH05Pi": "Model zařízení:", "tH8S0d": "Přidat IP", "tHKpoN": "V tomto kroku ve zvoleném období žádné relace neodpadly.", "tHSb8p": "Desktop", @@ -1598,7 +1640,7 @@ "wX/ZOC": "API Reference", "wXpKtu": "Existující štítky:", "wYsv4Z": "Měsíční", - "wbsq7O": "", + "wbsq7O": "Využití", "wc+2Lv": "Sleduj, kdy uživatelé klikají na externí odkazy", "wkXAvx": "Nepodařilo se aktualizovat člena", "wmFdws": "", @@ -1655,6 +1697,7 @@ "yi/uLq": "události", "yl0JSO": "", "ym0FiC": "Upgraduj svůj plán pro pokračování sběru analytických dat.", + "ynio5d": "Neplatný formát názvu balíčku", "ytceSa": "", "yunf47": "Doba trvání relace", "yxDujp": "", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Vše", "zSOvI0": "Filtry", "zUJjPA": "Bez týmu", + "zUxWZC": "Datové centrum", "zWgbGg": "Dnes", "zWkvNO": "Časová osa", "zY8xSk": "Zadej doménu webu, který chceš sledovat", "zZE+e+": "", + "zff4lu": "Čekání na analytická data z vaší aplikace...", "znwd1N": "Smazat trychtýř", "zoRFJV": "", "zuA3Bg": "YouTube", diff --git a/client/messages/de.json b/client/messages/de.json index a3da5064e..7dddd5612 100644 --- a/client/messages/de.json +++ b/client/messages/de.json @@ -25,6 +25,7 @@ "+x2FLn": "Wiedergabe", "+zImMB": "Benutzer gelöscht", "/+XPOf": "Events der Organisation", + "/0J7Xq": "Symbolbild konnte nicht verarbeitet werden", "/0TOL5": "Betrag", "/0tjRq": "Website konnte nicht verschoben werden", "/20q2D": "Ihr Passwort wurde erfolgreich zurückgesetzt.", @@ -49,6 +50,7 @@ "/clOBU": "Wöchentlich", "/dxF+k": "Das Passwort muss mindestens 8 Zeichen lang sein", "/eULaT": "Leistung nach Seite", + "/g8hj9": "ASN-Typ", "/jyLDY": "Inline-Pill", "/kZg9i": "Klick auf Schaltfläche", "/liMUf": "Trichter erfolgreich gelöscht", @@ -68,6 +70,7 @@ "0VV/sK": "Ziel", "0WpQtp": "Bindungsdaten konnten nicht geladen werden", "0cEOKu": "Erweiterte Optionen", + "0cOykX": "ASN-Organisation", "0eYAOP": "Nicht ausgeführt", "0eoYsu": "Absprungrate", "0fs/5S": "Gestartet am", @@ -95,6 +98,7 @@ "1bNJRY": "Benutzermerkmale", "1bqKv/": "Benutzer-ID:", "1fe3VI": "API-Schlüssel sind in den Standard- und Pro-Plänen verfügbar.", + "1fzzQF": "Crawler", "1gYD1y": "Sitzungsinformationen", "1hGB1j": "Fehler bei der Anmeldung", "1iEPTM": "Allgemein", @@ -121,6 +125,7 @@ "2YpSsF": "App-Kennung darf nicht leer sein", "2ZpsMl": "Keine Mitglieder", "2eRO6y": "Gerätetyp", + "2rUVsU": "App", "2tOWr2": "Enthält", "34eVnl": "z.B. Registrierung abgeschlossen", "34g6zC": "Merkmale bearbeiten", @@ -172,6 +177,7 @@ "4l6vz1": "Kopieren", "4nfQk6": "JavaScript-Fehler und Ausnahmen von Ihrer Website erfassen", "4v7I1t": "Pfadausschlüsse", + "4vQ9fL": "Fehlerverfolgung erfasst Fehler und Ausnahmen aus Ihrer Anwendung.", "5/6deG": "Beim Abrufen der Trichter ist ein Problem aufgetreten. Bitte versuchen Sie es später erneut.", "52UMW3": "Gesamtzeitraum", "54Ob9x": "Ja, Website verschieben", @@ -182,12 +188,14 @@ "5QiCvx": "Wählen Sie mindestens eine Berechtigung aus oder deaktivieren Sie die Einschränkungen für einen Vollzugriffsschlüssel", "5U2wOH": "Variante hinzufügen", "5WuADX": "Ziel hinzufügen", + "5Zmqv6": "Symbol hochladen", "5bo+CW": "Noch keine API-Schlüssel", "5dX7fM": "Erweiterte Funktionen für Unternehmensteams", "5elgIe": "Fehler:", "5iGg8I": "Noch nicht genügend Daten", "5jeq8P": "Unbekannt", "5kpx3b": "Traffic auf Schlüssel aufteilen", + "5qOEz+": "Flutter SDK Dokumentation", "5sg7KC": "Passwort", "5y2qWO": "Wird verbunden...", "5yUsUe": "Wiedergabe", @@ -196,6 +204,7 @@ "62nsdy": "Erneut versuchen", "65uDP/": "Traffic von bekannten Bots und Crawlern wird nicht erfasst", "66kGA0": "Laufzeit", + "67Q/sm": "Ungültiges Domain-Format", "68djwb": "Noch keine Teams", "69jzMr": "Domain erfolgreich aktualisiert", "6ErXbc": "ist null", @@ -207,6 +216,7 @@ "6RB7ZL": "API-Spielplatz", "6RlEDq": "Benutzeridentitätswechsel fehlgeschlagen", "6S3VIt": "Trichter konnte nicht gespeichert werden: {message}", + "6TFjfy": "Unternehmenstyp", "6V/E/f": "Website konnte nicht gelöscht werden", "6VzeBI": "Keine Sitzungen haben dieses Ziel im ausgewählten Zeitraum erreicht.", "6Z97iS": "Organisation kann nicht gelöscht werden", @@ -252,6 +262,7 @@ "7nUCu9": "Zeitzone", "7pXrnq": "Tag", "7q/cDS": "Woche {index}", + "7qesNi": "Unternehmens-Domain", "7ufubd": "Misst die Interaktivität. INP bewertet die Reaktionsfähigkeit, indem die Zeit von der Benutzerinteraktion bis zum nächsten Paint gemessen wird.", "7x+EYZ": "{count} Events/Monat", "81yKPX": "Basierend auf Ihrem aktuellen Nutzungsmuster", @@ -354,6 +365,7 @@ "BffFao": "Die Fehlerverfolgung erfasst JavaScript-Fehler und Ausnahmen aus Ihrer Anwendung.", "BhbATb": "Entspricht dem kopierten Text. Leer lassen, um jeden kopierten Text zu zählen.", "BhlBsq": "Organisation erfolgreich gelöscht", + "BiyUAo": "Füge das Rybbit SDK zu deiner App hinzu:", "BkOSF2": "Parameter", "C2EM7h": "Inhaber-E-Mail", "C2iTEH": "Pausiert", @@ -364,6 +376,7 @@ "CHR+o1": "Zeige {shown} von {total} Events", "CHTM5p": "Einladung konnte nicht gesendet werden", "CHzo7t": "Einbettungscode", + "CLGask": "App-Symbol", "CNXQN5": "Code kopieren", "CRAx66": "Benutzer-IDs werden mit einem täglich rotierenden Schlüssel für verbesserten Datenschutz gesalzen", "CRjhOx": "8x größer", @@ -410,6 +423,7 @@ "E/O9LL": "Optional — beschreiben Sie, was dieses Flag steuert", "E/tVTI": "Formularinteraktions-Tracking deaktiviert", "E2rh1p": "Buttons", + "E3X5ej": "Einstiegsbildschirme", "E3oB+t": "Browser", "E4L2LT": "Abgebrochen ({count})", "E9m5xj": "Identifizierter Benutzer", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} neue Events", "G1YoMt": "Import konnte nicht gelöscht werden. Bitte versuchen Sie es erneut.", "G2HskF": "Experiment gelöscht", + "G7U5Ai": "Entspricht nicht Regex", "G7giEo": "Durchschn. 30d", "GBIt8l": "Pfade, die auf .pdf, .doc oder .docx enden", "GDgEmC": "Keine Events gefunden", @@ -557,6 +572,7 @@ "J6C2uX": "Die ID, die diese Person in Ihrem eigenen System hat, z. B. eine E-Mail oder Konto-ID.", "JCIgkj": "Benutzername", "JD2pwH": "Organisationen", + "JEsxDw": "Wird hochgeladen...", "JIOFW/": "5 Min", "JIbDU1": "Letzter Kanal", "JKxOsh": "Monatliche Events", @@ -616,6 +632,7 @@ "L44v6R": "Dadurch werden alle Analysedaten dieses Benutzers dauerhaft gelöscht, einschließlich Ereignissen, Sitzungen, Sitzungsaufzeichnungen und Merkmalen auf allen verknüpften Geräten. Diese Aktion kann nicht rückgängig gemacht werden.", "L5X96Z": "URL-Muster, die vom Tracking ausgeschlossen werden sollen (eines pro Zeile)", "L8DzHm": "Teamname ist erforderlich", + "L9CtJP": "Plattform-Anleitungen", "LAwI3R": "Name darf nicht leer sein", "LB/Amg": "Ihre kostenlose Testphase ist abgelaufen. Abonnieren Sie einen Plan, um erneut Besuche zu verfolgen.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "URL-Parameter-Tracking deaktiviert", "LONGFB": "Plan auswählen", "LQ46NL": "Koordinaten", + "LUJTwh": "Symbol hochgeladen", "LUrJwE": "Kanäle", "LaV9M1": "Zeitzone auswählen", "LeFOAk": "Filter für diese Seite nicht aktiv", @@ -651,6 +669,8 @@ "MPAiyo": "Sie haben einen aktiven Import. Bitte warten Sie, bis er abgeschlossen ist, bevor Sie einen neuen starten.", "MPQ0kS": "Organisation auswählen", "MQQbnY": "Wiedergaben", + "MUaUWe": "API-Schlüssel werden geladen...", + "MVEvjb": "Symbol entfernen", "MYsZNO": "Variante entfernen", "MZNJkb": "Nach Name filtern...", "MZjNWA": "Einstellung für E-Mail-Berichte konnte nicht aktualisiert werden", @@ -717,6 +737,7 @@ "OtSSs+": "{count} Websites", "Otefae": "Dimension", "OvzONl": "Aus", + "OwQmnm": "App-Version", "P3H0J/": "Mehr anzeigen ({count} weitere)", "P3YB9A": "Letzte Woche", "P6RGqE": "Hostnamen", @@ -730,8 +751,10 @@ "PWxvnC": "Bitten Sie einen Site-Administrator, die Google Search Console zu verbinden.", "PZOOdj": "Geben Sie ein gültiges JSON-Objekt ein", "PbRfhj": "Entspricht dem sichtbaren Text des Buttons. Leer lassen, um jeden Button-Klick zu zählen.", + "Pbtcej": "Bildschirmaufrufe", "Pc+tM3": "Generieren", "Pe0ogR": "Design", + "PfUTPz": "VPN", "Pgj6mf": "Events diesen Monat", "Pgv4+O": "Noch keine Trichter", "PkGCG1": "Generieren Sie einen privaten Link, um die Dashboard-Einbettung auszuprobieren.", @@ -763,6 +786,7 @@ "QgMK2h": "UTM-Quelle", "QiKKFz": "Passwort ändern", "QirE3M": "Startdatum", + "QitbBm": "Bildschirmaufruf", "QkTyho": "Rybbit-Seitenleiste", "Qp/Bnx": "Keine Benutzer entsprechen den aktuellen Filtern", "Qp1beM": "Pfad", @@ -794,6 +818,7 @@ "Rqzsq/": "Filter hinzufügen", "RsY5rI": "Konvertierte Sitzungen", "RumeRz": "Regionen", + "RvxWt3": "Einstiegsbildschirm:", "RwzAp9": "Event-Typen", "RzSw2z": "Button-Text (optional)", "S1utKH": "Längengrad", @@ -801,6 +826,7 @@ "S4zxrU": "Sitzungen zählen, bei denen Ihre App ein benanntes Event auslöst.", "S5IFjk": "Erstellen Sie ein Team, um Websites zu gruppieren und den Mitgliederzugriff zu verwalten.", "S7U3Nx": "Sie nähern sich Ihrem monatlichen Event-Limit. Erwägen Sie ein Upgrade auf einen kostenpflichtigen Plan für höhere Limits.", + "S9jCu8": "Symbol entfernt", "SB3+JE": "Keine Trichterdaten verfügbar", "SBUMrd": "Feature-Flags filtern", "SBoJg8": "Diesen API-Schlüssel löschen?", @@ -812,6 +838,7 @@ "SWvHLr": "Diese Woche", "SX126d": "Daten exportieren", "Sb7oIl": "Leistung nach Betriebssystem", + "ShHw0c": "App-Version:", "Sjo1P4": "Individuell", "SnIGkx": "Dies ist das einzige Mal, dass Sie diesen Schlüssel sehen. Kopieren Sie ihn jetzt und bewahren Sie ihn sicher auf.", "SoySgJ": "Referrer:", @@ -907,6 +934,7 @@ "VqI0Nz": "Standort & Gerät", "VtLIgh": "Ein Organisationsschlüssel ist die eigene Anmeldeinformation der Organisation: Er kann auf alle Websites dieser Organisation zugreifen und funktioniert weiter, wenn Mitglieder das Team verlassen. Verwende ihn für Produktionsintegrationen.", "Vwukkb": "JSON-Payload hinzufügen", + "VxUjLr": "App hinzufügen", "VyvTDF": "Bildschirm:", "VzzYJk": "Erstellen", "W/V6+Y": "Einklappen", @@ -927,6 +955,7 @@ "WnQ/AZ": "Kontaktieren Sie uns", "WuGkfJ": "Gibt zurück", "WuHoZ9": "Konversionen", + "X0cqyR": "Betriebssystemversion", "X0ha1a": "Änderungen speichern", "X0ppsC": "Websites", "X2PZ7D": "Ziel erstellen", @@ -1062,6 +1091,7 @@ "cfiGPy": "Neuer Parameter", "ciDaNU": "Automatisch den ersten Seitenaufruf beim Laden des Skripts erfassen", "cj34Wi": "Keine Organisations-ID zurückgegeben", + "ckAea8": "ASN-Domain", "ckBJVF": "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch wird die Website \"{siteName}\" und alle zugehörigen Analysedaten dauerhaft gelöscht.", "cmDzrA": "Beim Abrufen der Events ist ein Problem aufgetreten. Bitte versuchen Sie es später erneut.", "cmGAVy": "Seitengröße:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "Verwenden Sie den generierten Flag-Schlüssel in Ihrer App und verzweigen Sie Ihre UI oder Ihr Verhalten je nach Variante.", "dv4SpB": "Noch keine Importe", "e/U0Xt": "Konversionsrate", + "e/WYt2": "Gerätemodell", "eA5EH7": "Organisationen suchen...", "eDqstJ": "24h-Events", "eGIKNJ": "Top-Seiten", @@ -1099,6 +1130,7 @@ "eKEL/g": "Ausstehend", "eOj4Bs": "Event-Name", "eQnbde": "Google Search Console trennen?", + "eXWZOl": "App-Versionen", "eYX7hS": "Aktualisieren Sie Ihre E-Mail-Adresse für Kontobenachrichtigungen", "ebEqgQ": "Sie haben ein aktives Abonnement. Bitte kündigen Sie Ihr Abonnement, bevor Sie die Organisation löschen.", "ekXood": "Unbegrenzt", @@ -1111,6 +1143,7 @@ "f5efCD": "Das Ergebnis wählen", "f7uwif": "Organisieren Sie Websites in Teams, um zu steuern, welche Mitglieder darauf zugreifen können.", "fB09El": "Pfade, die mit /blog/ beginnen", + "fCvg+B": "Entspricht Regex", "fDjNLX": "Sie sind dabei, eine große Datei ({size}) zu importieren. Die Verarbeitung kann einige Minuten dauern. Möchten Sie fortfahren?", "fGblXC": "Oder folge einer Einrichtungsanleitung für deine Plattform", "fItAD9": "Kohorte", @@ -1132,6 +1165,7 @@ "fobMp1": "Gute TTFB-Werte liegen bei 800 ms oder schneller.", "fr4eNi": "1 $ Test-Abonnement für Entwicklung", "frN4vy": "Zuletzt gesehen", + "fsUN9Y": "Min. Bildschirmaufrufe", "fx99AL": "Schwach", "fz0z4c": "Plan", "g12Dke": "Wachstum", @@ -1224,6 +1258,7 @@ "isUnnK": "Web Vitals-Erfassung aktiviert", "itC9lG": "Grundlagen", "iv7EVe": "Alle Websites", + "iwp2S0": "Bildschirme", "izvHMl": "Identifizieren", "j3ehbn": "Bitte verwenden Sie ein stärkeres Passwort", "j5kA5A": "Beim Abrufen der Fehler ist ein Problem aufgetreten. Bitte versuchen Sie es später erneut.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Erneut versuchen", "jt9BUv": "Noch keine Konversionen", "jvo0vs": "Speichern", + "jxsBGE": "Wird als Favicon in der Seitenleiste, der Website-Auswahl und den Website-Karten angezeigt. Akzeptiert jedes Bildformat — wird automatisch auf 128×128 PNG skaliert. Max. 50 KB.", "k+kHlN": "Teamname", "k/CG+N": "Varianten-Schlüssel sind erforderlich", "k1sXXZ": "Abfrage konnte nicht generiert werden", @@ -1299,6 +1335,7 @@ "lILp7K": "Bot-Blockierung deaktiviert", "lKv8ex": "Standard", "lRnHfd": "Events haben die Validierung nicht bestanden", + "lTavFx": "Ausstiegsbildschirme", "lVP3yI": "Kein Wert erforderlich", "lXZjmr": "Laden Sie ein neues Mitglied zu dieser Organisation ein.", "lYB8jy": "{count} Benutzer", @@ -1346,6 +1383,7 @@ "nGQuNF": "Seiten pro Sitzung", "nIXcfs": "Kopieren in die Zwischenablage fehlgeschlagen. Wählen Sie den Schlüssel aus und kopieren Sie ihn manuell.", "nIe+Wv": "Neuen Benutzer erstellen", + "nJNzqM": "Symbol konnte nicht hochgeladen werden", "nJuUfX": "Nach Name, Slug, Domain oder Mitglieds-E-Mail suchen...", "nL6Jl0": "Bindung", "nVYMBG": "Benutzer erstellen", @@ -1367,6 +1405,7 @@ "o5Jj28": "Konto kann nicht gelöscht werden", "o5XOA3": "{percent}% des Event-Limits Ihrer Organisation", "o7g8s9": "z.B. ^/blog/.*", + "o9wmBB": "Optional. Wird auf 128x128 PNG skaliert.", "oA0ogD": "Beides", "oAFgOq": "Domain", "oCBPQ4": "Filter entfernen", @@ -1422,6 +1461,7 @@ "q0NdNV": "Top-Länder anzeigen", "q2ZLFN": "Feature-Flag gelöscht", "q4S4cE": "Konsolenprotokoll", + "qAktpY": "Bildschirme pro Sitzung", "qB5AJG": "Laden Sie eine CSV-Datei hoch, um zu beginnen", "qDwvZ4": "Unbekannter Fehler", "qFQkeR": "Richten Sie eine neue Organisation ein, um Websites und Benutzer hinzuzufügen", @@ -1486,6 +1526,7 @@ "scPXsn": "Regex", "sd2ieZ": "Woche", "siqsij": "Nur der Inhaber der Organisation kann das Abonnement verwalten.", + "smsDJm": "Symbol konnte nicht entfernt werden", "snh6R6": "Bezeichnung (optional)", "soeWeh": "Organisationseinstellungen", "sy+pv5": "E-Mail", @@ -1499,6 +1540,7 @@ "tGAYL2": "Option auswählen", "tGWccD": "Fügen Sie Groß-/Kleinbuchstaben, Zahlen oder Symbole hinzu für ein stärkeres Passwort", "tGuKrF": "Neues Experiment", + "tH05Pi": "Gerätemodell:", "tH8S0d": "IP hinzufügen", "tHKpoN": "Keine Sitzungen wurden vor Erreichen dieses Schritts im ausgewählten Zeitraum abgebrochen.", "tHSb8p": "Desktop", @@ -1655,6 +1697,7 @@ "yi/uLq": "Events", "yl0JSO": "Das Ändern dieser Notizen ändert weder die Zuteilung noch das Tracking.", "ym0FiC": "Upgraden Sie Ihren Plan, um weiterhin Analysedaten zu sammeln.", + "ynio5d": "Ungültiges Paketnamenformat", "ytceSa": "Kein Ziel verknüpft", "yunf47": "Sitzungsdauer", "yxDujp": "Merkmal hinzufügen", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Alle", "zSOvI0": "Filter", "zUJjPA": "Kein Team", + "zUxWZC": "Rechenzentrum", "zWgbGg": "Heute", "zWkvNO": "Zeitachse", "zY8xSk": "Geben Sie die Domain der Website ein, die Sie verfolgen möchten", "zZE+e+": "Experimente verwenden multivariate Feature-Flags, um jeden Besucher einer stabilen Variante zuzuweisen.", + "zff4lu": "", "znwd1N": "Trichter löschen", "zoRFJV": "Die Bundle- oder Paketkennung für das Tracking", "zuA3Bg": "YouTube", diff --git a/client/messages/en.json b/client/messages/en.json index 70558d0be..ef3ba1a2e 100644 --- a/client/messages/en.json +++ b/client/messages/en.json @@ -25,6 +25,7 @@ "+x2FLn": "replay", "+zImMB": "User deleted", "/+XPOf": "Organization events", + "/0J7Xq": "Failed to process icon image", "/0TOL5": "Amount", "/0tjRq": "Failed to move site", "/20q2D": "Your password has been reset successfully.", @@ -49,6 +50,7 @@ "/clOBU": "Weekly", "/dxF+k": "Password must be at least 8 characters long", "/eULaT": "Performance by Page", + "/g8hj9": "ASN Type", "/jyLDY": "Inline pill", "/kZg9i": "Button Click", "/liMUf": "Funnel deleted successfully", @@ -68,6 +70,7 @@ "0VV/sK": "Goal", "0WpQtp": "Failed to load retention data", "0cEOKu": "Advanced options", + "0cOykX": "ASN Org", "0eYAOP": "Not run", "0eoYsu": "Bounce Rate", "0fs/5S": "Started At", @@ -96,6 +99,7 @@ "1bgBOg": "Yes, disable embed", "1bqKv/": "User ID:", "1fe3VI": "API keys are available on Standard and Pro plans.", + "1fzzQF": "Crawler", "1gYD1y": "Session Info", "1hGB1j": "Error Logging In", "1iEPTM": "General", @@ -122,6 +126,7 @@ "2YpSsF": "App identifier cannot be empty", "2ZpsMl": "No members", "2eRO6y": "Device Type", + "2rUVsU": "App", "2tOWr2": "Contains", "34eVnl": "e.g., Sign Up Completion", "34g6zC": "Edit Traits", @@ -173,6 +178,7 @@ "4l6vz1": "Copy", "4nfQk6": "Capture JavaScript errors and exceptions from your site", "4v7I1t": "Path Exclusions", + "4vQ9fL": "Error tracking captures errors and exceptions from your application.", "5/6deG": "There was a problem fetching the funnels. Please try again later.", "52UMW3": "All Time", "54Ob9x": "Yes, move site", @@ -183,12 +189,14 @@ "5QiCvx": "Select at least one permission, or turn off restrictions for a full-access key", "5U2wOH": "Add variant", "5WuADX": "Add Goal", + "5Zmqv6": "Upload Icon", "5bo+CW": "No API keys yet", "5dX7fM": "Advanced features for enterprise teams", "5elgIe": "Error:", "5iGg8I": "Not enough data yet", "5jeq8P": "Unknown", "5kpx3b": "Split traffic across keys", + "5qOEz+": "Flutter SDK documentation", "5sg7KC": "Password", "5y2qWO": "Connecting...", "5yUsUe": "Replay", @@ -197,6 +205,7 @@ "62nsdy": "Retry", "65uDP/": "Traffic from known bots and crawlers will not be tracked", "66kGA0": "Runtime", + "67Q/sm": "Invalid domain format", "68djwb": "No teams yet", "69jzMr": "Domain updated successfully", "6ErXbc": "is null", @@ -208,6 +217,7 @@ "6RB7ZL": "API Playground", "6RlEDq": "Failed to impersonate user", "6S3VIt": "Failed to save funnel: {message}", + "6TFjfy": "Company Type", "6V/E/f": "Failed to delete site", "6VzeBI": "No sessions converted to this goal in the selected time period.", "6Z97iS": "Cannot delete organization", @@ -253,6 +263,7 @@ "7nUCu9": "Timezone", "7pXrnq": "Day", "7q/cDS": "Week {index}", + "7qesNi": "Company Domain", "7ufubd": "Measures interactivity. INP assesses responsiveness by measuring the time from user interaction to the next paint.", "7x+EYZ": "{count} events/month", "81yKPX": "Based on your current usage pattern", @@ -355,6 +366,7 @@ "BffFao": "Error tracking captures JavaScript errors and exceptions from your application.", "BhbATb": "Matches the text that was copied. Leave empty to count any copied text.", "BhlBsq": "Organization deleted successfully", + "BiyUAo": "Add the Rybbit SDK to your app:", "BkOSF2": "Parameters", "C2EM7h": "Owner Email", "C2iTEH": "Paused", @@ -365,6 +377,7 @@ "CHR+o1": "Showing {shown} of {total} events", "CHTM5p": "Failed to send invitation", "CHzo7t": "Embed Code", + "CLGask": "App Icon", "CNXQN5": "Copy code", "CRAx66": "User IDs will be salted with a daily rotating key for enhanced privacy", "CRjhOx": "8x larger", @@ -411,6 +424,7 @@ "E/O9LL": "Optional — describe what this flag controls", "E/tVTI": "Form interaction tracking disabled", "E2rh1p": "Buttons", + "E3X5ej": "Entry Screens", "E3oB+t": "Browser", "E4L2LT": "Dropped Off ({count})", "E9m5xj": "Identified User", @@ -476,6 +490,7 @@ "G0wWf0": "{bufferedCount} new events", "G1YoMt": "Failed to delete import. Please try again.", "G2HskF": "Experiment deleted", + "G7U5Ai": "Not matches regex", "G7giEo": "Avg 30d", "GBIt8l": "Paths ending in .pdf, .doc, or .docx", "GDgEmC": "No events found", @@ -560,6 +575,7 @@ "J6C2uX": "The ID this person has in your own system, like an email or account ID.", "JCIgkj": "Username", "JD2pwH": "Organizations", + "JEsxDw": "Uploading...", "JIOFW/": "5 Min", "JIbDU1": "Latest channel", "JKxOsh": "Monthly Events", @@ -619,6 +635,7 @@ "L44v6R": "This permanently deletes all analytics data for this user, including events, sessions, session replays, and traits across all linked devices. This action cannot be undone.", "L5X96Z": "URL patterns to exclude from tracking (one per line)", "L8DzHm": "Team name is required", + "L9CtJP": "Platform Guides", "LAwI3R": "Name cannot be empty", "LB/Amg": "Your free trial has expired. Subscribe to a plan to continue tracking visits again.", "LB7mg3": "s", @@ -627,6 +644,7 @@ "LIX+6S": "URL parameters tracking disabled", "LONGFB": "Pick plan", "LQ46NL": "Coordinates", + "LUJTwh": "Icon uploaded", "LUrJwE": "Channels", "LaV9M1": "Select timezone", "LeFOAk": "Filter not active for this page", @@ -654,6 +672,8 @@ "MPAiyo": "You have an active import in progress. Please wait for it to complete before starting a new import.", "MPQ0kS": "Select an organization", "MQQbnY": "Replays", + "MUaUWe": "Loading API keys...", + "MVEvjb": "Remove Icon", "MYsZNO": "Remove variant", "MZNJkb": "Filter by name...", "MZjNWA": "Failed to update email reports setting", @@ -721,6 +741,7 @@ "OtSSs+": "{count} sites", "Otefae": "Dimension", "OvzONl": "Off", + "OwQmnm": "App Version", "P3H0J/": "Show more ({count} more)", "P3YB9A": "Last Week", "P6RGqE": "Hostnames", @@ -734,8 +755,10 @@ "PWxvnC": "Ask a site admin to connect Google Search Console.", "PZOOdj": "Enter a valid JSON object", "PbRfhj": "Matches the button's visible text. Leave empty to count any button click.", + "Pbtcej": "Screenviews", "Pc+tM3": "Generate", "Pe0ogR": "Theme", + "PfUTPz": "VPN", "Pgj6mf": "Events this month", "Pgv4+O": "No funnels yet", "PkGCG1": "Generate a private link to try the dashboard embed.", @@ -767,6 +790,7 @@ "QgMK2h": "UTM Source", "QiKKFz": "Change Password", "QirE3M": "Start Date", + "QitbBm": "Screenview", "QkTyho": "Rybbit Sidebar", "Qp/Bnx": "No users match the current filters", "Qp1beM": "Path", @@ -798,6 +822,7 @@ "Rqzsq/": "Add Filter", "RsY5rI": "Converted Sessions", "RumeRz": "Regions", + "RvxWt3": "Entry Screen:", "Rw7iI7": "Disable", "RwzAp9": "Event Types", "RzSw2z": "Button Text (optional)", @@ -806,6 +831,7 @@ "S4zxrU": "Count sessions where your app fires a named event.", "S5IFjk": "Create a team to group sites and manage member access.", "S7U3Nx": "You are approaching your monthly event limit. Consider upgrading to a paid plan for higher limits.", + "S9jCu8": "Icon removed", "SB3+JE": "No funnel data available", "SBUMrd": "Filter feature flags", "SBoJg8": "Delete this API key?", @@ -817,6 +843,7 @@ "SWvHLr": "This Week", "SX126d": "Export data", "Sb7oIl": "Performance by Operating System", + "ShHw0c": "App Version:", "Sjo1P4": "Custom", "SnIGkx": "This is the only time you'll see this key. Copy it now and store it somewhere safe.", "SoySgJ": "Referrer:", @@ -912,6 +939,7 @@ "VqI0Nz": "Location & Device", "VtLIgh": "An organization key is the organization's own credential: it can access all of this organization's sites and keeps working when members leave. Use it for production integrations.", "Vwukkb": "Add JSON payload", + "VxUjLr": "Add App", "VyvTDF": "Screen:", "VzzYJk": "Create", "W/V6+Y": "Collapse", @@ -932,6 +960,7 @@ "WnQ/AZ": "Contact us", "WuGkfJ": "Returns", "WuHoZ9": "Conversions", + "X0cqyR": "Operating System Version", "X0ha1a": "Save changes", "X0ppsC": "websites", "X2PZ7D": "Create Goal", @@ -1068,6 +1097,7 @@ "cfiGPy": "New Parameter", "ciDaNU": "Automatically track the first page view when the script loads", "cj34Wi": "No organization ID returned", + "ckAea8": "ASN Domain", "ckBJVF": "This action cannot be undone. This will permanently delete the site \"{siteName}\" and all of its analytics data.", "cmDzrA": "There was a problem fetching the events. Please try again later.", "cmGAVy": "Page size:", @@ -1097,6 +1127,7 @@ "dv+Q6J": "Use the generated flag key in your app and branch your UI or behavior by variant.", "dv4SpB": "No imports yet", "e/U0Xt": "Conversion Rate", + "e/WYt2": "Device Model", "eA5EH7": "Search organizations...", "eDqstJ": "24h Events", "eGIKNJ": "Top Pages", @@ -1105,6 +1136,7 @@ "eKEL/g": "Pending", "eOj4Bs": "Event name", "eQnbde": "Disconnect Google Search Console?", + "eXWZOl": "App Versions", "eYX7hS": "Update your email address for account notifications", "ebEqgQ": "You have an active subscription. Please cancel your subscription before deleting your organization.", "ekXood": "Unlimited", @@ -1117,6 +1149,7 @@ "f5efCD": "Pick the outcome", "f7uwif": "Organize sites into teams to control which members can access them.", "fB09El": "Paths starting with /blog/", + "fCvg+B": "Matches regex", "fDjNLX": "You're about to import a large file ({size}). This may take several minutes to process. Are you sure you want to continue?", "fGblXC": "Or follow a setup guide for your platform", "fItAD9": "Cohort", @@ -1138,6 +1171,7 @@ "fobMp1": "Good TTFB scores are 800ms or faster.", "fr4eNi": "$1 test subscription for development", "frN4vy": "Last Seen", + "fsUN9Y": "Min screenviews", "fx99AL": "Weak", "fz0z4c": "Plan", "g12Dke": "Growth", @@ -1231,6 +1265,7 @@ "isUnnK": "Web Vitals collection enabled", "itC9lG": "Basics", "iv7EVe": "All sites", + "iwp2S0": "Screens", "izvHMl": "Identify", "j3ehbn": "Please use a stronger password", "j5kA5A": "There was a problem fetching the errors. Please try again later.", @@ -1264,6 +1299,7 @@ "jsy7pk": "Try Again", "jt9BUv": "No conversions yet", "jvo0vs": "Save", + "jxsBGE": "Displayed as a favicon in the sidebar, site selector, and site cards. Accepts any image format — automatically resized to 128×128 PNG. Max 50 KB.", "k+kHlN": "Team Name", "k/CG+N": "Variant keys are required", "k1sXXZ": "Failed to generate query", @@ -1306,6 +1342,7 @@ "lILp7K": "Bot blocking disabled", "lKv8ex": "Default", "lRnHfd": "Events failed validation", + "lTavFx": "Exit Screens", "lVP3yI": "No value needed", "lXZjmr": "Invite a new member to this organization.", "lYB8jy": "{count} users", @@ -1353,6 +1390,7 @@ "nGQuNF": "Pages per Session", "nIXcfs": "Couldn't copy to clipboard. Select the key and copy it manually.", "nIe+Wv": "Create a new user", + "nJNzqM": "Failed to upload icon", "nJuUfX": "Search by name, slug, domain, or member email...", "nL6Jl0": "Retention", "nVYMBG": "Create User", @@ -1374,6 +1412,7 @@ "o5Jj28": "Cannot delete account", "o5XOA3": "{percent}% of your organization's event limit", "o7g8s9": "e.g. ^/blog/.*", + "o9wmBB": "Optional. Resized to 128x128 PNG.", "oA0ogD": "Both", "oAFgOq": "Domain", "oCBPQ4": "Remove filter", @@ -1429,6 +1468,7 @@ "q0NdNV": "Show top countries", "q2ZLFN": "Feature flag deleted", "q4S4cE": "Console log", + "qAktpY": "Screens per Session", "qB5AJG": "Upload a CSV file to get started", "qDwvZ4": "Unknown error", "qFQkeR": "Set up a new organization to add websites and users", @@ -1493,6 +1533,7 @@ "scPXsn": "regex", "sd2ieZ": "Week", "siqsij": "Only the owner of the organization can manage the subscription.", + "smsDJm": "Failed to remove icon", "snh6R6": "Label (optional)", "soeWeh": "Organization Settings", "sy+pv5": "Email", @@ -1506,6 +1547,7 @@ "tGAYL2": "Select an option", "tGWccD": "Add uppercase, lowercase, numbers, or symbols for a stronger password", "tGuKrF": "New experiment", + "tH05Pi": "Device Model:", "tH8S0d": "Add IP", "tHKpoN": "No sessions dropped off before reaching this step in the selected time period.", "tHSb8p": "Desktop", @@ -1662,6 +1704,7 @@ "yi/uLq": "events", "yl0JSO": "Changing these notes does not change assignment or tracking.", "ym0FiC": "Upgrade your plan to continue collecting analytics.", + "ynio5d": "Invalid package name format", "ytceSa": "No goal connected", "yunf47": "Session Duration", "yxDujp": "Add trait", @@ -1679,10 +1722,12 @@ "zQvVDJ": "All", "zSOvI0": "Filters", "zUJjPA": "No team", + "zUxWZC": "Datacenter", "zWgbGg": "Today", "zWkvNO": "Timeline", "zY8xSk": "Enter the domain of the website you want to track", "zZE+e+": "Experiments use multivariate feature flags to assign each visitor to one stable variant.", + "zff4lu": "Waiting for analytics from your app...", "znwd1N": "Delete Funnel", "zoRFJV": "The bundle or package identifier used for tracking", "zuA3Bg": "YouTube", diff --git a/client/messages/es.json b/client/messages/es.json index e05fc5e35..0b1a39965 100644 --- a/client/messages/es.json +++ b/client/messages/es.json @@ -25,6 +25,7 @@ "+x2FLn": "repetición", "+zImMB": "Usuario eliminado", "/+XPOf": "Eventos de la organización", + "/0J7Xq": "No se pudo procesar la imagen del icono", "/0TOL5": "Monto", "/0tjRq": "Error al mover el sitio", "/20q2D": "Tu contraseña se ha restablecido correctamente.", @@ -49,6 +50,7 @@ "/clOBU": "Semanalmente", "/dxF+k": "La contraseña debe tener al menos 8 caracteres", "/eULaT": "Rendimiento por página", + "/g8hj9": "Tipo ASN", "/jyLDY": "Píldora en línea", "/kZg9i": "Clic en botón", "/liMUf": "Embudo eliminado correctamente", @@ -68,6 +70,7 @@ "0VV/sK": "Objetivo", "0WpQtp": "Error al cargar datos de retención", "0cEOKu": "Opciones avanzadas", + "0cOykX": "Org ASN", "0eYAOP": "No ejecutado", "0eoYsu": "Tasa de rebote", "0fs/5S": "Iniciado el", @@ -95,6 +98,7 @@ "1bNJRY": "Rasgos de usuario", "1bqKv/": "ID de usuario:", "1fe3VI": "Las claves de API están disponibles en los planes Standard y Pro.", + "1fzzQF": "Rastreador", "1gYD1y": "Información de la sesión", "1hGB1j": "Error al iniciar sesión", "1iEPTM": "Objetivos", @@ -121,6 +125,7 @@ "2YpSsF": "El identificador de la aplicación no puede estar vacío", "2ZpsMl": "No hay miembros", "2eRO6y": "Tipo de dispositivo", + "2rUVsU": "Aplicación", "2tOWr2": "Contiene", "34eVnl": "ej., Registro completado", "34g6zC": "Editar rasgos", @@ -172,6 +177,7 @@ "4l6vz1": "Copiar", "4nfQk6": "Captura errores y excepciones de JavaScript de tu sitio", "4v7I1t": "Exclusiones de Ruta", + "4vQ9fL": "El seguimiento de errores captura errores y excepciones de tu aplicación.", "5/6deG": "Hubo un problema al obtener los embudos. Inténtalo de nuevo más tarde.", "52UMW3": "Todo el tiempo", "54Ob9x": "Sí, mover sitio", @@ -182,12 +188,14 @@ "5QiCvx": "Selecciona al menos un permiso, o desactiva las restricciones para una clave de acceso completo", "5U2wOH": "Añadir variante", "5WuADX": "Añadir objetivo", + "5Zmqv6": "Subir icono", "5bo+CW": "Aún no hay claves de API", "5dX7fM": "Funciones avanzadas para equipos de la empresa", "5elgIe": "Tasa de rebote", "5iGg8I": "Aún no hay suficientes datos", "5jeq8P": "Desconocido", "5kpx3b": "Distribuir tráfico entre claves", + "5qOEz+": "documentación del Flutter SDK", "5sg7KC": "Contraseña", "5y2qWO": "Conectando...", "5yUsUe": "Repetición", @@ -196,6 +204,7 @@ "62nsdy": "Reintentar", "65uDP/": "El tráfico de bots y rastreadores conocidos no será rastreado", "66kGA0": "Tiempo de ejecución", + "67Q/sm": "Formato de dominio no válido", "68djwb": "Aún no hay equipos", "69jzMr": "Dominio actualizado correctamente", "6ErXbc": "es nulo", @@ -207,6 +216,7 @@ "6RB7ZL": "Playground de API", "6RlEDq": "Error al suplantar al usuario", "6S3VIt": "Error al guardar el embudo: {message}", + "6TFjfy": "Tipo de empresa", "6V/E/f": "Error al eliminar el sitio", "6VzeBI": "Ninguna sesión se convirtió en este objetivo en el periodo seleccionado.", "6Z97iS": "No se puede eliminar la organización", @@ -252,6 +262,7 @@ "7nUCu9": "Zona Horaria", "7pXrnq": "día", "7q/cDS": "Semana {index}", + "7qesNi": "Dominio de empresa", "7ufubd": "Mide la interactividad. El INP evalúa la capacidad de respuesta midiendo el tiempo desde la interacción del usuario hasta el siguiente paint.", "7x+EYZ": "{count} eventos/mes", "81yKPX": "Basado en tu patrón de uso actual", @@ -354,6 +365,7 @@ "BffFao": "El rastreo de errores captura errores y excepciones de JavaScript de tu aplicación.", "BhbATb": "Coincide con el texto copiado. Déjelo vacío para contar cualquier texto copiado.", "BhlBsq": "Organización eliminada correctamente", + "BiyUAo": "Añade el Rybbit SDK a tu aplicación:", "BkOSF2": "Parámetros", "C2EM7h": "Correo del propietario", "C2iTEH": "Pausado", @@ -364,6 +376,7 @@ "CHR+o1": "Mostrando {shown} de {total} eventos", "CHTM5p": "Error al enviar la invitación", "CHzo7t": "Código de Inserción", + "CLGask": "Icono de la aplicación", "CNXQN5": "Copiar código", "CRAx66": "Los ID de usuario se codificarán con una clave rotativa diaria para una mayor privacidad", "CRjhOx": "8x más grande", @@ -410,6 +423,7 @@ "E/O9LL": "Opcional: describe qué controla esta bandera", "E/tVTI": "Rastreo de interacciones en formularios desactivado", "E2rh1p": "Botones", + "E3X5ej": "Pantallas de entrada", "E3oB+t": "Navegador", "E4L2LT": "Abandonaron ({count})", "E9m5xj": "Usuario identificado", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} nuevos eventos", "G1YoMt": "Error al eliminar la importación. Inténtalo de nuevo.", "G2HskF": "Experimento eliminado", + "G7U5Ai": "No coincide con regex", "G7giEo": "Promedio 30d", "GBIt8l": "Rutas que terminan en .pdf, .doc, o .docx", "GDgEmC": "No se encontraron eventos", @@ -557,6 +572,7 @@ "J6C2uX": "El ID de esta persona en tu propio sistema, como un correo electrónico o un ID de cuenta.", "JCIgkj": "Nombre de usuario", "JD2pwH": "Organizaciones", + "JEsxDw": "Subiendo...", "JIOFW/": "5 min", "JIbDU1": "Último canal", "JKxOsh": "Eventos mensuales", @@ -616,6 +632,7 @@ "L44v6R": "Esto elimina permanentemente todos los datos analíticos de este usuario, incluidos eventos, sesiones, grabaciones de sesión y rasgos en todos los dispositivos vinculados. Esta acción no se puede deshacer.", "L5X96Z": "Patrones de URL a excluir del seguimiento (uno por línea)", "L8DzHm": "El nombre del equipo es obligatorio", + "L9CtJP": "Guías de la plataforma", "LAwI3R": "El nombre no puede estar vacío", "LB/Amg": "Tu prueba gratuita ha expirado. Suscríbete a un plan para continuar rastreando visitas nuevamente.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "Rastreo de parámetros de URL desactivado", "LONGFB": "Elegir plan", "LQ46NL": "Coordenadas", + "LUJTwh": "Icono subido", "LUrJwE": "Canales", "LaV9M1": "Seleccionar zona horaria", "LeFOAk": "Filtro no activo para esta página", @@ -651,6 +669,8 @@ "MPAiyo": "Tienes una importación activa en progreso. Por favor, espera a que se complete antes de iniciar una nueva importación.", "MPQ0kS": "Selecciona una organización", "MQQbnY": "Repeticiones", + "MUaUWe": "Cargando claves de API...", + "MVEvjb": "Eliminar icono", "MYsZNO": "Eliminar variante", "MZNJkb": "Filtrar por nombre...", "MZjNWA": "Error al actualizar la configuración de informes por correo", @@ -717,6 +737,7 @@ "OtSSs+": "{count} sitios", "Otefae": "Dimensión", "OvzONl": "Desactivado", + "OwQmnm": "Versión de la app", "P3H0J/": "Mostrar más ({count} más)", "P3YB9A": "Semana pasada", "P6RGqE": "Nombres de host", @@ -730,8 +751,10 @@ "PWxvnC": "Pídele a un administrador del sitio que conecte Google Search Console.", "PZOOdj": "Introduce un objeto JSON válido", "PbRfhj": "Coincide con el texto visible del botón. Déjelo vacío para contar cualquier clic en el botón.", + "Pbtcej": "Vistas de pantalla", "Pc+tM3": "Generar", "Pe0ogR": "Tema", + "PfUTPz": "VPN", "Pgj6mf": "Eventos este mes", "Pgv4+O": "No hay embudos todavía", "PkGCG1": "Genera un enlace privado para probar la inserción del panel.", @@ -763,6 +786,7 @@ "QgMK2h": "Fuente UTM", "QiKKFz": "Cambiar contraseña", "QirE3M": "Fecha de inicio", + "QitbBm": "Vista de pantalla", "QkTyho": "Barra lateral de Rybbit", "Qp/Bnx": "Ningún usuario coincide con los filtros actuales", "Qp1beM": "Ruta", @@ -794,6 +818,7 @@ "Rqzsq/": "Añadir Filtro", "RsY5rI": "Sesiones convertidas", "RumeRz": "Regiones", + "RvxWt3": "Pantalla de entrada:", "RwzAp9": "Tipos de eventos", "RzSw2z": "Texto del botón (opcional)", "S1utKH": "Long", @@ -801,6 +826,7 @@ "S4zxrU": "Contar sesiones donde tu aplicación dispara un evento con nombre.", "S5IFjk": "Crea un equipo para agrupar sitios y administrar el acceso de los miembros.", "S7U3Nx": "Te estás acercando a tu límite de eventos mensuales. Considera actualizar a un plan de pago para obtener límites más altos.", + "S9jCu8": "Icono eliminado", "SB3+JE": "No hay datos de embudo disponibles", "SBUMrd": "Filtrar banderas de funcionalidad", "SBoJg8": "¿Eliminar esta clave de API?", @@ -812,6 +838,7 @@ "SWvHLr": "Esta semana", "SX126d": "Exportar datos", "Sb7oIl": "Rendimiento por sistema operativo", + "ShHw0c": "Versión de la app:", "Sjo1P4": "Personalizado", "SnIGkx": "Esta es la única vez que verás esta clave. Cópiala ahora y guárdala en un lugar seguro.", "SoySgJ": "Referidor:", @@ -907,6 +934,7 @@ "VqI0Nz": "Ubicación y Dispositivo", "VtLIgh": "Una clave de organización es la credencial propia de la organización: puede acceder a todos los sitios de esta organización y sigue funcionando cuando los miembros se van. Úsala para integraciones de producción.", "Vwukkb": "Añadir carga útil JSON", + "VxUjLr": "Agregar aplicación", "VyvTDF": "Pantalla:", "VzzYJk": "Crear", "W/V6+Y": "Colapsar", @@ -927,6 +955,7 @@ "WnQ/AZ": "Contáctanos", "WuGkfJ": "Devuelve", "WuHoZ9": "Conversiones", + "X0cqyR": "Versión del sistema operativo", "X0ha1a": "Guardar cambios", "X0ppsC": "sitios web", "X2PZ7D": "Crear objetivo", @@ -1062,6 +1091,7 @@ "cfiGPy": "Nuevo parámetro", "ciDaNU": "Rastrea automáticamente la primera visita a la página cuando se carga el script", "cj34Wi": "No se devolvió ID de organización", + "ckAea8": "Dominio ASN", "ckBJVF": "Esta acción no se puede deshacer. Esto eliminará permanentemente el sitio \"{siteName}\" y todos sus datos de análisis.", "cmDzrA": "Hubo un problema al obtener los eventos. Inténtalo de nuevo más tarde.", "cmGAVy": "Tamaño de página:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "Usa la clave de bandera generada en tu aplicación y ramifica tu interfaz o comportamiento según la variante.", "dv4SpB": "No hay importaciones aún", "e/U0Xt": "Tasa de conversión", + "e/WYt2": "Modelo de dispositivo", "eA5EH7": "Buscar organizaciones...", "eDqstJ": "Eventos 24h", "eGIKNJ": "Páginas principales", @@ -1099,6 +1130,7 @@ "eKEL/g": "Pendiente", "eOj4Bs": "Nombre del evento", "eQnbde": "¿Desconectar Google Search Console?", + "eXWZOl": "Versiones de la app", "eYX7hS": "Actualiza tu correo electrónico para notificaciones de la cuenta", "ebEqgQ": "Tienes una suscripción activa. Cancela tu suscripción antes de eliminar la organización.", "ekXood": "Ilimitado", @@ -1111,6 +1143,7 @@ "f5efCD": "Elige el resultado", "f7uwif": "Organiza los sitios en equipos para controlar qué miembros pueden acceder a ellos.", "fB09El": "Rutas que comienzan con /blog/", + "fCvg+B": "Coincide con regex", "fDjNLX": "Estás a punto de importar un archivo grande ({size}). Esto puede tardar varios minutos en procesarse. ¿Estás seguro de que quieres continuar?", "fGblXC": "O sigue una guía de configuración para tu plataforma", "fItAD9": "Cohorte", @@ -1132,6 +1165,7 @@ "fobMp1": "Las puntuaciones buenas de TTFB son de 800 ms o más rápidas.", "fr4eNi": "Suscripción de prueba de $1 para desarrollo", "frN4vy": "Visto por última vez", + "fsUN9Y": "Mín. vistas de pantalla", "fx99AL": "Débil", "fz0z4c": "Plan", "g12Dke": "Crecimiento", @@ -1224,6 +1258,7 @@ "isUnnK": "Recopilación de Web Vitals activada", "itC9lG": "Conceptos básicos", "iv7EVe": "Todos los sitios", + "iwp2S0": "Pantallas", "izvHMl": "Identificar", "j3ehbn": "Por favor, usa una contraseña más fuerte", "j5kA5A": "Hubo un problema al obtener los errores. Inténtalo de nuevo más tarde.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Intentar de nuevo", "jt9BUv": "Aún no hay conversiones", "jvo0vs": "Guardar", + "jxsBGE": "Se muestra como favicon en la barra lateral, el selector de sitios y las tarjetas de sitios. Acepta cualquier formato de imagen — se redimensiona automáticamente a 128×128 PNG. Máx. 50 KB.", "k+kHlN": "Nombre del equipo", "k/CG+N": "Las claves de variante son obligatorias", "k1sXXZ": "Error al generar la consulta", @@ -1299,6 +1335,7 @@ "lILp7K": "Bloqueo de bots desactivado", "lKv8ex": "Por defecto", "lRnHfd": "Los eventos fallaron la validación", + "lTavFx": "Pantallas de salida", "lVP3yI": "No se necesita valor", "lXZjmr": "Invita a un nuevo miembro a esta organización.", "lYB8jy": "{count} usuarios", @@ -1346,6 +1383,7 @@ "nGQuNF": "Páginas por sesión", "nIXcfs": "No se pudo copiar al portapapeles. Selecciona la clave y cópiala manualmente.", "nIe+Wv": "Crear nuevo usuario", + "nJNzqM": "No se pudo subir el icono", "nJuUfX": "Buscar por nombre, slug, dominio o correo de miembro...", "nL6Jl0": "Retención", "nVYMBG": "Crear usuario", @@ -1367,6 +1405,7 @@ "o5Jj28": "No se puede eliminar la cuenta", "o5XOA3": "{percent}% del límite de eventos de tu organización", "o7g8s9": "ej. ^/blog/.*", + "o9wmBB": "Opcional. Se redimensiona a 128x128 PNG.", "oA0ogD": "Ambos", "oAFgOq": "Dominio", "oCBPQ4": "Eliminar filtro", @@ -1422,6 +1461,7 @@ "q0NdNV": "Mostrar países principales", "q2ZLFN": "Bandera de funcionalidad eliminada", "q4S4cE": "Registro de la consola", + "qAktpY": "Pantallas por sesión", "qB5AJG": "Sube un archivo CSV para empezar", "qDwvZ4": "Error desconocido", "qFQkeR": "Configura una nueva organización para añadir sitios web y usuarios", @@ -1486,6 +1526,7 @@ "scPXsn": "regex", "sd2ieZ": "semana", "siqsij": "Solo el propietario de la organización puede gestionar la suscripción.", + "smsDJm": "No se pudo eliminar el icono", "snh6R6": "Etiqueta (opcional)", "soeWeh": "Configuración de la organización", "sy+pv5": "Correo electrónico", @@ -1499,6 +1540,7 @@ "tGAYL2": "Selecciona una opción", "tGWccD": "Añade mayúsculas, minúsculas, números o símbolos para una contraseña más fuerte", "tGuKrF": "Nuevo experimento", + "tH05Pi": "Modelo del dispositivo:", "tH8S0d": "Añadir IP", "tHKpoN": "Ninguna sesión abandonó antes de alcanzar este paso en el periodo seleccionado.", "tHSb8p": "Escritorio", @@ -1655,6 +1697,7 @@ "yi/uLq": "eventos", "yl0JSO": "Cambiar estas notas no modifica la asignación ni el seguimiento.", "ym0FiC": "Actualiza tu plan para seguir recopilando analíticas.", + "ynio5d": "Formato de nombre de paquete no válido", "ytceSa": "Sin objetivo conectado", "yunf47": "Duración de la sesión", "yxDujp": "Añadir rasgo", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Todos", "zSOvI0": "Filtros", "zUJjPA": "Sin equipo", + "zUxWZC": "Centro de datos", "zWgbGg": "Hoy", "zWkvNO": "Línea de tiempo", "zY8xSk": "Introduce el dominio del sitio web que quieres rastrear", "zZE+e+": "Los experimentos usan banderas de funcionalidad multivariantes para asignar a cada visitante una variante estable.", + "zff4lu": "", "znwd1N": "Eliminar embudo", "zoRFJV": "El identificador de paquete usado para el seguimiento", "zuA3Bg": "YouTube", diff --git a/client/messages/fr.json b/client/messages/fr.json index 0017b81cb..a8eb1e008 100644 --- a/client/messages/fr.json +++ b/client/messages/fr.json @@ -25,6 +25,7 @@ "+x2FLn": "relecture", "+zImMB": "Utilisateur supprimé", "/+XPOf": "Événements de l'organisation", + "/0J7Xq": "Impossible de traiter l'image de l'icône", "/0TOL5": "Montant", "/0tjRq": "Échec du déplacement du site", "/20q2D": "Votre mot de passe a été réinitialisé avec succès.", @@ -49,6 +50,7 @@ "/clOBU": "Hebdomadaire", "/dxF+k": "Le mot de passe doit contenir au moins 8 caractères", "/eULaT": "Performance par page", + "/g8hj9": "Type ASN", "/jyLDY": "Pastille en ligne", "/kZg9i": "Clic sur bouton", "/liMUf": "Entonnoir supprimé avec succès", @@ -68,6 +70,7 @@ "0VV/sK": "Objectif", "0WpQtp": "Échec du chargement des données de rétention", "0cEOKu": "Options avancées", + "0cOykX": "Org. ASN", "0eYAOP": "Non exécuté", "0eoYsu": "Taux de rebond", "0fs/5S": "Démarré le", @@ -95,6 +98,7 @@ "1bNJRY": "Attributs utilisateur", "1bqKv/": "ID utilisateur :", "1fe3VI": "Les clés API sont disponibles avec les forfaits Standard et Pro.", + "1fzzQF": "Robot", "1gYD1y": "Info session", "1hGB1j": "Erreur de connexion", "1iEPTM": "Général", @@ -121,6 +125,7 @@ "2YpSsF": "L'identifiant de l'application ne peut pas être vide", "2ZpsMl": "Aucun membre", "2eRO6y": "Type d'appareil", + "2rUVsU": "Application", "2tOWr2": "Contient", "34eVnl": "ex. Inscription terminée", "34g6zC": "Modifier les attributs", @@ -172,6 +177,7 @@ "4l6vz1": "Copier", "4nfQk6": "Capturer les erreurs JavaScript et les exceptions de votre site", "4v7I1t": "Exclusions de chemin", + "4vQ9fL": "Le suivi des erreurs capture les erreurs et exceptions de votre application.", "5/6deG": "Un problème est survenu lors de la récupération des entonnoirs. Veuillez réessayer plus tard.", "52UMW3": "Depuis toujours", "54Ob9x": "Oui, déplacer le site", @@ -182,12 +188,14 @@ "5QiCvx": "Sélectionnez au moins une autorisation, ou désactivez les restrictions pour une clé à accès complet", "5U2wOH": "Ajouter une variante", "5WuADX": "Ajouter un objectif", + "5Zmqv6": "Téléverser l'icône", "5bo+CW": "Aucune clé API pour le moment", "5dX7fM": "Fonctionnalités avancées pour les équipes entreprise", "5elgIe": "Taux de rebond", "5iGg8I": "Pas encore assez de données", "5jeq8P": "Inconnu", "5kpx3b": "Répartir le trafic entre les clés", + "5qOEz+": "documentation du SDK Flutter", "5sg7KC": "Mot de passe", "5y2qWO": "Connexion en cours...", "5yUsUe": "Relecture", @@ -196,6 +204,7 @@ "62nsdy": "Réessayer", "65uDP/": "Le trafic des bots et robots connus ne sera pas suivi", "66kGA0": "Environnement d'exécution", + "67Q/sm": "Format de domaine invalide", "68djwb": "Pas encore d'équipes", "69jzMr": "Domaine mis à jour avec succès", "6ErXbc": "est nul", @@ -207,6 +216,7 @@ "6RB7ZL": "Bac à sable API", "6RlEDq": "Échec de l'usurpation d'identité de l'utilisateur", "6S3VIt": "Échec de l'enregistrement de l'entonnoir : {message}", + "6TFjfy": "Type d'entreprise", "6V/E/f": "Échec de la suppression du site", "6VzeBI": "Aucune session n'a converti vers cet objectif dans la période sélectionnée.", "6Z97iS": "Impossible de supprimer l'organisation", @@ -252,6 +262,7 @@ "7nUCu9": "Fuseau horaire", "7pXrnq": "jour", "7q/cDS": "Semaine {index}", + "7qesNi": "Domaine de l'entreprise", "7ufubd": "Mesure l'interactivité. INP évalue la réactivité en mesurant le temps entre l'interaction utilisateur et le prochain affichage.", "7x+EYZ": "{count} événements/mois", "81yKPX": "Basé sur votre schéma d'utilisation actuel", @@ -354,6 +365,7 @@ "BffFao": "Le suivi des erreurs capture les erreurs JavaScript et les exceptions de votre application.", "BhbATb": "Correspond au texte copié. Laissez vide pour comptabiliser tout texte copié.", "BhlBsq": "Organisation supprimée avec succès", + "BiyUAo": "Ajoutez le Rybbit SDK à votre application :", "BkOSF2": "Paramètres", "C2EM7h": "E-mail du propriétaire", "C2iTEH": "En pause", @@ -364,6 +376,7 @@ "CHR+o1": "Affichage de {shown} sur {total} événements", "CHTM5p": "Échec de l'envoi de l'invitation", "CHzo7t": "Code d'intégration", + "CLGask": "Icône de l'application", "CNXQN5": "Copier le code", "CRAx66": "Les ID utilisateur seront salés avec une clé rotative quotidienne pour une confidentialité renforcée", "CRjhOx": "8x plus grand", @@ -410,6 +423,7 @@ "E/O9LL": "Facultatif — décrivez ce que ce drapeau contrôle", "E/tVTI": "Suivi des interactions de formulaire désactivé", "E2rh1p": "Boutons", + "E3X5ej": "Écrans d'entrée", "E3oB+t": "Navigateur", "E4L2LT": "Abandonné ({count})", "E9m5xj": "Utilisateur identifié", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} nouveaux événements", "G1YoMt": "Échec de la suppression de l'import. Veuillez réessayer.", "G2HskF": "Expérience supprimée", + "G7U5Ai": "Ne correspond pas à la regex", "G7giEo": "Moy. 30j", "GBIt8l": "Chemins se terminant par .pdf, .doc ou .docx", "GDgEmC": "Aucun événement trouvé", @@ -557,6 +572,7 @@ "J6C2uX": "L'identifiant de cette personne dans votre propre système, comme un e-mail ou un ID de compte.", "JCIgkj": "Nom d'utilisateur", "JD2pwH": "Organisations", + "JEsxDw": "Téléversement...", "JIOFW/": "5 min", "JIbDU1": "Dernier canal", "JKxOsh": "Événements mensuels", @@ -616,6 +632,7 @@ "L44v6R": "Cette action supprime définitivement toutes les données analytiques de cet utilisateur, y compris les événements, les sessions, les enregistrements de session et les attributs sur tous les appareils associés. Cette action est irréversible.", "L5X96Z": "Modèles d'URL à exclure du suivi (un par ligne)", "L8DzHm": "Le nom de l'équipe est requis", + "L9CtJP": "Guides de plateformes", "LAwI3R": "Le nom ne peut pas être vide", "LB/Amg": "Votre essai gratuit a expiré. Abonnez-vous à un plan pour recommencer à suivre les visites.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "Suivi des paramètres URL désactivé", "LONGFB": "Choisir un plan", "LQ46NL": "Coordonnées", + "LUJTwh": "Icône téléversée", "LUrJwE": "Canaux", "LaV9M1": "Sélectionner le fuseau horaire", "LeFOAk": "Filtre non actif pour cette page", @@ -651,6 +669,8 @@ "MPAiyo": "Vous avez un import actif en cours. Veuillez attendre qu'il se termine avant d'en lancer un nouveau.", "MPQ0kS": "Sélectionner une organisation", "MQQbnY": "Relectures", + "MUaUWe": "Chargement des clés API...", + "MVEvjb": "Supprimer l'icône", "MYsZNO": "Supprimer la variante", "MZNJkb": "Filtrer par nom...", "MZjNWA": "Échec de la mise à jour du paramètre de rapports par e-mail", @@ -717,6 +737,7 @@ "OtSSs+": "{count} sites", "Otefae": "Dimension", "OvzONl": "Désactivé", + "OwQmnm": "Version de l'application", "P3H0J/": "Voir plus ({count} de plus)", "P3YB9A": "Semaine dernière", "P6RGqE": "Noms d'hôte", @@ -730,8 +751,10 @@ "PWxvnC": "Demandez à un administrateur du site de connecter Google Search Console.", "PZOOdj": "Saisissez un objet JSON valide", "PbRfhj": "Correspond au texte visible du bouton. Laissez vide pour comptabiliser tout clic sur un bouton.", + "Pbtcej": "Vues d'écran", "Pc+tM3": "Générer", "Pe0ogR": "Thème", + "PfUTPz": "VPN", "Pgj6mf": "Événements ce mois-ci", "Pgv4+O": "Aucun entonnoir pour le moment", "PkGCG1": "Générez un lien privé pour tester l'intégration du tableau de bord.", @@ -763,6 +786,7 @@ "QgMK2h": "Source UTM", "QiKKFz": "Changer le mot de passe", "QirE3M": "Date de début", + "QitbBm": "Vue d'écran", "QkTyho": "Barre latérale Rybbit", "Qp/Bnx": "Aucun utilisateur ne correspond aux filtres actuels", "Qp1beM": "Chemin", @@ -794,6 +818,7 @@ "Rqzsq/": "Ajouter un filtre", "RsY5rI": "Sessions converties", "RumeRz": "Régions", + "RvxWt3": "Écran d'entrée :", "RwzAp9": "Types d'événements", "RzSw2z": "Texte du bouton (facultatif)", "S1utKH": "Long", @@ -801,6 +826,7 @@ "S4zxrU": "Comptabiliser les sessions où votre application déclenche un événement nommé.", "S5IFjk": "Créez une équipe pour regrouper les sites et gérer l'accès des membres.", "S7U3Nx": "Vous approchez de votre limite mensuelle d'événements. Envisagez de passer à un plan payant pour des limites plus élevées.", + "S9jCu8": "Icône supprimée", "SB3+JE": "Aucune donnée d'entonnoir disponible", "SBUMrd": "Filtrer les feature flags", "SBoJg8": "Supprimer cette clé API ?", @@ -812,6 +838,7 @@ "SWvHLr": "Cette semaine", "SX126d": "Exporter les données", "Sb7oIl": "Performance par système d'exploitation", + "ShHw0c": "Version de l'app :", "Sjo1P4": "Personnalisé", "SnIGkx": "C'est la seule fois où vous verrez cette clé. Copiez-la maintenant et conservez-la en lieu sûr.", "SoySgJ": "Référent :", @@ -907,6 +934,7 @@ "VqI0Nz": "Emplacement et appareil", "VtLIgh": "Une clé d'organisation est l'identifiant propre de l'organisation : elle peut accéder à tous les sites de cette organisation et continue de fonctionner lorsque des membres quittent l'équipe. Utilisez-la pour les intégrations de production.", "Vwukkb": "Ajouter une charge utile JSON", + "VxUjLr": "Ajouter une application", "VyvTDF": "Écran :", "VzzYJk": "Créer", "W/V6+Y": "Réduire", @@ -927,6 +955,7 @@ "WnQ/AZ": "Contactez-nous", "WuGkfJ": "Retourne", "WuHoZ9": "Conversions", + "X0cqyR": "Version du système d'exploitation", "X0ha1a": "Enregistrer les modifications", "X0ppsC": "sites web", "X2PZ7D": "Créer un objectif", @@ -1062,6 +1091,7 @@ "cfiGPy": "Nouveau paramètre", "ciDaNU": "Enregistrer automatiquement la première page vue au chargement du script", "cj34Wi": "Aucun ID d'organisation retourné", + "ckAea8": "Domaine ASN", "ckBJVF": "Cette action est irréversible. Le site \"{siteName}\" et toutes ses données analytiques seront définitivement supprimés.", "cmDzrA": "Un problème est survenu lors de la récupération des événements. Veuillez réessayer plus tard.", "cmGAVy": "Taille de page :", @@ -1091,6 +1121,7 @@ "dv+Q6J": "Utilisez la clé de drapeau générée dans votre application et branchez votre interface ou comportement selon la variante.", "dv4SpB": "Aucun import pour le moment", "e/U0Xt": "Taux de conversion", + "e/WYt2": "Modèle d'appareil", "eA5EH7": "Rechercher des organisations...", "eDqstJ": "Événements 24h", "eGIKNJ": "Pages principales", @@ -1099,6 +1130,7 @@ "eKEL/g": "En attente", "eOj4Bs": "Nom de l'événement", "eQnbde": "Déconnecter Google Search Console ?", + "eXWZOl": "Versions de l'application", "eYX7hS": "Mettez à jour votre adresse e-mail pour les notifications du compte", "ebEqgQ": "Vous avez un abonnement actif. Veuillez annuler votre abonnement avant de supprimer votre organisation.", "ekXood": "Illimité", @@ -1111,6 +1143,7 @@ "f5efCD": "Choisir le résultat", "f7uwif": "Organisez les sites en équipes pour contrôler quels membres peuvent y accéder.", "fB09El": "Chemins commençant par /blog/", + "fCvg+B": "Correspond à la regex", "fDjNLX": "Vous êtes sur le point d'importer un gros fichier ({size}). Le traitement peut prendre plusieurs minutes. Voulez-vous continuer ?", "fGblXC": "Ou suivez un guide de configuration pour votre plateforme", "fItAD9": "Cohorte", @@ -1132,6 +1165,7 @@ "fobMp1": "De bons scores TTFB sont de 800 ms ou moins.", "fr4eNi": "Abonnement test à 1 $ pour le développement", "frN4vy": "Dernière visite", + "fsUN9Y": "Min. vues d'écran", "fx99AL": "Faible", "fz0z4c": "Forfait", "g12Dke": "Croissance", @@ -1224,6 +1258,7 @@ "isUnnK": "Collecte des Web Vitals activée", "itC9lG": "Bases", "iv7EVe": "Tous les sites", + "iwp2S0": "Écrans", "izvHMl": "Identifier", "j3ehbn": "Veuillez utiliser un mot de passe plus fort", "j5kA5A": "Un problème est survenu lors de la récupération des erreurs. Veuillez réessayer plus tard.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Réessayer", "jt9BUv": "Aucune conversion pour l'instant", "jvo0vs": "Enregistrer", + "jxsBGE": "Affiché comme favicon dans la barre latérale, le sélecteur de sites et les cartes de sites. Accepte tout format d'image — automatiquement redimensionné en 128×128 PNG. Max 50 Ko.", "k+kHlN": "Nom de l'équipe", "k/CG+N": "Les clés de variante sont requises", "k1sXXZ": "Échec de la génération de la requête", @@ -1299,6 +1335,7 @@ "lILp7K": "Blocage des bots désactivé", "lKv8ex": "Par défaut", "lRnHfd": "Les événements n'ont pas passé la validation", + "lTavFx": "Écrans de sortie", "lVP3yI": "Aucune valeur requise", "lXZjmr": "Invitez un nouveau membre dans cette organisation.", "lYB8jy": "{count} utilisateurs", @@ -1346,6 +1383,7 @@ "nGQuNF": "Pages par session", "nIXcfs": "Impossible de copier dans le presse-papiers. Sélectionnez la clé et copiez-la manuellement.", "nIe+Wv": "Créer un nouvel utilisateur", + "nJNzqM": "Impossible de téléverser l'icône", "nJuUfX": "Rechercher par nom, slug, domaine ou e-mail de membre...", "nL6Jl0": "Rétention", "nVYMBG": "Créer un utilisateur", @@ -1367,6 +1405,7 @@ "o5Jj28": "Impossible de supprimer le compte", "o5XOA3": "{percent}% de la limite d'événements de votre organisation", "o7g8s9": "ex. ^/blog/.*", + "o9wmBB": "Facultatif. Redimensionné en 128x128 PNG.", "oA0ogD": "Les deux", "oAFgOq": "Domaine", "oCBPQ4": "Supprimer le filtre", @@ -1422,6 +1461,7 @@ "q0NdNV": "Afficher les pays principaux", "q2ZLFN": "Feature flag supprimé", "q4S4cE": "Journal de la console", + "qAktpY": "Écrans par session", "qB5AJG": "Téléchargez un fichier CSV pour commencer", "qDwvZ4": "Erreur inconnue", "qFQkeR": "Configurez une nouvelle organisation pour ajouter des sites web et des utilisateurs", @@ -1486,6 +1526,7 @@ "scPXsn": "regex", "sd2ieZ": "semaine", "siqsij": "Seul le propriétaire de l'organisation peut gérer l'abonnement.", + "smsDJm": "Impossible de supprimer l'icône", "snh6R6": "Libellé (optionnel)", "soeWeh": "Paramètres de l'organisation", "sy+pv5": "E-mail", @@ -1499,6 +1540,7 @@ "tGAYL2": "Sélectionner une option", "tGWccD": "Ajoutez des majuscules, minuscules, chiffres ou symboles pour un mot de passe plus fort", "tGuKrF": "Nouvelle expérience", + "tH05Pi": "Modèle d'appareil :", "tH8S0d": "Ajouter une IP", "tHKpoN": "Aucune session n'a abandonné avant d'atteindre cette étape dans la période sélectionnée.", "tHSb8p": "Bureau", @@ -1655,6 +1697,7 @@ "yi/uLq": "événements", "yl0JSO": "La modification de ces notes ne change pas l'assignation ni le suivi.", "ym0FiC": "Mettez à niveau votre plan pour continuer à collecter des données analytiques.", + "ynio5d": "Format de nom de paquet invalide", "ytceSa": "Aucun objectif associé", "yunf47": "Durée de session", "yxDujp": "Ajouter un attribut", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Tous", "zSOvI0": "Filtres", "zUJjPA": "Aucune équipe", + "zUxWZC": "Centre de données", "zWgbGg": "Aujourd'hui", "zWkvNO": "Chronologie", "zY8xSk": "Saisissez le domaine du site web que vous souhaitez suivre", "zZE+e+": "Les expériences utilisent des feature flags multivariantes pour assigner chaque visiteur à une variante stable.", + "zff4lu": "", "znwd1N": "Supprimer l'entonnoir", "zoRFJV": "L'identifiant de bundle ou de package utilisé pour le suivi", "zuA3Bg": "YouTube", diff --git a/client/messages/it.json b/client/messages/it.json index 2c6379f45..284ba4379 100644 --- a/client/messages/it.json +++ b/client/messages/it.json @@ -25,6 +25,7 @@ "+x2FLn": "replay", "+zImMB": "Utente eliminato", "/+XPOf": "Eventi dell'organizzazione", + "/0J7Xq": "Impossibile elaborare l'immagine dell'icona", "/0TOL5": "Importo", "/0tjRq": "Impossibile spostare il sito", "/20q2D": "La tua password è stata reimpostata con successo.", @@ -49,6 +50,7 @@ "/clOBU": "Settimanale", "/dxF+k": "La password deve essere lunga almeno 8 caratteri", "/eULaT": "Prestazioni per Pagina", + "/g8hj9": "Tipo ASN", "/jyLDY": "Pillola inline", "/kZg9i": "Clic su pulsante", "/liMUf": "Funnel eliminato con successo", @@ -68,6 +70,7 @@ "0VV/sK": "Obiettivo", "0WpQtp": "Impossibile caricare i dati di fidelizzazione", "0cEOKu": "Opzioni avanzate", + "0cOykX": "Org ASN", "0eYAOP": "Non eseguito", "0eoYsu": "Frequenza di Rimbalzo", "0fs/5S": "Iniziata Il", @@ -95,6 +98,7 @@ "1bNJRY": "Tratti Utente", "1bqKv/": "ID Utente:", "1fe3VI": "Le chiavi API sono disponibili nei piani Standard e Pro.", + "1fzzQF": "Bot", "1gYD1y": "Info Sessione", "1hGB1j": "Errore di Accesso", "1iEPTM": "Generale", @@ -121,6 +125,7 @@ "2YpSsF": "L'identificatore dell'app non può essere vuoto", "2ZpsMl": "Nessun membro", "2eRO6y": "Tipo di Dispositivo", + "2rUVsU": "App", "2tOWr2": "Contiene", "34eVnl": "es., Completamento Registrazione", "34g6zC": "Modifica tratti", @@ -172,6 +177,7 @@ "4l6vz1": "Copia", "4nfQk6": "Cattura errori ed eccezioni JavaScript dal tuo sito", "4v7I1t": "Esclusioni di Percorso", + "4vQ9fL": "Il tracciamento degli errori cattura errori ed eccezioni dalla tua applicazione.", "5/6deG": "Si è verificato un problema nel recupero dei funnel. Riprova più tardi.", "52UMW3": "Tutto il tempo", "54Ob9x": "Sì, sposta sito", @@ -182,12 +188,14 @@ "5QiCvx": "Seleziona almeno un'autorizzazione oppure disattiva le restrizioni per una chiave con accesso completo", "5U2wOH": "Aggiungi variante", "5WuADX": "Aggiungi Obiettivo", + "5Zmqv6": "Carica icona", "5bo+CW": "Nessuna chiave API ancora", "5dX7fM": "Funzionalità avanzate per team aziendali", "5elgIe": "Frequenza di Rimbalzo", "5iGg8I": "Dati ancora insufficienti", "5jeq8P": "Sconosciuto", "5kpx3b": "Distribuisci il traffico tra le chiavi", + "5qOEz+": "documentazione del Flutter SDK", "5sg7KC": "Password", "5y2qWO": "Connessione in corso...", "5yUsUe": "Riproduzione", @@ -196,6 +204,7 @@ "62nsdy": "Riprova", "65uDP/": "Il traffico da bot e crawler noti non verrà tracciato", "66kGA0": "Runtime", + "67Q/sm": "Formato del dominio non valido", "68djwb": "Nessun team ancora", "69jzMr": "Dominio aggiornato con successo", "6ErXbc": "è null", @@ -207,6 +216,7 @@ "6RB7ZL": "Playground API", "6RlEDq": "Impossibile impersonare l'utente", "6S3VIt": "Impossibile salvare il funnel: {message}", + "6TFjfy": "Tipo Azienda", "6V/E/f": "Impossibile eliminare il sito", "6VzeBI": "Nessuna sessione convertita per questo obiettivo nel periodo di tempo selezionato.", "6Z97iS": "Impossibile eliminare l'organizzazione", @@ -252,6 +262,7 @@ "7nUCu9": "Fuso Orario", "7pXrnq": "giorno", "7q/cDS": "Settimana {index}", + "7qesNi": "Dominio Azienda", "7ufubd": "Misura l'interattività. INP valuta la reattività misurando il tempo dall'interazione dell'utente alla verniciatura successiva.", "7x+EYZ": "{count} eventi/mese", "81yKPX": "In base al tuo attuale modello di utilizzo", @@ -354,6 +365,7 @@ "BffFao": "Il tracciamento degli errori cattura gli errori e le eccezioni JavaScript della tua applicazione.", "BhbATb": "Corrisponde al testo copiato. Lascia vuoto per conteggiare qualsiasi testo copiato.", "BhlBsq": "Organizzazione eliminata con successo", + "BiyUAo": "Aggiungi il Rybbit SDK alla tua app:", "BkOSF2": "Parametri", "C2EM7h": "Email Proprietario", "C2iTEH": "In pausa", @@ -364,6 +376,7 @@ "CHR+o1": "Mostrando {shown} di {total} eventi", "CHTM5p": "Impossibile inviare l'invito", "CHzo7t": "Codice di Incorporamento", + "CLGask": "Icona dell'app", "CNXQN5": "Copia codice", "CRAx66": "Gli ID utente verranno crittografati con una chiave rotante giornaliera per una maggiore privacy", "CRjhOx": "8x più grande", @@ -410,6 +423,7 @@ "E/O9LL": "Facoltativo — descrivi cosa controlla questo flag", "E/tVTI": "Tracciamento interazioni moduli disabilitato", "E2rh1p": "Pulsanti", + "E3X5ej": "Schermate di ingresso", "E3oB+t": "Browser", "E4L2LT": "Abbandonati ({count})", "E9m5xj": "Utente Identificato", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} nuovi eventi", "G1YoMt": "Impossibile eliminare l'importazione. Riprova.", "G2HskF": "Esperimento eliminato", + "G7U5Ai": "Non corrisponde alla regex", "G7giEo": "Media 30g", "GBIt8l": "Percorsi che finiscono in .pdf, .doc, o .docx", "GDgEmC": "Nessun evento trovato", @@ -557,6 +572,7 @@ "J6C2uX": "L'ID di questa persona nel tuo sistema, come un'e-mail o un ID account.", "JCIgkj": "Nome utente", "JD2pwH": "Organizzazioni", + "JEsxDw": "Caricamento...", "JIOFW/": "5 min", "JIbDU1": "Ultimo canale", "JKxOsh": "Eventi Mensili", @@ -616,6 +632,7 @@ "L44v6R": "Questa operazione elimina definitivamente tutti i dati analitici di questo utente, inclusi eventi, sessioni, replay delle sessioni e tratti su tutti i dispositivi collegati. Questa azione non può essere annullata.", "L5X96Z": "Modelli URL da escludere dal tracciamento (uno per riga)", "L8DzHm": "Il nome del team è obbligatorio", + "L9CtJP": "Guide della Piattaforma", "LAwI3R": "Il nome non può essere vuoto", "LB/Amg": "La tua prova gratuita è scaduta. Abbonati a un piano per continuare a tracciare di nuovo le visite.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "Tracciamento parametri URL disabilitato", "LONGFB": "Scegli piano", "LQ46NL": "Coordinate", + "LUJTwh": "Icona caricata", "LUrJwE": "Canali", "LaV9M1": "Seleziona fuso orario", "LeFOAk": "Filtro non attivo per questa pagina", @@ -651,6 +669,8 @@ "MPAiyo": "Hai un'importazione attiva in corso. Attendi il completamento prima di avviare una nuova importazione.", "MPQ0kS": "Seleziona un'organizzazione", "MQQbnY": "Replay", + "MUaUWe": "Caricamento chiavi API...", + "MVEvjb": "Rimuovi icona", "MYsZNO": "Rimuovi variante", "MZNJkb": "Filtra per nome...", "MZjNWA": "Impossibile aggiornare l'impostazione dei report email", @@ -717,6 +737,7 @@ "OtSSs+": "{count} siti", "Otefae": "Dimensione", "OvzONl": "Disattivo", + "OwQmnm": "Versione dell'app", "P3H0J/": "Mostra di più ({count} in più)", "P3YB9A": "Settimana Scorsa", "P6RGqE": "Hostname", @@ -730,8 +751,10 @@ "PWxvnC": "Chiedi a un amministratore del sito di connettere Google Search Console.", "PZOOdj": "Inserisci un oggetto JSON valido", "PbRfhj": "Corrisponde al testo visibile del pulsante. Lascia vuoto per conteggiare qualsiasi clic sul pulsante.", + "Pbtcej": "Visualizzazioni schermo", "Pc+tM3": "Genera", "Pe0ogR": "Tema", + "PfUTPz": "VPN", "Pgj6mf": "Eventi questo mese", "Pgv4+O": "Ancora nessun funnel", "PkGCG1": "Genera un link privato per provare l'incorporamento della dashboard.", @@ -763,6 +786,7 @@ "QgMK2h": "Sorgente UTM", "QiKKFz": "Cambia Password", "QirE3M": "Data di Inizio", + "QitbBm": "Visualizzazione schermo", "QkTyho": "Barra Laterale Rybbit", "Qp/Bnx": "Nessun utente corrisponde ai filtri attuali", "Qp1beM": "Percorso", @@ -794,6 +818,7 @@ "Rqzsq/": "Aggiungi Filtro", "RsY5rI": "Sessioni Convertite", "RumeRz": "Regioni", + "RvxWt3": "Schermata di ingresso:", "RwzAp9": "Tipi di Evento", "RzSw2z": "Testo del pulsante (opzionale)", "S1utKH": "Long", @@ -801,6 +826,7 @@ "S4zxrU": "Conta le sessioni in cui la tua app genera un evento con nome.", "S5IFjk": "Crea un team per raggruppare i siti e gestire l'accesso dei membri.", "S7U3Nx": "Ti stai avvicinando al tuo limite di eventi mensili. Considera di passare a un piano a pagamento per limiti superiori.", + "S9jCu8": "Icona rimossa", "SB3+JE": "Nessun dato del funnel disponibile", "SBUMrd": "Filtra feature flag", "SBoJg8": "Eliminare questa chiave API?", @@ -812,6 +838,7 @@ "SWvHLr": "Questa Settimana", "SX126d": "Esporta dati", "Sb7oIl": "Prestazioni per Sistema Operativo", + "ShHw0c": "Versione app:", "Sjo1P4": "Personalizzato", "SnIGkx": "Questa è l'unica volta in cui vedrai questa chiave. Copiala ora e conservala in un luogo sicuro.", "SoySgJ": "Referrer:", @@ -907,6 +934,7 @@ "VqI0Nz": "Posizione & Dispositivo", "VtLIgh": "Una chiave dell'organizzazione è la credenziale dell'organizzazione stessa: può accedere a tutti i siti di questa organizzazione e continua a funzionare quando i membri se ne vanno. Usala per le integrazioni di produzione.", "Vwukkb": "Aggiungi payload JSON", + "VxUjLr": "Aggiungi app", "VyvTDF": "Schermo:", "VzzYJk": "Crea", "W/V6+Y": "Comprimi", @@ -927,6 +955,7 @@ "WnQ/AZ": "Contattaci", "WuGkfJ": "Restituisce", "WuHoZ9": "Conversioni", + "X0cqyR": "Versione Sistema Operativo", "X0ha1a": "Salva modifiche", "X0ppsC": "siti web", "X2PZ7D": "Crea Obiettivo", @@ -1062,6 +1091,7 @@ "cfiGPy": "Nuovo Parametro", "ciDaNU": "Traccia automaticamente la prima visualizzazione di pagina al caricamento dello script", "cj34Wi": "Nessun ID organizzazione restituito", + "ckAea8": "Dominio ASN", "ckBJVF": "Questa azione non può essere annullata. Il sito \"{siteName}\" e tutti i suoi dati analitici verranno eliminati definitivamente.", "cmDzrA": "Si è verificato un problema nel recupero degli eventi. Riprova più tardi.", "cmGAVy": "Dimensione pagina:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "Usa la chiave del flag generata nella tua app e differenzia UI o comportamento per variante.", "dv4SpB": "Ancora nessuna importazione", "e/U0Xt": "Tasso di Conversione", + "e/WYt2": "Modello del dispositivo", "eA5EH7": "Cerca organizzazioni...", "eDqstJ": "Eventi 24h", "eGIKNJ": "Pagine Principali", @@ -1099,6 +1130,7 @@ "eKEL/g": "In attesa", "eOj4Bs": "Nome evento", "eQnbde": "Disconnettere Google Search Console?", + "eXWZOl": "Versioni dell'app", "eYX7hS": "Aggiorna il tuo indirizzo email per le notifiche dell'account", "ebEqgQ": "Hai un abbonamento attivo. Annulla il tuo abbonamento prima di eliminare l'organizzazione.", "ekXood": "Illimitato", @@ -1111,6 +1143,7 @@ "f5efCD": "Scegli il risultato", "f7uwif": "Organizza i siti in team per controllare quali membri possono accedervi.", "fB09El": "Percorsi che iniziano con /blog/", + "fCvg+B": "Corrisponde alla regex", "fDjNLX": "Stai per importare un file di grandi dimensioni ({size}). Questo potrebbe richiedere diversi minuti. Sei sicuro di voler continuare?", "fGblXC": "O segui una guida di configurazione per la tua piattaforma", "fItAD9": "Coorte", @@ -1132,6 +1165,7 @@ "fobMp1": "I punteggi TTFB buoni sono di 800ms o più veloci.", "fr4eNi": "Abbonamento di prova da 1$ per lo sviluppo", "frN4vy": "Visto per l'ultima volta", + "fsUN9Y": "Min. visualizzazioni schermo", "fx99AL": "Debole", "fz0z4c": "Piano", "g12Dke": "Crescita", @@ -1224,6 +1258,7 @@ "isUnnK": "Raccolta dei Web Vitals abilitata", "itC9lG": "Informazioni di base", "iv7EVe": "Tutti i siti", + "iwp2S0": "Schermate", "izvHMl": "Identifica", "j3ehbn": "Usa una password più forte", "j5kA5A": "Si è verificato un problema nel recupero degli errori. Riprova più tardi.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Riprova", "jt9BUv": "Nessuna conversione ancora", "jvo0vs": "Salva", + "jxsBGE": "Visualizzata come favicon nella barra laterale, nel selettore dei siti e nelle schede dei siti. Accetta qualsiasi formato immagine — ridimensionata automaticamente a 128×128 PNG. Max 50 KB.", "k+kHlN": "Nome del team", "k/CG+N": "Le chiavi delle varianti sono obbligatorie", "k1sXXZ": "Impossibile generare la query", @@ -1299,6 +1335,7 @@ "lILp7K": "Blocco bot disabilitato", "lKv8ex": "Predefinito", "lRnHfd": "Validazione eventi fallita", + "lTavFx": "Schermate di uscita", "lVP3yI": "Nessun valore necessario", "lXZjmr": "Invita un nuovo membro in questa organizzazione.", "lYB8jy": "{count} utenti", @@ -1346,6 +1383,7 @@ "nGQuNF": "Pagine per Sessione", "nIXcfs": "Impossibile copiare negli appunti. Seleziona la chiave e copiala manualmente.", "nIe+Wv": "Crea nuovo utente", + "nJNzqM": "Caricamento dell'icona non riuscito", "nJuUfX": "Cerca per nome, slug, dominio o email del membro...", "nL6Jl0": "Fidelizzazione", "nVYMBG": "Crea utente", @@ -1367,6 +1405,7 @@ "o5Jj28": "Impossibile eliminare l'account", "o5XOA3": "{percent}% del limite di eventi della tua organizzazione", "o7g8s9": "es. ^/blog/.*", + "o9wmBB": "Facoltativo. Ridimensionata a 128x128 PNG.", "oA0ogD": "Entrambi", "oAFgOq": "Dominio", "oCBPQ4": "Rimuovi filtro", @@ -1422,6 +1461,7 @@ "q0NdNV": "Mostra i paesi principali", "q2ZLFN": "Feature flag eliminato", "q4S4cE": "Log della console", + "qAktpY": "Schermate per sessione", "qB5AJG": "Carica un file CSV per iniziare", "qDwvZ4": "Errore sconosciuto", "qFQkeR": "Imposta una nuova organizzazione per aggiungere siti web e utenti", @@ -1486,6 +1526,7 @@ "scPXsn": "regex", "sd2ieZ": "settimana", "siqsij": "Solo il proprietario dell'organizzazione può gestire l'abbonamento.", + "smsDJm": "Rimozione dell'icona non riuscita", "snh6R6": "Etichetta (opzionale)", "soeWeh": "Impostazioni organizzazione", "sy+pv5": "E-mail", @@ -1499,6 +1540,7 @@ "tGAYL2": "Seleziona un'opzione", "tGWccD": "Aggiungi maiuscole, minuscole, numeri o simboli per una password più forte", "tGuKrF": "Nuovo esperimento", + "tH05Pi": "Modello dispositivo:", "tH8S0d": "Aggiungi IP", "tHKpoN": "Nessuna sessione è stata abbandonata prima di raggiungere questo passaggio nel periodo di tempo selezionato.", "tHSb8p": "Desktop", @@ -1655,6 +1697,7 @@ "yi/uLq": "eventi", "yl0JSO": "Modificare queste note non cambia l'assegnazione o il tracciamento.", "ym0FiC": "Aggiorna il tuo piano per continuare a raccogliere analisi.", + "ynio5d": "Formato del nome del pacchetto non valido", "ytceSa": "Nessun obiettivo collegato", "yunf47": "Durata Sessione", "yxDujp": "Aggiungi tratto", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Tutto", "zSOvI0": "Filtri", "zUJjPA": "Nessun team", + "zUxWZC": "Datacenter", "zWgbGg": "Oggi", "zWkvNO": "Cronologia", "zY8xSk": "Inserisci il dominio del sito web che vuoi tracciare", "zZE+e+": "Gli esperimenti utilizzano feature flag multivarianti per assegnare ogni visitatore a una variante stabile.", + "zff4lu": "", "znwd1N": "Elimina Funnel", "zoRFJV": "L'identificatore bundle o pacchetto utilizzato per il tracciamento", "zuA3Bg": "YouTube", diff --git a/client/messages/ja.json b/client/messages/ja.json index e17a24255..834b10302 100644 --- a/client/messages/ja.json +++ b/client/messages/ja.json @@ -25,6 +25,7 @@ "+x2FLn": "リプレイ", "+zImMB": "ユーザーを削除しました", "/+XPOf": "組織のイベント", + "/0J7Xq": "アイコン画像の処理に失敗しました", "/0TOL5": "金額", "/0tjRq": "サイトの移動に失敗しました", "/20q2D": "パスワードが正常にリセットされました。", @@ -49,6 +50,7 @@ "/clOBU": "週別", "/dxF+k": "パスワードは8文字以上である必要があります", "/eULaT": "ページ別パフォーマンス", + "/g8hj9": "ASNタイプ", "/jyLDY": "インラインピル", "/kZg9i": "ボタンクリック", "/liMUf": "ファネルを削除しました", @@ -68,6 +70,7 @@ "0VV/sK": "ゴール", "0WpQtp": "リテンションデータの読み込みに失敗しました", "0cEOKu": "詳細オプション", + "0cOykX": "ASN組織", "0eYAOP": "未実行", "0eoYsu": "直帰率", "0fs/5S": "開始日時", @@ -95,6 +98,7 @@ "1bNJRY": "ユーザー属性", "1bqKv/": "ユーザーID:", "1fe3VI": "API キーはスタンダードプランおよびプロプランでご利用いただけます。", + "1fzzQF": "クローラー", "1gYD1y": "セッション情報", "1hGB1j": "ログインエラー", "1iEPTM": "一般", @@ -121,6 +125,7 @@ "2YpSsF": "アプリ識別子を空にすることはできません", "2ZpsMl": "メンバーなし", "2eRO6y": "デバイスタイプ", + "2rUVsU": "アプリ", "2tOWr2": "含む", "34eVnl": "例:サインアップ完了", "34g6zC": "属性を編集", @@ -172,6 +177,7 @@ "4l6vz1": "コピー", "4nfQk6": "サイトのJavaScriptエラーと例外をキャプチャ", "4v7I1t": "パス除外", + "4vQ9fL": "エラートラッキングはアプリケーションのエラーと例外をキャプチャします。", "5/6deG": "ファネルの取得中に問題が発生しました。後でもう一度お試しください。", "52UMW3": "全期間", "54Ob9x": "はい、サイトを移動", @@ -182,12 +188,14 @@ "5QiCvx": "少なくとも1つの権限を選択するか、フルアクセスキーにするには制限をオフにしてください", "5U2wOH": "バリアントを追加", "5WuADX": "ゴールを追加", + "5Zmqv6": "アイコンをアップロード", "5bo+CW": "APIキーはまだありません", "5dX7fM": "エンタープライズチーム向けの高度な機能", "5elgIe": "エラー:", "5iGg8I": "まだ十分なデータがありません", "5jeq8P": "不明", "5kpx3b": "キー間でトラフィックを分割", + "5qOEz+": "Flutter SDKドキュメント", "5sg7KC": "パスワード", "5y2qWO": "接続中...", "5yUsUe": "リプレイ", @@ -196,6 +204,7 @@ "62nsdy": "再試行", "65uDP/": "既知のボットやクローラーからのトラフィックは追跡されません", "66kGA0": "ランタイム", + "67Q/sm": "ドメイン形式が無効です", "68djwb": "チームがまだありません", "69jzMr": "ドメインを更新しました", "6ErXbc": "null である", @@ -207,6 +216,7 @@ "6RB7ZL": "APIプレイグラウンド", "6RlEDq": "ユーザーのなりすましに失敗しました", "6S3VIt": "ファネルの保存に失敗しました:{message}", + "6TFjfy": "企業タイプ", "6V/E/f": "サイトの削除に失敗しました", "6VzeBI": "選択した期間にこのゴールにコンバージョンしたセッションはありません。", "6Z97iS": "組織を削除できません", @@ -252,6 +262,7 @@ "7nUCu9": "タイムゾーン", "7pXrnq": "日", "7q/cDS": "{index}週目", + "7qesNi": "企業ドメイン", "7ufubd": "インタラクティブ性を測定します。INPはユーザー操作から次の描画までの時間を測定して応答性を評価します。", "7x+EYZ": "{count}イベント/月", "81yKPX": "現在の使用パターンに基づく", @@ -354,6 +365,7 @@ "BffFao": "エラートラッキングはアプリケーションのJavaScriptエラーと例外をキャプチャします。", "BhbATb": "コピーされたテキストと一致します。空欄の場合はすべてのコピーをカウントします。", "BhlBsq": "組織が正常に削除されました", + "BiyUAo": "アプリに Rybbit SDK を追加してください:", "BkOSF2": "パラメーター", "C2EM7h": "オーナーメール", "C2iTEH": "一時停止中", @@ -364,6 +376,7 @@ "CHR+o1": "{total}件中{shown}件のイベントを表示", "CHTM5p": "招待の送信に失敗しました", "CHzo7t": "埋め込みコード", + "CLGask": "アプリアイコン", "CNXQN5": "コードをコピー", "CRAx66": "ユーザーIDは日次ローテーションキーでソルトされ、プライバシーが強化されます", "CRjhOx": "8倍大きく", @@ -410,6 +423,7 @@ "E/O9LL": "任意 — このフラグが制御する内容を説明", "E/tVTI": "フォーム操作追跡を無効にしました", "E2rh1p": "ボタン", + "E3X5ej": "入口画面", "E3oB+t": "ブラウザ", "E4L2LT": "離脱({count})", "E9m5xj": "識別済みユーザー", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount}件の新しいイベント", "G1YoMt": "インポートの削除に失敗しました。もう一度お試しください。", "G2HskF": "実験を削除しました", + "G7U5Ai": "正規表現に一致しない", "G7giEo": "平均30日間", "GBIt8l": ".pdf、.doc、.docxで終わるパス", "GDgEmC": "イベントが見つかりません", @@ -557,6 +572,7 @@ "J6C2uX": "自社システムでのこの人物の ID(メールアドレスやアカウント ID など)。", "JCIgkj": "ユーザー名", "JD2pwH": "組織", + "JEsxDw": "アップロード中...", "JIOFW/": "5分", "JIbDU1": "直近のチャネル", "JKxOsh": "月間イベント", @@ -616,6 +632,7 @@ "L44v6R": "この操作により、リンクされたすべてのデバイスにおけるイベント、セッション、セッションリプレイ、属性を含む、このユーザーのすべての分析データが完全に削除されます。この操作は元に戻せません。", "L5X96Z": "トラッキングから除外するURLパターン(1行に1つ)", "L8DzHm": "チーム名は必須です", + "L9CtJP": "プラットフォームガイド", "LAwI3R": "名前を空にすることはできません", "LB/Amg": "無料トライアルが期限切れになりました。訪問の追跡を再開するにはプランをサブスクリプションしてください。", "LB7mg3": "秒", @@ -624,6 +641,7 @@ "LIX+6S": "URLパラメータ追跡を無効にしました", "LONGFB": "プランを選択", "LQ46NL": "座標", + "LUJTwh": "アイコンをアップロードしました", "LUrJwE": "チャネル", "LaV9M1": "タイムゾーンを選択", "LeFOAk": "このページではフィルターが無効です", @@ -651,6 +669,8 @@ "MPAiyo": "アクティブなインポートが進行中です。新しいインポートを開始する前に完了をお待ちください。", "MPQ0kS": "組織を選択", "MQQbnY": "リプレイ", + "MUaUWe": "APIキーを読み込み中...", + "MVEvjb": "アイコンを削除", "MYsZNO": "バリアントを削除", "MZNJkb": "名前でフィルター...", "MZjNWA": "メールレポート設定の更新に失敗しました", @@ -717,6 +737,7 @@ "OtSSs+": "{count}サイト", "Otefae": "ディメンション", "OvzONl": "オフ", + "OwQmnm": "アプリバージョン", "P3H0J/": "もっと表示(他{count}件)", "P3YB9A": "先週", "P6RGqE": "ホスト名", @@ -730,8 +751,10 @@ "PWxvnC": "Google Search Console に接続するよう、サイト管理者に依頼してください。", "PZOOdj": "有効な JSON オブジェクトを入力してください", "PbRfhj": "ボタンの表示テキストと一致します。空欄の場合はすべてのボタンクリックをカウントします。", + "Pbtcej": "スクリーンビュー", "Pc+tM3": "生成", "Pe0ogR": "テーマ", + "PfUTPz": "VPN", "Pgj6mf": "今月のイベント", "Pgv4+O": "ファネルはまだありません", "PkGCG1": "プライベートリンクを生成してダッシュボード埋め込みを試してみてください。", @@ -763,6 +786,7 @@ "QgMK2h": "UTMソース", "QiKKFz": "パスワードを変更", "QirE3M": "開始日", + "QitbBm": "スクリーンビュー", "QkTyho": "Rybbitサイドバー", "Qp/Bnx": "現在のフィルターに一致するユーザーがいません", "Qp1beM": "パス", @@ -794,6 +818,7 @@ "Rqzsq/": "フィルターを追加", "RsY5rI": "コンバージョンしたセッション", "RumeRz": "地域", + "RvxWt3": "入口画面:", "RwzAp9": "イベントタイプ", "RzSw2z": "ボタンテキスト(任意)", "S1utKH": "経度", @@ -801,6 +826,7 @@ "S4zxrU": "アプリが指定のイベントを発火したセッションをカウントします。", "S5IFjk": "サイトをグループ化してメンバーアクセスを管理するチームを作成します。", "S7U3Nx": "月間イベント上限に近づいています。より高い上限にするには有料プランへのアップグレードをご検討ください。", + "S9jCu8": "アイコンを削除しました", "SB3+JE": "ファネルデータがありません", "SBUMrd": "フィーチャーフラグを絞り込む", "SBoJg8": "このAPIキーを削除しますか?", @@ -812,6 +838,7 @@ "SWvHLr": "今週", "SX126d": "データをエクスポート", "Sb7oIl": "OS別パフォーマンス", + "ShHw0c": "アプリバージョン:", "Sjo1P4": "カスタム", "SnIGkx": "このキーが表示されるのはこの一度限りです。今すぐコピーして安全な場所に保存してください。", "SoySgJ": "リファラー:", @@ -907,6 +934,7 @@ "VqI0Nz": "場所とデバイス", "VtLIgh": "組織キーは組織自体の認証情報です。この組織のすべてのサイトにアクセスでき、メンバーが退会しても機能し続けます。本番環境の統合に使用してください。", "Vwukkb": "JSONペイロードを追加", + "VxUjLr": "アプリを追加", "VyvTDF": "画面:", "VzzYJk": "作成", "W/V6+Y": "折りたたむ", @@ -927,6 +955,7 @@ "WnQ/AZ": "お問い合わせ", "WuGkfJ": "戻り値", "WuHoZ9": "コンバージョン", + "X0cqyR": "OSバージョン", "X0ha1a": "変更を保存", "X0ppsC": "ウェブサイト", "X2PZ7D": "ゴールを作成", @@ -1062,6 +1091,7 @@ "cfiGPy": "新しいパラメータ", "ciDaNU": "スクリプト読み込み時に最初のページビューを自動追跡", "cj34Wi": "組織IDが返されませんでした", + "ckAea8": "ASNドメイン", "ckBJVF": "この操作は元に戻せません。サイト「{siteName}」とすべての分析データが完全に削除されます。", "cmDzrA": "イベントの取得中に問題が発生しました。後でもう一度お試しください。", "cmGAVy": "ページサイズ:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "生成されたフラグキーをアプリで使用し、バリアントによってUIや動作を分岐させてください。", "dv4SpB": "インポートはまだありません", "e/U0Xt": "コンバージョン率", + "e/WYt2": "デバイスモデル", "eA5EH7": "組織を検索...", "eDqstJ": "24時間イベント", "eGIKNJ": "上位ページ", @@ -1099,6 +1130,7 @@ "eKEL/g": "保留中", "eOj4Bs": "イベント名", "eQnbde": "Google Search Consoleを切断しますか?", + "eXWZOl": "アプリバージョン", "eYX7hS": "アカウント通知用のメールアドレスを更新", "ebEqgQ": "アクティブなサブスクリプションがあります。組織を削除する前にサブスクリプションをキャンセルしてください。", "ekXood": "無制限", @@ -1111,6 +1143,7 @@ "f5efCD": "結果を選ぶ", "f7uwif": "サイトをチームに整理して、どのメンバーがアクセスできるかを制御します。", "fB09El": "/blog/で始まるパス", + "fCvg+B": "正規表現に一致", "fDjNLX": "大きなファイル({size})をインポートしようとしています。処理に数分かかる場合があります。続行しますか?", "fGblXC": "またはプラットフォームのセットアップガイドをご覧ください", "fItAD9": "コホート", @@ -1132,6 +1165,7 @@ "fobMp1": "良好なTTFBスコアは800ms以内です。", "fr4eNi": "開発用$1テストサブスクリプション", "frN4vy": "最終訪問", + "fsUN9Y": "最小スクリーンビュー数", "fx99AL": "弱い", "fz0z4c": "プラン", "g12Dke": "成長", @@ -1224,6 +1258,7 @@ "isUnnK": "Web Vitals収集を有効にしました", "itC9lG": "基本設定", "iv7EVe": "すべてのサイト", + "iwp2S0": "画面", "izvHMl": "識別", "j3ehbn": "より強いパスワードを使用してください", "j5kA5A": "エラーの取得中に問題が発生しました。後でもう一度お試しください。", @@ -1257,6 +1292,7 @@ "jsy7pk": "再試行", "jt9BUv": "まだコンバージョンなし", "jvo0vs": "保存", + "jxsBGE": "サイドバー、サイトセレクター、サイトカードでファビコンとして表示されます。任意の画像形式に対応 — 自動的に 128×128 PNG にリサイズされます。最大 50 KB。", "k+kHlN": "チーム名", "k/CG+N": "バリアントキーは必須です", "k1sXXZ": "クエリの生成に失敗しました", @@ -1299,6 +1335,7 @@ "lILp7K": "ボットブロックを無効にしました", "lKv8ex": "デフォルト", "lRnHfd": "イベントがバリデーションに失敗しました", + "lTavFx": "出口画面", "lVP3yI": "値は不要", "lXZjmr": "この組織に新しいメンバーを招待します。", "lYB8jy": "{count}ユーザー", @@ -1346,6 +1383,7 @@ "nGQuNF": "セッションあたりのページ数", "nIXcfs": "クリップボードにコピーできませんでした。キーを選択して手動でコピーしてください。", "nIe+Wv": "新しいユーザーを作成", + "nJNzqM": "アイコンのアップロードに失敗しました", "nJuUfX": "名前、スラッグ、ドメイン、またはメンバーメールで検索...", "nL6Jl0": "リテンション", "nVYMBG": "ユーザーを作成", @@ -1367,6 +1405,7 @@ "o5Jj28": "アカウントを削除できません", "o5XOA3": "組織のイベント上限の{percent}%", "o7g8s9": "例: ^/blog/.*", + "o9wmBB": "任意。128x128 PNG にリサイズされます。", "oA0ogD": "両方", "oAFgOq": "ドメイン", "oCBPQ4": "フィルターを削除", @@ -1422,6 +1461,7 @@ "q0NdNV": "上位国を表示", "q2ZLFN": "フィーチャーフラグを削除しました", "q4S4cE": "コンソールログ", + "qAktpY": "セッションあたりの画面数", "qB5AJG": "CSVファイルをアップロードして開始", "qDwvZ4": "不明なエラー", "qFQkeR": "新しい組織を作成してウェブサイトとユーザーを追加", @@ -1486,6 +1526,7 @@ "scPXsn": "正規表現", "sd2ieZ": "週", "siqsij": "サブスクリプションを管理できるのは組織のオーナーのみです。", + "smsDJm": "アイコンの削除に失敗しました", "snh6R6": "ラベル(任意)", "soeWeh": "組織設定", "sy+pv5": "メール", @@ -1499,6 +1540,7 @@ "tGAYL2": "オプションを選択", "tGWccD": "大文字、小文字、数字、記号を追加してパスワードを強化してください", "tGuKrF": "新しい実験", + "tH05Pi": "デバイスモデル:", "tH8S0d": "IPを追加", "tHKpoN": "選択した期間にこのステップの前に離脱したセッションはありません。", "tHSb8p": "デスクトップ", @@ -1655,6 +1697,7 @@ "yi/uLq": "イベント", "yl0JSO": "これらのメモを変更しても、割り当てや追跡には影響しません。", "ym0FiC": "アナリティクスの収集を続けるにはプランをアップグレードしてください。", + "ynio5d": "パッケージ名の形式が無効です", "ytceSa": "ゴールが接続されていません", "yunf47": "セッション時間", "yxDujp": "属性を追加", @@ -1672,10 +1715,12 @@ "zQvVDJ": "すべて", "zSOvI0": "フィルター", "zUJjPA": "チームなし", + "zUxWZC": "データセンター", "zWgbGg": "今日", "zWkvNO": "タイムライン", "zY8xSk": "追跡したいウェブサイトのドメインを入力", "zZE+e+": "実験は多変量フィーチャーフラグを使用して、各訪問者を安定したバリアントに割り当てます。", + "zff4lu": "", "znwd1N": "ファネルを削除", "zoRFJV": "追跡に使用するバンドルまたはパッケージ識別子", "zuA3Bg": "YouTube", diff --git a/client/messages/ko.json b/client/messages/ko.json index bd04aeaac..b0a66651b 100644 --- a/client/messages/ko.json +++ b/client/messages/ko.json @@ -25,6 +25,7 @@ "+x2FLn": "재생", "+zImMB": "사용자가 삭제되었습니다", "/+XPOf": "조직 이벤트", + "/0J7Xq": "아이콘 이미지 처리에 실패했습니다", "/0TOL5": "금액", "/0tjRq": "사이트 이동 실패", "/20q2D": "비밀번호가 성공적으로 재설정되었습니다.", @@ -49,6 +50,7 @@ "/clOBU": "주별", "/dxF+k": "비밀번호는 8자 이상이어야 합니다", "/eULaT": "페이지별 성능", + "/g8hj9": "ASN 유형", "/jyLDY": "인라인 필", "/kZg9i": "버튼 클릭", "/liMUf": "퍼널이 성공적으로 삭제되었습니다", @@ -68,6 +70,7 @@ "0VV/sK": "목표", "0WpQtp": "리텐션 데이터 로드 실패", "0cEOKu": "고급 옵션", + "0cOykX": "ASN 조직", "0eYAOP": "실행 안 됨", "0eoYsu": "이탈률", "0fs/5S": "시작 시간", @@ -95,6 +98,7 @@ "1bNJRY": "사용자 특성", "1bqKv/": "사용자 ID:", "1fe3VI": "API 키는 스탠다드 및 프로 플랜에서 사용할 수 있습니다.", + "1fzzQF": "크롤러", "1gYD1y": "세션 정보", "1hGB1j": "로그인 오류", "1iEPTM": "일반", @@ -121,6 +125,7 @@ "2YpSsF": "앱 식별자는 비워둘 수 없습니다", "2ZpsMl": "멤버 없음", "2eRO6y": "기기 유형", + "2rUVsU": "앱", "2tOWr2": "포함", "34eVnl": "예: 가입 완료", "34g6zC": "특성 편집", @@ -172,6 +177,7 @@ "4l6vz1": "복사", "4nfQk6": "사이트에서 JavaScript 오류 및 예외를 캡처합니다", "4v7I1t": "경로 제외", + "4vQ9fL": "오류 추적은 애플리케이션의 오류 및 예외를 캡처합니다.", "5/6deG": "퍼널을 가져오는 중 문제가 발생했습니다. 나중에 다시 시도해 주세요.", "52UMW3": "전체 기간", "54Ob9x": "예, 사이트 이동", @@ -182,12 +188,14 @@ "5QiCvx": "권한을 하나 이상 선택하거나, 전체 액세스 키를 사용하려면 제한을 해제하세요", "5U2wOH": "변형 추가", "5WuADX": "목표 추가", + "5Zmqv6": "아이콘 업로드", "5bo+CW": "아직 API 키가 없습니다", "5dX7fM": "엔터프라이즈 팀을 위한 고급 기능", "5elgIe": "오류:", "5iGg8I": "아직 데이터가 충분하지 않습니다", "5jeq8P": "알 수 없음", "5kpx3b": "키별로 트래픽 분배", + "5qOEz+": "Flutter SDK 문서", "5sg7KC": "비밀번호", "5y2qWO": "연결 중...", "5yUsUe": "리플레이", @@ -196,6 +204,7 @@ "62nsdy": "재시도", "65uDP/": "알려진 봇 및 크롤러의 트래픽이 추적되지 않습니다", "66kGA0": "런타임", + "67Q/sm": "잘못된 도메인 형식", "68djwb": "아직 팀이 없습니다", "69jzMr": "도메인이 성공적으로 업데이트되었습니다", "6ErXbc": "null임", @@ -207,6 +216,7 @@ "6RB7ZL": "API 플레이그라운드", "6RlEDq": "사용자 가장 실패", "6S3VIt": "퍼널 저장 실패: {message}", + "6TFjfy": "회사 유형", "6V/E/f": "사이트 삭제 실패", "6VzeBI": "선택한 기간에 이 목표로 전환된 세션이 없습니다.", "6Z97iS": "조직을 삭제할 수 없습니다", @@ -252,6 +262,7 @@ "7nUCu9": "시간대", "7pXrnq": "일", "7q/cDS": "{index}주차", + "7qesNi": "회사 도메인", "7ufubd": "상호작용성을 측정합니다. INP는 사용자 상호작용에서 다음 페인트까지의 시간을 측정하여 응답성을 평가합니다.", "7x+EYZ": "{count}개 이벤트/월", "81yKPX": "현재 사용 패턴 기준", @@ -354,6 +365,7 @@ "BffFao": "오류 추적은 애플리케이션에서 JavaScript 오류 및 예외를 캡처합니다.", "BhbATb": "복사된 텍스트와 일치합니다. 비워두면 모든 복사된 텍스트를 집계합니다.", "BhlBsq": "조직이 성공적으로 삭제되었습니다", + "BiyUAo": "앱에 Rybbit SDK를 추가하세요:", "BkOSF2": "매개변수", "C2EM7h": "소유자 이메일", "C2iTEH": "일시정지됨", @@ -364,6 +376,7 @@ "CHR+o1": "{total}개 중 {shown}개 이벤트 표시", "CHTM5p": "초대 발송에 실패했습니다", "CHzo7t": "임베드 코드", + "CLGask": "앱 아이콘", "CNXQN5": "코드 복사", "CRAx66": "개인정보 보호 강화를 위해 사용자 ID가 일일 회전 키로 솔트됩니다", "CRjhOx": "8배 더 크게", @@ -410,6 +423,7 @@ "E/O9LL": "선택사항 — 이 플래그가 제어하는 내용을 설명하세요", "E/tVTI": "폼 상호작용 추적 비활성화됨", "E2rh1p": "버튼", + "E3X5ej": "진입 화면", "E3oB+t": "브라우저", "E4L2LT": "이탈 ({count})", "E9m5xj": "식별된 사용자", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount}개의 새 이벤트", "G1YoMt": "가져오기 삭제에 실패했습니다. 다시 시도해 주세요.", "G2HskF": "실험이 삭제되었습니다", + "G7U5Ai": "정규식 불일치", "G7giEo": "평균 30일", "GBIt8l": ".pdf, .doc 또는 .docx로 끝나는 경로", "GDgEmC": "이벤트를 찾을 수 없음", @@ -557,6 +572,7 @@ "J6C2uX": "이 사람이 내 시스템에서 갖는 ID입니다. 예: 이메일 또는 계정 ID.", "JCIgkj": "사용자명", "JD2pwH": "조직", + "JEsxDw": "업로드 중...", "JIOFW/": "5분", "JIbDU1": "최근 채널", "JKxOsh": "월별 이벤트", @@ -616,6 +632,7 @@ "L44v6R": "이 작업은 연결된 모든 기기에서 이벤트, 세션, 세션 리플레이, 특성을 포함한 이 사용자의 모든 분석 데이터를 영구적으로 삭제합니다. 이 작업은 취소할 수 없습니다.", "L5X96Z": "추적에서 제외할 URL 패턴 (줄당 하나)", "L8DzHm": "팀 이름이 필요합니다", + "L9CtJP": "플랫폼 가이드", "LAwI3R": "이름은 비워둘 수 없습니다", "LB/Amg": "무료 체험이 만료되었습니다. 방문 추적을 계속하려면 플랜을 구독하세요.", "LB7mg3": "초", @@ -624,6 +641,7 @@ "LIX+6S": "URL 매개변수 추적 비활성화됨", "LONGFB": "플랜 선택", "LQ46NL": "좌표", + "LUJTwh": "아이콘이 업로드되었습니다", "LUrJwE": "채널", "LaV9M1": "시간대 선택", "LeFOAk": "이 페이지에서 필터가 활성화되지 않음", @@ -651,6 +669,8 @@ "MPAiyo": "진행 중인 가져오기가 있습니다. 새 가져오기를 시작하기 전에 완료될 때까지 기다려 주세요.", "MPQ0kS": "조직 선택", "MQQbnY": "재생 목록", + "MUaUWe": "API 키 로딩 중...", + "MVEvjb": "아이콘 제거", "MYsZNO": "변형 제거", "MZNJkb": "이름으로 필터링...", "MZjNWA": "이메일 보고서 설정 업데이트에 실패했습니다", @@ -717,6 +737,7 @@ "OtSSs+": "{count}개 사이트", "Otefae": "차원", "OvzONl": "꺼짐", + "OwQmnm": "앱 버전", "P3H0J/": "더 보기 ({count}개 더)", "P3YB9A": "지난주", "P6RGqE": "호스트명", @@ -730,8 +751,10 @@ "PWxvnC": "사이트 관리자에게 Google Search Console 연결을 요청하세요.", "PZOOdj": "유효한 JSON 객체를 입력하세요", "PbRfhj": "버튼에 표시되는 텍스트와 일치합니다. 비워두면 모든 버튼 클릭을 집계합니다.", + "Pbtcej": "화면 조회수", "Pc+tM3": "생성", "Pe0ogR": "테마", + "PfUTPz": "VPN", "Pgj6mf": "이번 달 이벤트", "Pgv4+O": "아직 퍼널이 없습니다", "PkGCG1": "대시보드 임베드를 사용해보려면 비공개 링크를 생성하세요.", @@ -763,6 +786,7 @@ "QgMK2h": "UTM 소스", "QiKKFz": "비밀번호 변경", "QirE3M": "시작일", + "QitbBm": "화면 조회", "QkTyho": "Rybbit 사이드바", "Qp/Bnx": "현재 필터에 일치하는 사용자가 없습니다", "Qp1beM": "경로", @@ -794,6 +818,7 @@ "Rqzsq/": "필터 추가", "RsY5rI": "전환된 세션", "RumeRz": "지역", + "RvxWt3": "진입 화면:", "RwzAp9": "이벤트 유형", "RzSw2z": "버튼 텍스트(선택 사항)", "S1utKH": "경도", @@ -801,6 +826,7 @@ "S4zxrU": "앱이 특정 이름의 이벤트를 발생시킨 세션을 계산합니다.", "S5IFjk": "사이트를 그룹화하고 멤버 액세스를 관리하는 팀을 만듭니다.", "S7U3Nx": "월별 이벤트 한도에 근접하고 있습니다. 더 높은 한도를 위해 유료 플랜으로 업그레이드를 고려하세요.", + "S9jCu8": "아이콘이 제거되었습니다", "SB3+JE": "사용 가능한 퍼널 데이터 없음", "SBUMrd": "기능 플래그 필터", "SBoJg8": "이 API 키를 삭제하시겠습니까?", @@ -812,6 +838,7 @@ "SWvHLr": "이번 주", "SX126d": "데이터 내보내기", "Sb7oIl": "운영 체제별 성능", + "ShHw0c": "앱 버전:", "Sjo1P4": "사용자 정의", "SnIGkx": "이 키는 지금만 확인할 수 있습니다. 지금 복사하여 안전한 곳에 보관하세요.", "SoySgJ": "리퍼러:", @@ -907,6 +934,7 @@ "VqI0Nz": "위치 및 기기", "VtLIgh": "조직 키는 조직 자체의 자격 증명입니다. 이 조직의 모든 사이트에 액세스할 수 있으며, 멤버가 탈퇴해도 계속 작동합니다. 프로덕션 통합에 사용하세요.", "Vwukkb": "JSON 페이로드 추가", + "VxUjLr": "앱 추가", "VyvTDF": "화면:", "VzzYJk": "만들기", "W/V6+Y": "접기", @@ -927,6 +955,7 @@ "WnQ/AZ": "문의하기", "WuGkfJ": "반환값", "WuHoZ9": "전환", + "X0cqyR": "운영 체제 버전", "X0ha1a": "변경사항 저장", "X0ppsC": "웹사이트", "X2PZ7D": "목표 만들기", @@ -1062,6 +1091,7 @@ "cfiGPy": "새 매개변수", "ciDaNU": "스크립트 로드 시 첫 번째 페이지뷰를 자동으로 추적합니다", "cj34Wi": "조직 ID가 반환되지 않았습니다", + "ckAea8": "ASN 도메인", "ckBJVF": "이 작업은 취소할 수 없습니다. 사이트 \"{siteName}\"과 모든 분석 데이터가 영구적으로 삭제됩니다.", "cmDzrA": "이벤트를 가져오는 중 문제가 발생했습니다. 나중에 다시 시도해 주세요.", "cmGAVy": "페이지 크기:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "앱에서 생성된 플래그 키를 사용하고 변형에 따라 UI 또는 동작을 분기하세요.", "dv4SpB": "아직 가져오기 없음", "e/U0Xt": "전환율", + "e/WYt2": "기기 모델", "eA5EH7": "조직 검색...", "eDqstJ": "24시간 이벤트", "eGIKNJ": "인기 페이지", @@ -1099,6 +1130,7 @@ "eKEL/g": "대기 중", "eOj4Bs": "이벤트 이름", "eQnbde": "Google Search Console 연결을 해제하시겠습니까?", + "eXWZOl": "앱 버전", "eYX7hS": "계정 알림용 이메일 주소 업데이트", "ebEqgQ": "활성 구독이 있습니다. 조직을 삭제하기 전에 구독을 취소해 주세요.", "ekXood": "무제한", @@ -1111,6 +1143,7 @@ "f5efCD": "결과 선택", "f7uwif": "어떤 멤버가 사이트에 액세스할 수 있는지 제어하기 위해 팀에 사이트를 구성합니다.", "fB09El": "/blog/로 시작하는 경로", + "fCvg+B": "정규식 일치", "fDjNLX": "대용량 파일 ({size})을 가져오려 합니다. 처리하는 데 몇 분이 걸릴 수 있습니다. 계속하시겠습니까?", "fGblXC": "또는 플랫폼 설정 가이드를 따르세요", "fItAD9": "코호트", @@ -1132,6 +1165,7 @@ "fobMp1": "좋은 TTFB 점수는 800ms 이하입니다.", "fr4eNi": "개발용 $1 테스트 구독", "frN4vy": "마지막 방문", + "fsUN9Y": "최소 화면 조회수", "fx99AL": "약함", "fz0z4c": "플랜", "g12Dke": "성장", @@ -1224,6 +1258,7 @@ "isUnnK": "Web Vitals 수집 활성화됨", "itC9lG": "기본 설정", "iv7EVe": "모든 사이트", + "iwp2S0": "화면", "izvHMl": "식별", "j3ehbn": "더 강력한 비밀번호를 사용해 주세요", "j5kA5A": "오류를 가져오는 중 문제가 발생했습니다. 나중에 다시 시도해 주세요.", @@ -1257,6 +1292,7 @@ "jsy7pk": "다시 시도", "jt9BUv": "아직 전환 없음", "jvo0vs": "저장", + "jxsBGE": "사이드바, 사이트 선택기, 사이트 카드에서 파비콘으로 표시됩니다. 모든 이미지 형식을 지원하며 자동으로 128×128 PNG로 리사이즈됩니다. 최대 50 KB.", "k+kHlN": "팀 이름", "k/CG+N": "변형 키는 필수입니다", "k1sXXZ": "쿼리 생성에 실패했습니다", @@ -1299,6 +1335,7 @@ "lILp7K": "봇 차단 비활성화됨", "lKv8ex": "기본값", "lRnHfd": "유효성 검사에 실패한 이벤트", + "lTavFx": "종료 화면", "lVP3yI": "값 불필요", "lXZjmr": "이 조직에 새 멤버를 초대합니다.", "lYB8jy": "{count}명 사용자", @@ -1346,6 +1383,7 @@ "nGQuNF": "세션당 페이지", "nIXcfs": "클립보드에 복사할 수 없습니다. 키를 선택하여 수동으로 복사하세요.", "nIe+Wv": "새 사용자 생성", + "nJNzqM": "아이콘 업로드에 실패했습니다", "nJuUfX": "이름, 슬러그, 도메인 또는 멤버 이메일로 검색...", "nL6Jl0": "리텐션", "nVYMBG": "사용자 생성", @@ -1367,6 +1405,7 @@ "o5Jj28": "계정을 삭제할 수 없습니다", "o5XOA3": "조직 이벤트 한도의 {percent}%", "o7g8s9": "예: ^/blog/.*", + "o9wmBB": "선택 사항. 128x128 PNG로 리사이즈됩니다.", "oA0ogD": "모두", "oAFgOq": "도메인", "oCBPQ4": "필터 제거", @@ -1422,6 +1461,7 @@ "q0NdNV": "상위 국가 표시", "q2ZLFN": "기능 플래그가 삭제되었습니다", "q4S4cE": "콘솔 로그", + "qAktpY": "세션당 화면 수", "qB5AJG": "시작하려면 CSV 파일을 업로드하세요", "qDwvZ4": "알 수 없는 오류", "qFQkeR": "웹사이트와 사용자를 추가할 새 조직을 설정하세요", @@ -1486,6 +1526,7 @@ "scPXsn": "정규식", "sd2ieZ": "주", "siqsij": "조직 소유자만 구독을 관리할 수 있습니다.", + "smsDJm": "아이콘 제거에 실패했습니다", "snh6R6": "라벨 (선택사항)", "soeWeh": "조직 설정", "sy+pv5": "이메일", @@ -1499,6 +1540,7 @@ "tGAYL2": "옵션 선택", "tGWccD": "더 강력한 비밀번호를 위해 대문자, 소문자, 숫자 또는 기호를 추가하세요", "tGuKrF": "새 실험", + "tH05Pi": "기기 모델:", "tH8S0d": "IP 추가", "tHKpoN": "선택한 기간에 이 단계 전에 이탈한 세션이 없습니다.", "tHSb8p": "데스크톱", @@ -1655,6 +1697,7 @@ "yi/uLq": "이벤트", "yl0JSO": "이 메모를 변경해도 할당이나 추적에는 영향을 주지 않습니다.", "ym0FiC": "분석 수집을 계속하려면 플랜을 업그레이드하세요.", + "ynio5d": "잘못된 패키지 이름 형식", "ytceSa": "연결된 목표 없음", "yunf47": "세션 시간", "yxDujp": "특성 추가", @@ -1672,10 +1715,12 @@ "zQvVDJ": "전체", "zSOvI0": "필터", "zUJjPA": "팀 없음", + "zUxWZC": "데이터센터", "zWgbGg": "오늘", "zWkvNO": "타임라인", "zY8xSk": "추적할 웹사이트의 도메인을 입력하세요", "zZE+e+": "실험은 다변량 기능 플래그를 사용하여 각 방문자를 하나의 안정적인 변형에 할당합니다.", + "zff4lu": "", "znwd1N": "퍼널 삭제", "zoRFJV": "추적에 사용되는 번들 또는 패키지 식별자", "zuA3Bg": "YouTube", diff --git a/client/messages/pl.json b/client/messages/pl.json index a3e978c8b..3a19a55e3 100644 --- a/client/messages/pl.json +++ b/client/messages/pl.json @@ -25,6 +25,7 @@ "+x2FLn": "powtórka", "+zImMB": "Użytkownik usunięty", "/+XPOf": "Zdarzenia organizacji", + "/0J7Xq": "Nie udało się przetworzyć obrazu ikony", "/0TOL5": "Kwota", "/0tjRq": "Nie udało się przenieść witryny", "/20q2D": "Twoje hasło zostało pomyślnie zresetowane.", @@ -49,6 +50,7 @@ "/clOBU": "Tygodniowo", "/dxF+k": "Hasło musi mieć co najmniej 8 znaków", "/eULaT": "Wydajność według strony", + "/g8hj9": "Typ ASN", "/jyLDY": "Wbudowana pigułka", "/kZg9i": "Kliknięcie przycisku", "/liMUf": "Lejek usunięty pomyślnie", @@ -68,6 +70,7 @@ "0VV/sK": "Cel", "0WpQtp": "Nie udało się załadować danych retencji", "0cEOKu": "Opcje zaawansowane", + "0cOykX": "Organizacja ASN", "0eYAOP": "Nie uruchomiono", "0eoYsu": "Współczynnik odrzuceń", "0fs/5S": "Rozpoczęto", @@ -95,6 +98,7 @@ "1bNJRY": "Cechy użytkownika", "1bqKv/": "ID użytkownika:", "1fe3VI": "Klucze API są dostępne w planach Standard i Pro.", + "1fzzQF": "Crawler", "1gYD1y": "Informacje o sesji", "1hGB1j": "Błąd logowania", "1iEPTM": "Ogólne", @@ -121,6 +125,7 @@ "2YpSsF": "Identyfikator aplikacji nie może być pusty", "2ZpsMl": "Brak członków", "2eRO6y": "Typ urządzenia", + "2rUVsU": "Aplikacja", "2tOWr2": "Zawiera", "34eVnl": "np. Zakończenie rejestracji", "34g6zC": "Edytuj cechy", @@ -172,6 +177,7 @@ "4l6vz1": "Kopiuj", "4nfQk6": "Przechwytuj błędy JavaScript i wyjątki ze swojej strony", "4v7I1t": "Wykluczenia ścieżek", + "4vQ9fL": "Śledzenie błędów przechwytuje błędy i wyjątki z Twojej aplikacji.", "5/6deG": "Wystąpił problem z pobieraniem lejków. Spróbuj ponownie później.", "52UMW3": "Cały czas", "54Ob9x": "Tak, przenieś witrynę", @@ -182,12 +188,14 @@ "5QiCvx": "Wybierz co najmniej jedno uprawnienie lub wyłącz ograniczenia, aby uzyskać klucz z pełnym dostępem", "5U2wOH": "Dodaj wariant", "5WuADX": "Dodaj cel", + "5Zmqv6": "Prześlij ikonę", "5bo+CW": "Brak kluczy API", "5dX7fM": "Zaawansowane funkcje dla zespołów korporacyjnych", "5elgIe": "Błąd:", "5iGg8I": "Jeszcze za mało danych", "5jeq8P": "Nieznany", "5kpx3b": "Podziel ruch między klucze", + "5qOEz+": "dokumentację Flutter SDK", "5sg7KC": "Hasło", "5y2qWO": "Łączenie...", "5yUsUe": "Odtwarzanie", @@ -196,6 +204,7 @@ "62nsdy": "Ponów", "65uDP/": "Ruch ze znanych botów i crawlerów nie będzie śledzony", "66kGA0": "Środowisko uruchomieniowe", + "67Q/sm": "Nieprawidłowy format domeny", "68djwb": "Brak zespołów", "69jzMr": "Domena zaktualizowana pomyślnie", "6ErXbc": "jest null", @@ -207,6 +216,7 @@ "6RB7ZL": "Plac zabaw API", "6RlEDq": "Nie udało się podszyć pod użytkownika", "6S3VIt": "Nie udało się zapisać lejka: {message}", + "6TFjfy": "Typ firmy", "6V/E/f": "Nie udało się usunąć strony", "6VzeBI": "Żadna sesja nie skonwertowała na ten cel w wybranym okresie.", "6Z97iS": "Nie można usunąć organizacji", @@ -252,6 +262,7 @@ "7nUCu9": "Strefa czasowa", "7pXrnq": "dzień", "7q/cDS": "Tydzień {index}", + "7qesNi": "Domena firmy", "7ufubd": "Mierzy interaktywność. INP ocenia responsywność, mierząc czas od interakcji użytkownika do następnego renderowania.", "7x+EYZ": "{count} zdarzeń/miesiąc", "81yKPX": "Na podstawie Twojego bieżącego wzorca wykorzystania", @@ -354,6 +365,7 @@ "BffFao": "Śledzenie błędów przechwytuje błędy JavaScript i wyjątki z Twojej aplikacji.", "BhbATb": "Dopasowuje skopiowany tekst. Pozostaw puste, aby liczyć każdy skopiowany tekst.", "BhlBsq": "Organizacja została pomyślnie usunięta", + "BiyUAo": "Dodaj Rybbit SDK do swojej aplikacji:", "BkOSF2": "Parametry", "C2EM7h": "E-mail właściciela", "C2iTEH": "Wstrzymany", @@ -364,6 +376,7 @@ "CHR+o1": "Wyświetlanie {shown} z {total} zdarzeń", "CHTM5p": "Nie udało się wysłać zaproszenia", "CHzo7t": "Kod osadzenia", + "CLGask": "Ikona aplikacji", "CNXQN5": "Kopiuj kod", "CRAx66": "ID użytkowników będą solone codziennie rotowanym kluczem dla zwiększonej prywatności", "CRjhOx": "8x większy", @@ -410,6 +423,7 @@ "E/O9LL": "Opcjonalnie — opisz, co kontroluje ta flaga", "E/tVTI": "Śledzenie interakcji z formularzami wyłączone", "E2rh1p": "Przyciski", + "E3X5ej": "Ekrany wejściowe", "E3oB+t": "Przeglądarka", "E4L2LT": "Porzuciło ({count})", "E9m5xj": "Zidentyfikowany użytkownik", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} nowych zdarzeń", "G1YoMt": "Nie udało się usunąć importu. Spróbuj ponownie.", "G2HskF": "Eksperyment usunięty", + "G7U5Ai": "Nie pasuje do regex", "G7giEo": "Śr. 30d", "GBIt8l": "Ścieżki kończące się na .pdf, .doc lub .docx", "GDgEmC": "Nie znaleziono zdarzeń", @@ -557,6 +572,7 @@ "J6C2uX": "Identyfikator tej osoby w Twoim systemie, np. e-mail lub ID konta.", "JCIgkj": "Nazwa użytkownika", "JD2pwH": "Organizacje", + "JEsxDw": "Przesyłanie...", "JIOFW/": "5 min", "JIbDU1": "Ostatni kanał", "JKxOsh": "Miesięczne zdarzenia", @@ -616,6 +632,7 @@ "L44v6R": "Spowoduje to trwałe usunięcie wszystkich danych analitycznych tego użytkownika, w tym zdarzeń, sesji, nagrań sesji i cech na wszystkich powiązanych urządzeniach. Tej akcji nie można cofnąć.", "L5X96Z": "Wzorce URL do wykluczenia ze śledzenia (jeden na linię)", "L8DzHm": "Nazwa zespołu jest wymagana", + "L9CtJP": "Przewodniki platform", "LAwI3R": "Nazwa nie może być pusta", "LB/Amg": "Twoja darmowa wersja próbna wygasła. Subskrybuj plan, aby ponownie śledzić odwiedziny.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "Śledzenie parametrów URL wyłączone", "LONGFB": "Wybierz plan", "LQ46NL": "Współrzędne", + "LUJTwh": "Ikona przesłana", "LUrJwE": "Kanały", "LaV9M1": "Wybierz strefę czasową", "LeFOAk": "Filtr nieaktywny dla tej strony", @@ -651,6 +669,8 @@ "MPAiyo": "Masz aktywny import w toku. Poczekaj na jego zakończenie przed rozpoczęciem nowego importu.", "MPQ0kS": "Wybierz organizację", "MQQbnY": "Powtórki", + "MUaUWe": "Ładowanie kluczy API...", + "MVEvjb": "Usuń ikonę", "MYsZNO": "Usuń wariant", "MZNJkb": "Filtruj według nazwy...", "MZjNWA": "Nie udało się zaktualizować ustawienia raportów e-mail", @@ -717,6 +737,7 @@ "OtSSs+": "{count} stron", "Otefae": "Wymiar", "OvzONl": "Wyłączone", + "OwQmnm": "Wersja aplikacji", "P3H0J/": "Pokaż więcej (jeszcze {count})", "P3YB9A": "Poprzedni tydzień", "P6RGqE": "Nazwy hostów", @@ -730,8 +751,10 @@ "PWxvnC": "Poproś administratora witryny o połączenie Google Search Console.", "PZOOdj": "Wprowadź prawidłowy obiekt JSON", "PbRfhj": "Dopasowuje widoczny tekst przycisku. Pozostaw puste, aby liczyć każde kliknięcie przycisku.", + "Pbtcej": "Wyświetlenia ekranów", "Pc+tM3": "Generuj", "Pe0ogR": "Motyw", + "PfUTPz": "VPN", "Pgj6mf": "Zdarzenia w tym miesiącu", "Pgv4+O": "Brak lejków", "PkGCG1": "Wygeneruj prywatny link, aby wypróbować osadzenie panelu.", @@ -763,6 +786,7 @@ "QgMK2h": "Źródło UTM", "QiKKFz": "Zmień hasło", "QirE3M": "Data rozpoczęcia", + "QitbBm": "Wyświetlenie ekranu", "QkTyho": "Panel boczny Rybbit", "Qp/Bnx": "Żaden użytkownik nie spełnia obecnych kryteriów filtrowania", "Qp1beM": "Ścieżka", @@ -794,6 +818,7 @@ "Rqzsq/": "Dodaj filtr", "RsY5rI": "Skonwertowane sesje", "RumeRz": "Regiony", + "RvxWt3": "Ekran wejściowy:", "RwzAp9": "Typy zdarzeń", "RzSw2z": "Tekst przycisku (opcjonalnie)", "S1utKH": "Dług. geog.", @@ -801,6 +826,7 @@ "S4zxrU": "Zliczaj sesje, w których aplikacja wywołuje zdarzenie o podanej nazwie.", "S5IFjk": "Utwórz zespół, aby grupować witryny i zarządzać dostępem członków.", "S7U3Nx": "Zbliżasz się do miesięcznego limitu zdarzeń. Rozważ ulepszenie do płatnego planu dla wyższych limitów.", + "S9jCu8": "Ikona usunięta", "SB3+JE": "Brak dostępnych danych lejka", "SBUMrd": "Filtruj flagi funkcji", "SBoJg8": "Usunąć ten klucz API?", @@ -812,6 +838,7 @@ "SWvHLr": "Ten tydzień", "SX126d": "Eksportuj dane", "Sb7oIl": "Wydajność według systemu operacyjnego", + "ShHw0c": "Wersja aplikacji:", "Sjo1P4": "Niestandardowy", "SnIGkx": "To jedyny moment, w którym zobaczysz ten klucz. Skopiuj go teraz i przechowuj w bezpiecznym miejscu.", "SoySgJ": "Źródło odesłania:", @@ -907,6 +934,7 @@ "VqI0Nz": "Lokalizacja i urządzenie", "VtLIgh": "Klucz organizacji to własne poświadczenie organizacji: może uzyskać dostęp do wszystkich witryn tej organizacji i nadal działa, gdy członkowie odejdą. Używaj go do integracji produkcyjnych.", "Vwukkb": "Dodaj ładunek JSON", + "VxUjLr": "Dodaj aplikację", "VyvTDF": "Ekran:", "VzzYJk": "Utwórz", "W/V6+Y": "Zwiń", @@ -927,6 +955,7 @@ "WnQ/AZ": "Skontaktuj się z nami", "WuGkfJ": "Zwraca", "WuHoZ9": "Konwersje", + "X0cqyR": "Wersja systemu operacyjnego", "X0ha1a": "Zapisz zmiany", "X0ppsC": "witryn", "X2PZ7D": "Utwórz cel", @@ -1062,6 +1091,7 @@ "cfiGPy": "Nowy parametr", "ciDaNU": "Automatycznie śledź pierwszą odsłonę strony po załadowaniu skryptu", "cj34Wi": "Nie zwrócono ID organizacji", + "ckAea8": "Domena ASN", "ckBJVF": "Tej akcji nie można cofnąć. Spowoduje to trwałe usunięcie witryny \"{siteName}\" i wszystkich jej danych analitycznych.", "cmDzrA": "Wystąpił problem z pobieraniem zdarzeń. Spróbuj ponownie później.", "cmGAVy": "Rozmiar strony:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "Użyj wygenerowanego klucza flagi w swojej aplikacji i rozgałęź interfejs lub zachowanie według wariantu.", "dv4SpB": "Brak importów", "e/U0Xt": "Współczynnik konwersji", + "e/WYt2": "Model urządzenia", "eA5EH7": "Szukaj organizacji...", "eDqstJ": "Zdarzenia 24h", "eGIKNJ": "Najpopularniejsze strony", @@ -1099,6 +1130,7 @@ "eKEL/g": "Oczekujące", "eOj4Bs": "Nazwa zdarzenia", "eQnbde": "Odłączyć Google Search Console?", + "eXWZOl": "Wersje aplikacji", "eYX7hS": "Zaktualizuj swój adres e-mail dla powiadomień konta", "ebEqgQ": "Masz aktywną subskrypcję. Anuluj subskrypcję przed usunięciem organizacji.", "ekXood": "Bez limitu", @@ -1111,6 +1143,7 @@ "f5efCD": "Wybierz wynik", "f7uwif": "Organizuj witryny w zespoły, aby kontrolować, którzy członkowie mogą do nich uzyskać dostęp.", "fB09El": "Ścieżki zaczynające się od /blog/", + "fCvg+B": "Pasuje do regex", "fDjNLX": "Zamierzasz zaimportować duży plik ({size}). Przetwarzanie może potrwać kilka minut. Czy na pewno chcesz kontynuować?", "fGblXC": "Lub postępuj zgodnie z przewodnikiem konfiguracji dla swojej platformy", "fItAD9": "Kohorta", @@ -1132,6 +1165,7 @@ "fobMp1": "Dobre wyniki TTFB to 800 ms lub szybciej.", "fr4eNi": "Subskrypcja testowa za $1 do celów deweloperskich", "frN4vy": "Ostatnia wizyta", + "fsUN9Y": "Min. wyświetleń", "fx99AL": "Słabe", "fz0z4c": "Plan", "g12Dke": "Wzrost", @@ -1224,6 +1258,7 @@ "isUnnK": "Zbieranie Web Vitals włączone", "itC9lG": "Podstawy", "iv7EVe": "Wszystkie strony", + "iwp2S0": "Ekrany", "izvHMl": "Zidentyfikuj", "j3ehbn": "Użyj silniejszego hasła", "j5kA5A": "Wystąpił problem z pobieraniem błędów. Spróbuj ponownie później.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Spróbuj ponownie", "jt9BUv": "Brak konwersji", "jvo0vs": "Zapisz", + "jxsBGE": "Wyświetlana jako favicon na pasku bocznym, w selektorze witryn i na kartach witryn. Akceptuje dowolny format obrazu — automatycznie skalowany do 128×128 PNG. Maks. 50 KB.", "k+kHlN": "Nazwa zespołu", "k/CG+N": "Klucze wariantów są wymagane", "k1sXXZ": "Nie udało się wygenerować zapytania", @@ -1299,6 +1335,7 @@ "lILp7K": "Blokowanie botów wyłączone", "lKv8ex": "Domyślna", "lRnHfd": "Zdarzenia nie przeszły walidacji", + "lTavFx": "Ekrany wyjściowe", "lVP3yI": "Wartość nie jest potrzebna", "lXZjmr": "Zaproś nowego członka do tej organizacji.", "lYB8jy": "{count} użytkowników", @@ -1346,6 +1383,7 @@ "nGQuNF": "Strony na sesję", "nIXcfs": "Nie udało się skopiować do schowka. Zaznacz klucz i skopiuj go ręcznie.", "nIe+Wv": "Utwórz nowego użytkownika", + "nJNzqM": "Nie udało się przesłać ikony", "nJuUfX": "Szukaj po nazwie, identyfikatorze, domenie lub e-mailu członka...", "nL6Jl0": "Retencja", "nVYMBG": "Utwórz użytkownika", @@ -1367,6 +1405,7 @@ "o5Jj28": "Nie można usunąć konta", "o5XOA3": "{percent}% limitu zdarzeń Twojej organizacji", "o7g8s9": "np. ^/blog/.*", + "o9wmBB": "Opcjonalne. Skalowane do 128x128 PNG.", "oA0ogD": "Oba", "oAFgOq": "Domena", "oCBPQ4": "Usuń filtr", @@ -1422,6 +1461,7 @@ "q0NdNV": "Pokaż najczęstsze kraje", "q2ZLFN": "Flaga funkcji usunięta", "q4S4cE": "Dziennik konsoli", + "qAktpY": "Ekrany na sesję", "qB5AJG": "Prześlij plik CSV, aby rozpocząć", "qDwvZ4": "Nieznany błąd", "qFQkeR": "Skonfiguruj nową organizację, aby dodać strony i użytkowników", @@ -1486,6 +1526,7 @@ "scPXsn": "regex", "sd2ieZ": "tydzień", "siqsij": "Tylko właściciel organizacji może zarządzać subskrypcją.", + "smsDJm": "Nie udało się usunąć ikony", "snh6R6": "Etykieta (opcjonalnie)", "soeWeh": "Ustawienia organizacji", "sy+pv5": "E-mail", @@ -1499,6 +1540,7 @@ "tGAYL2": "Wybierz opcję", "tGWccD": "Dodaj wielkie litery, małe litery, cyfry lub symbole, aby hasło było silniejsze", "tGuKrF": "Nowy eksperyment", + "tH05Pi": "Model urządzenia:", "tH8S0d": "Dodaj IP", "tHKpoN": "Żadna sesja nie porzuciła przed dotarciem do tego kroku w wybranym okresie.", "tHSb8p": "Komputer", @@ -1655,6 +1697,7 @@ "yi/uLq": "zdarzenia", "yl0JSO": "Zmiana tych notatek nie zmienia przypisania ani śledzenia.", "ym0FiC": "Ulepsz swój plan, aby kontynuować zbieranie danych analitycznych.", + "ynio5d": "Nieprawidłowy format nazwy pakietu", "ytceSa": "Brak połączonego celu", "yunf47": "Czas trwania sesji", "yxDujp": "Dodaj cechę", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Wszystko", "zSOvI0": "Filtry", "zUJjPA": "Brak zespołu", + "zUxWZC": "Centrum danych", "zWgbGg": "Dzisiaj", "zWkvNO": "Oś czasu", "zY8xSk": "Wprowadź domenę strony, którą chcesz śledzić", "zZE+e+": "Eksperymenty używają wielowariantowych flag funkcji, aby przypisać każdego odwiedzającego do jednego stabilnego wariantu.", + "zff4lu": "", "znwd1N": "Usuń lejek", "zoRFJV": "Identyfikator pakietu lub bundle używany do śledzenia", "zuA3Bg": "YouTube", diff --git a/client/messages/pt.json b/client/messages/pt.json index ef10633f7..66b59d4d8 100644 --- a/client/messages/pt.json +++ b/client/messages/pt.json @@ -25,6 +25,7 @@ "+x2FLn": "reprodução", "+zImMB": "Usuário excluído", "/+XPOf": "Eventos da organização", + "/0J7Xq": "Falha ao processar a imagem do ícone", "/0TOL5": "Valor", "/0tjRq": "Falha ao mover site", "/20q2D": "Sua senha foi redefinida com sucesso.", @@ -49,6 +50,7 @@ "/clOBU": "Semanal", "/dxF+k": "A senha deve ter pelo menos 8 caracteres", "/eULaT": "Desempenho por Página", + "/g8hj9": "Tipo ASN", "/jyLDY": "Pílula inline", "/kZg9i": "Clique no botão", "/liMUf": "Funil excluído com sucesso", @@ -68,6 +70,7 @@ "0VV/sK": "Meta", "0WpQtp": "Falha ao carregar dados de retenção", "0cEOKu": "Opções avançadas", + "0cOykX": "Org ASN", "0eYAOP": "Não executado", "0eoYsu": "Taxa de Rejeição", "0fs/5S": "Iniciado Em", @@ -95,6 +98,7 @@ "1bNJRY": "Atributos do Usuário", "1bqKv/": "ID do Usuário:", "1fe3VI": "As chaves de API estão disponíveis nos planos Standard e Pro.", + "1fzzQF": "Rastreador", "1gYD1y": "Info da Sessão", "1hGB1j": "Erro ao Entrar", "1iEPTM": "Geral", @@ -121,6 +125,7 @@ "2YpSsF": "O identificador do app não pode estar vazio", "2ZpsMl": "Sem membros", "2eRO6y": "Tipo de Dispositivo", + "2rUVsU": "Aplicativo", "2tOWr2": "Contém", "34eVnl": "ex., Conclusão de Cadastro", "34g6zC": "Editar atributos", @@ -172,6 +177,7 @@ "4l6vz1": "Copiar", "4nfQk6": "Capture erros e exceções JavaScript do seu site", "4v7I1t": "Exclusões de Caminho", + "4vQ9fL": "O rastreamento de erros captura erros e exceções do seu aplicativo.", "5/6deG": "Houve um problema ao buscar os funis. Por favor, tente novamente mais tarde.", "52UMW3": "Todo o Período", "54Ob9x": "Sim, mover site", @@ -182,12 +188,14 @@ "5QiCvx": "Selecione pelo menos uma permissão ou desative as restrições para uma chave de acesso total", "5U2wOH": "Adicionar variante", "5WuADX": "Adicionar Meta", + "5Zmqv6": "Enviar ícone", "5bo+CW": "Nenhuma chave de API ainda", "5dX7fM": "Recursos avançados para equipes empresariais", "5elgIe": "Erro:", "5iGg8I": "Ainda não há dados suficientes", "5jeq8P": "Desconhecido", "5kpx3b": "Distribuir tráfego entre chaves", + "5qOEz+": "documentação do Flutter SDK", "5sg7KC": "Senha", "5y2qWO": "Conectando...", "5yUsUe": "Repetição", @@ -196,6 +204,7 @@ "62nsdy": "Tentar Novamente", "65uDP/": "Tráfego de bots e crawlers conhecidos não será rastreado", "66kGA0": "Tempo de execução", + "67Q/sm": "Formato de domínio inválido", "68djwb": "Nenhum time ainda", "69jzMr": "Domínio atualizado com sucesso", "6ErXbc": "é nulo", @@ -207,6 +216,7 @@ "6RB7ZL": "Playground da API", "6RlEDq": "Falha ao representar usuário", "6S3VIt": "Falha ao salvar funil: {message}", + "6TFjfy": "Tipo de Empresa", "6V/E/f": "Falha ao excluir site", "6VzeBI": "Nenhuma sessão converteu para esta meta no período selecionado.", "6Z97iS": "Não é possível excluir a organização", @@ -252,6 +262,7 @@ "7nUCu9": "Fuso Horário", "7pXrnq": "dia", "7q/cDS": "Semana {index}", + "7qesNi": "Domínio da Empresa", "7ufubd": "Mede a interatividade. O INP avalia a responsividade medindo o tempo desde a interação do usuário até a próxima pintura.", "7x+EYZ": "{count} eventos/mês", "81yKPX": "Com base no seu padrão de uso atual", @@ -354,6 +365,7 @@ "BffFao": "O rastreamento de erros captura erros e exceções JavaScript da sua aplicação.", "BhbATb": "Corresponde ao texto copiado. Deixe em branco para contar qualquer texto copiado.", "BhlBsq": "Organização excluída com sucesso", + "BiyUAo": "Adicione o Rybbit SDK ao seu aplicativo:", "BkOSF2": "Parâmetros", "C2EM7h": "Email do Proprietário", "C2iTEH": "Pausado", @@ -364,6 +376,7 @@ "CHR+o1": "Mostrando {shown} de {total} eventos", "CHTM5p": "Falha ao enviar convite", "CHzo7t": "Código de Incorporação", + "CLGask": "Ícone do aplicativo", "CNXQN5": "Copiar código", "CRAx66": "IDs de usuário serão saltados com uma chave rotativa diária para maior privacidade", "CRjhOx": "8x maior", @@ -410,6 +423,7 @@ "E/O9LL": "Opcional — descreva o que esta flag controla", "E/tVTI": "Rastreamento de interação com formulários desativado", "E2rh1p": "Botões", + "E3X5ej": "Telas de entrada", "E3oB+t": "Navegador", "E4L2LT": "Abandonaram ({count})", "E9m5xj": "Usuário Identificado", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} novos eventos", "G1YoMt": "Falha ao excluir importação. Por favor, tente novamente.", "G2HskF": "Experimento excluído", + "G7U5Ai": "Não corresponde à regex", "G7giEo": "Média 30d", "GBIt8l": "Caminhos terminando em .pdf, .doc ou .docx", "GDgEmC": "Nenhum evento encontrado", @@ -557,6 +572,7 @@ "J6C2uX": "O ID desta pessoa no seu próprio sistema, como um e-mail ou ID de conta.", "JCIgkj": "Nome de Usuário", "JD2pwH": "Organizações", + "JEsxDw": "Enviando...", "JIOFW/": "5 min", "JIbDU1": "Último canal", "JKxOsh": "Eventos Mensais", @@ -616,6 +632,7 @@ "L44v6R": "Isso exclui permanentemente todos os dados analíticos deste usuário, incluindo eventos, sessões, replays de sessão e atributos em todos os dispositivos vinculados. Esta ação não pode ser desfeita.", "L5X96Z": "Padrões de URL para excluir do rastreamento (um por linha)", "L8DzHm": "Nome do time é obrigatório", + "L9CtJP": "Guias de Plataforma", "LAwI3R": "O nome não pode estar vazio", "LB/Amg": "Seu teste gratuito expirou. Assine um plano para continuar rastreando visitas novamente.", "LB7mg3": "s", @@ -624,6 +641,7 @@ "LIX+6S": "Rastreamento de parâmetros de URL desativado", "LONGFB": "Escolher plano", "LQ46NL": "Coordenadas", + "LUJTwh": "Ícone enviado", "LUrJwE": "Canais", "LaV9M1": "Selecionar fuso horário", "LeFOAk": "Filtro não ativo para esta página", @@ -651,6 +669,8 @@ "MPAiyo": "Você tem uma importação ativa em andamento. Por favor, aguarde a conclusão antes de iniciar uma nova importação.", "MPQ0kS": "Selecione uma organização", "MQQbnY": "Reproduções", + "MUaUWe": "Carregando chaves de API...", + "MVEvjb": "Remover ícone", "MYsZNO": "Remover variante", "MZNJkb": "Filtrar por nome...", "MZjNWA": "Falha ao atualizar configuração de relatórios por email", @@ -717,6 +737,7 @@ "OtSSs+": "{count} sites", "Otefae": "Dimensão", "OvzONl": "Desligado", + "OwQmnm": "Versão do aplicativo", "P3H0J/": "Mostrar mais ({count} mais)", "P3YB9A": "Semana Passada", "P6RGqE": "Nomes de host", @@ -730,8 +751,10 @@ "PWxvnC": "Peça a um administrador do site para conectar o Google Search Console.", "PZOOdj": "Insira um objeto JSON válido", "PbRfhj": "Corresponde ao texto visível do botão. Deixe em branco para contar qualquer clique em botão.", + "Pbtcej": "Visualizações de tela", "Pc+tM3": "Gerar", "Pe0ogR": "Tema", + "PfUTPz": "VPN", "Pgj6mf": "Eventos deste mês", "Pgv4+O": "Nenhum funil ainda", "PkGCG1": "Gere um link privado para experimentar a incorporação do painel.", @@ -763,6 +786,7 @@ "QgMK2h": "Fonte UTM", "QiKKFz": "Alterar Senha", "QirE3M": "Data Inicial", + "QitbBm": "Visualização de tela", "QkTyho": "Barra Lateral do Rybbit", "Qp/Bnx": "Nenhum usuário corresponde aos filtros atuais", "Qp1beM": "Caminho", @@ -794,6 +818,7 @@ "Rqzsq/": "Adicionar Filtro", "RsY5rI": "Sessões Convertidas", "RumeRz": "Regiões", + "RvxWt3": "Tela de entrada:", "RwzAp9": "Tipos de Evento", "RzSw2z": "Texto do botão (opcional)", "S1utKH": "Long", @@ -801,6 +826,7 @@ "S4zxrU": "Conta sessões onde seu app dispara um evento nomeado.", "S5IFjk": "Crie um time para agrupar sites e gerenciar o acesso dos membros.", "S7U3Nx": "Você está se aproximando do seu limite mensal de eventos. Considere atualizar para um plano pago para limites maiores.", + "S9jCu8": "Ícone removido", "SB3+JE": "Nenhum dado de funil disponível", "SBUMrd": "Filtrar feature flags", "SBoJg8": "Excluir esta chave de API?", @@ -812,6 +838,7 @@ "SWvHLr": "Esta Semana", "SX126d": "Exportar dados", "Sb7oIl": "Desempenho por Sistema Operacional", + "ShHw0c": "Versão do aplicativo:", "Sjo1P4": "Personalizado", "SnIGkx": "Esta é a única vez que você verá esta chave. Copie-a agora e guarde-a em um local seguro.", "SoySgJ": "Referenciador:", @@ -907,6 +934,7 @@ "VqI0Nz": "Localização e Dispositivo", "VtLIgh": "Uma chave da organização é a credencial da própria organização: ela pode acessar todos os sites desta organização e continua funcionando quando membros saem. Use-a para integrações de produção.", "Vwukkb": "Adicionar payload JSON", + "VxUjLr": "Adicionar aplicativo", "VyvTDF": "Tela:", "VzzYJk": "Criar", "W/V6+Y": "Recolher", @@ -927,6 +955,7 @@ "WnQ/AZ": "Fale conosco", "WuGkfJ": "Retorna", "WuHoZ9": "Conversões", + "X0cqyR": "Versão do Sistema Operacional", "X0ha1a": "Salvar alterações", "X0ppsC": "sites", "X2PZ7D": "Criar Meta", @@ -1062,6 +1091,7 @@ "cfiGPy": "Novo Parâmetro", "ciDaNU": "Rastreie automaticamente a primeira visualização de página quando o script carrega", "cj34Wi": "Nenhum ID de organização retornado", + "ckAea8": "Domínio ASN", "ckBJVF": "Esta ação não pode ser desfeita. Isso excluirá permanentemente o site \"{siteName}\" e todos os seus dados de análise.", "cmDzrA": "Houve um problema ao buscar os eventos. Por favor, tente novamente mais tarde.", "cmGAVy": "Tamanho da página:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "Use a chave de flag gerada no seu app e diferencie sua interface ou comportamento por variante.", "dv4SpB": "Nenhuma importação ainda", "e/U0Xt": "Taxa de Conversão", + "e/WYt2": "Modelo do dispositivo", "eA5EH7": "Pesquisar organizações...", "eDqstJ": "Eventos 24h", "eGIKNJ": "Páginas Mais Visitadas", @@ -1099,6 +1130,7 @@ "eKEL/g": "Pendente", "eOj4Bs": "Nome do evento", "eQnbde": "Desconectar Google Search Console?", + "eXWZOl": "Versões do aplicativo", "eYX7hS": "Atualize seu endereço de email para notificações da conta", "ebEqgQ": "Você tem uma assinatura ativa. Cancele sua assinatura antes de excluir a organização.", "ekXood": "Ilimitado", @@ -1111,6 +1143,7 @@ "f5efCD": "Escolha o resultado", "f7uwif": "Organize sites em times para controlar quais membros podem acessá-los.", "fB09El": "Caminhos começando com /blog/", + "fCvg+B": "Corresponde à regex", "fDjNLX": "Você está prestes a importar um arquivo grande ({size}). Isso pode levar vários minutos para processar. Tem certeza de que deseja continuar?", "fGblXC": "Ou siga um guia de configuração para sua plataforma", "fItAD9": "Coorte", @@ -1132,6 +1165,7 @@ "fobMp1": "Boas pontuações de TTFB são de 800ms ou menos.", "fr4eNi": "Assinatura de teste de $1 para desenvolvimento", "frN4vy": "Visto Por Último", + "fsUN9Y": "Mín. visualizações de tela", "fx99AL": "Fraca", "fz0z4c": "Plano", "g12Dke": "Crescimento", @@ -1224,6 +1258,7 @@ "isUnnK": "Coleta de Web Vitals ativada", "itC9lG": "Básico", "iv7EVe": "Todos os sites", + "iwp2S0": "Telas", "izvHMl": "Identificar", "j3ehbn": "Por favor, use uma senha mais forte", "j5kA5A": "Houve um problema ao buscar os erros. Por favor, tente novamente mais tarde.", @@ -1257,6 +1292,7 @@ "jsy7pk": "Tentar Novamente", "jt9BUv": "Nenhuma conversão ainda", "jvo0vs": "Salvar", + "jxsBGE": "Exibido como favicon na barra lateral, no seletor de sites e nos cartões de sites. Aceita qualquer formato de imagem — redimensionado automaticamente para 128×128 PNG. Máx. 50 KB.", "k+kHlN": "Nome do time", "k/CG+N": "As chaves das variantes são obrigatórias", "k1sXXZ": "Falha ao gerar consulta", @@ -1299,6 +1335,7 @@ "lILp7K": "Bloqueio de bots desativado", "lKv8ex": "Padrão", "lRnHfd": "Eventos falharam na validação", + "lTavFx": "Telas de saída", "lVP3yI": "Nenhum valor necessário", "lXZjmr": "Convide um novo membro para esta organização.", "lYB8jy": "{count} usuários", @@ -1346,6 +1383,7 @@ "nGQuNF": "Páginas por Sessão", "nIXcfs": "Não foi possível copiar para a área de transferência. Selecione a chave e copie-a manualmente.", "nIe+Wv": "Criar novo usuário", + "nJNzqM": "Falha ao enviar o ícone", "nJuUfX": "Pesquisar por nome, slug, domínio ou email do membro...", "nL6Jl0": "Retenção", "nVYMBG": "Criar usuário", @@ -1367,6 +1405,7 @@ "o5Jj28": "Não é possível excluir a conta", "o5XOA3": "{percent}% do limite de eventos da sua organização", "o7g8s9": "ex. ^/blog/.*", + "o9wmBB": "Opcional. Redimensionado para 128x128 PNG.", "oA0ogD": "Ambos", "oAFgOq": "Domínio", "oCBPQ4": "Remover filtro", @@ -1422,6 +1461,7 @@ "q0NdNV": "Mostrar principais países", "q2ZLFN": "Feature flag excluída", "q4S4cE": "Log do console", + "qAktpY": "Telas por sessão", "qB5AJG": "Envie um arquivo CSV para começar", "qDwvZ4": "Erro desconhecido", "qFQkeR": "Configure uma nova organização para adicionar sites e usuários", @@ -1486,6 +1526,7 @@ "scPXsn": "regex", "sd2ieZ": "semana", "siqsij": "Apenas o proprietário da organização pode gerenciar a assinatura.", + "smsDJm": "Falha ao remover o ícone", "snh6R6": "Rótulo (opcional)", "soeWeh": "Configurações da organização", "sy+pv5": "E-mail", @@ -1499,6 +1540,7 @@ "tGAYL2": "Selecione uma opção", "tGWccD": "Adicione maiúsculas, minúsculas, números ou símbolos para uma senha mais forte", "tGuKrF": "Novo experimento", + "tH05Pi": "Modelo do dispositivo:", "tH8S0d": "Adicionar IP", "tHKpoN": "Nenhuma sessão abandonou antes de alcançar esta etapa no período selecionado.", "tHSb8p": "Desktop", @@ -1655,6 +1697,7 @@ "yi/uLq": "eventos", "yl0JSO": "Alterar estas notas não muda a atribuição nem o rastreamento.", "ym0FiC": "Atualize seu plano para continuar coletando análises.", + "ynio5d": "Formato de nome de pacote inválido", "ytceSa": "Nenhuma meta conectada", "yunf47": "Duração da Sessão", "yxDujp": "Adicionar atributo", @@ -1672,10 +1715,12 @@ "zQvVDJ": "Todos", "zSOvI0": "Filtros", "zUJjPA": "Sem equipe", + "zUxWZC": "Datacenter", "zWgbGg": "Hoje", "zWkvNO": "Linha do Tempo", "zY8xSk": "Digite o domínio do site que deseja rastrear", "zZE+e+": "Os experimentos usam feature flags multivariadas para atribuir cada visitante a uma variante estável.", + "zff4lu": "", "znwd1N": "Excluir Funil", "zoRFJV": "O identificador de bundle ou pacote usado para rastreamento", "zuA3Bg": "YouTube", diff --git a/client/messages/zh.json b/client/messages/zh.json index 47d9a0c8d..24bcd76bf 100644 --- a/client/messages/zh.json +++ b/client/messages/zh.json @@ -25,6 +25,7 @@ "+x2FLn": "回放", "+zImMB": "用户已删除", "/+XPOf": "组织事件", + "/0J7Xq": "图标图片处理失败", "/0TOL5": "金额", "/0tjRq": "站点移动失败", "/20q2D": "您的密码已成功重置。", @@ -49,6 +50,7 @@ "/clOBU": "每周", "/dxF+k": "密码长度必须至少为 8 个字符", "/eULaT": "按页面划分的性能", + "/g8hj9": "ASN 类型", "/jyLDY": "内联标签", "/kZg9i": "按钮点击", "/liMUf": "漏斗删除成功", @@ -68,6 +70,7 @@ "0VV/sK": "目标", "0WpQtp": "加载留存数据失败", "0cEOKu": "高级选项", + "0cOykX": "ASN 组织", "0eYAOP": "未运行", "0eoYsu": "跳出率", "0fs/5S": "开始时间", @@ -95,6 +98,7 @@ "1bNJRY": "用户特征", "1bqKv/": "用户 ID:", "1fe3VI": "API 密钥在标准版和专业版计划中可用。", + "1fzzQF": "爬虫", "1gYD1y": "会话信息", "1hGB1j": "登录时出错", "1iEPTM": "常规", @@ -121,6 +125,7 @@ "2YpSsF": "应用标识符不能为空", "2ZpsMl": "无成员", "2eRO6y": "设备类型", + "2rUVsU": "应用", "2tOWr2": "包含", "34eVnl": "例如:完成注册", "34g6zC": "编辑特征", @@ -172,6 +177,7 @@ "4l6vz1": "复制", "4nfQk6": "捕获网站中的 JavaScript 错误和异常", "4v7I1t": "路径排除", + "4vQ9fL": "错误追踪捕获应用程序中的错误和异常。", "5/6deG": "获取漏斗时出现问题。请稍后重试。", "52UMW3": "所有时间", "54Ob9x": "是,移动站点", @@ -182,12 +188,14 @@ "5QiCvx": "至少选择一个权限,或关闭限制以创建完全访问密钥", "5U2wOH": "添加变体", "5WuADX": "添加目标", + "5Zmqv6": "上传图标", "5bo+CW": "暂无 API 密钥", "5dX7fM": "面向企业团队的高级功能", "5elgIe": "错误:", "5iGg8I": "数据尚不足", "5jeq8P": "未知", "5kpx3b": "跨键分配流量", + "5qOEz+": "Flutter SDK 文档", "5sg7KC": "密码", "5y2qWO": "正在连接...", "5yUsUe": "会话回放", @@ -196,6 +204,7 @@ "62nsdy": "重试", "65uDP/": "来自已知机器人和爬虫的流量将不会被跟踪", "66kGA0": "运行时", + "67Q/sm": "域名格式无效", "68djwb": "暂无团队", "69jzMr": "域名更新成功", "6ErXbc": "为空", @@ -207,6 +216,7 @@ "6RB7ZL": "API 演练场", "6RlEDq": "用户模拟失败", "6S3VIt": "保存漏斗失败:{message}", + "6TFjfy": "公司类型", "6V/E/f": "删除网站失败", "6VzeBI": "在选定的时间段内,没有会话转化为该目标。", "6Z97iS": "无法删除组织", @@ -252,6 +262,7 @@ "7nUCu9": "时区", "7pXrnq": "天", "7q/cDS": "第 {index} 周", + "7qesNi": "公司域名", "7ufubd": "衡量交互性。INP 通过测量从用户交互到下一次绘制的时间来评估响应性。", "7x+EYZ": "{count} 个事件/月", "81yKPX": "基于您当前的使用模式", @@ -354,6 +365,7 @@ "BffFao": "错误追踪可捕获应用程序中的 JavaScript 错误和异常。", "BhbATb": "匹配被复制的文本。留空则计入任意复制文本。", "BhlBsq": "组织删除成功", + "BiyUAo": "将 Rybbit SDK 添加到您的应用:", "BkOSF2": "参数", "C2EM7h": "所有者电子邮件", "C2iTEH": "已暂停", @@ -364,6 +376,7 @@ "CHR+o1": "显示第 {shown} 条,共 {total} 条事件", "CHTM5p": "发送邀请失败", "CHzo7t": "嵌入代码", + "CLGask": "应用图标", "CNXQN5": "复制代码", "CRAx66": "用户 ID 将通过每日轮换的密钥加盐,以增强隐私保护", "CRjhOx": "大 8 倍", @@ -410,6 +423,7 @@ "E/O9LL": "可选——描述此标志的控制内容", "E/tVTI": "表单交互追踪已禁用", "E2rh1p": "按钮", + "E3X5ej": "入口屏幕", "E3oB+t": "浏览器", "E4L2LT": "已流失 ({count})", "E9m5xj": "已识别的用户", @@ -473,6 +487,7 @@ "G0wWf0": "{bufferedCount} 个新事件", "G1YoMt": "删除导入失败。请重试。", "G2HskF": "实验已删除", + "G7U5Ai": "不匹配正则", "G7giEo": "平均 30天", "GBIt8l": "以 .pdf、.doc 或 .docx 结尾的路径", "GDgEmC": "未找到事件", @@ -557,6 +572,7 @@ "J6C2uX": "此人在您自己系统中的 ID,例如电子邮件或账户 ID。", "JCIgkj": "用户名", "JD2pwH": "组织", + "JEsxDw": "上传中...", "JIOFW/": "5 分钟", "JIbDU1": "最近渠道", "JKxOsh": "每月事件", @@ -616,6 +632,7 @@ "L44v6R": "这将永久删除该用户的所有分析数据,包括所有关联设备上的事件、会话、会话回放和特征。此操作无法撤销。", "L5X96Z": "要从跟踪中排除的 URL 模式(每行一个)", "L8DzHm": "团队名称不能为空", + "L9CtJP": "平台指南", "LAwI3R": "姓名不能为空", "LB/Amg": "您的免费试用已过期。订阅计划以继续追踪访问情况。", "LB7mg3": "秒", @@ -624,6 +641,7 @@ "LIX+6S": "URL 参数跟踪已禁用", "LONGFB": "选择计划", "LQ46NL": "坐标", + "LUJTwh": "图标已上传", "LUrJwE": "渠道", "LaV9M1": "选择时区", "LeFOAk": "此页面未激活过滤器", @@ -651,6 +669,8 @@ "MPAiyo": "您有一个正在进行的导入任务。请等待其完成后再开始新的导入。", "MPQ0kS": "选择一个组织", "MQQbnY": "回放", + "MUaUWe": "正在加载 API 密钥...", + "MVEvjb": "移除图标", "MYsZNO": "删除变体", "MZNJkb": "按名称过滤...", "MZjNWA": "更新电子邮件报告设置失败", @@ -717,6 +737,7 @@ "OtSSs+": "{count} 个网站", "Otefae": "维度", "OvzONl": "关闭", + "OwQmnm": "应用版本", "P3H0J/": "显示更多(还有 {count} 个)", "P3YB9A": "上周", "P6RGqE": "主机名", @@ -730,8 +751,10 @@ "PWxvnC": "请联系站点管理员连接 Google Search Console。", "PZOOdj": "请输入有效的 JSON 对象", "PbRfhj": "匹配按钮的可见文本。留空则计入任意按钮点击。", + "Pbtcej": "屏幕浏览量", "Pc+tM3": "生成", "Pe0ogR": "主题", + "PfUTPz": "VPN", "Pgj6mf": "本月事件", "Pgv4+O": "还没有漏斗", "PkGCG1": "生成私有链接以试用仪表板嵌入。", @@ -763,6 +786,7 @@ "QgMK2h": "UTM 来源", "QiKKFz": "更改密码", "QirE3M": "开始日期", + "QitbBm": "屏幕浏览", "QkTyho": "Rybbit 侧边栏", "Qp/Bnx": "没有用户符合当前筛选条件", "Qp1beM": "路径", @@ -794,6 +818,7 @@ "Rqzsq/": "添加过滤器", "RsY5rI": "已转化的会话", "RumeRz": "地区", + "RvxWt3": "入口屏幕:", "RwzAp9": "事件类型", "RzSw2z": "按钮文本(可选)", "S1utKH": "经度", @@ -801,6 +826,7 @@ "S4zxrU": "统计应用触发指定命名事件的会话数。", "S5IFjk": "创建团队以对站点进行分组并管理成员访问权限。", "S7U3Nx": "您正在接近每月的事件限制。考虑升级到付费计划以获得更高的额度。", + "S9jCu8": "图标已移除", "SB3+JE": "没有可用的漏斗数据", "SBUMrd": "筛选功能标志", "SBoJg8": "删除此 API 密钥?", @@ -812,6 +838,7 @@ "SWvHLr": "本周", "SX126d": "导出数据", "Sb7oIl": "按操作系统划分的性能", + "ShHw0c": "应用版本:", "Sjo1P4": "自定义", "SnIGkx": "此密钥仅显示一次。请立即复制并妥善保存。", "SoySgJ": "来源网站:", @@ -907,6 +934,7 @@ "VqI0Nz": "位置和设备", "VtLIgh": "组织密钥是组织自身的凭证:可以访问该组织的所有站点,并且在成员离开后仍可继续使用。请将其用于生产环境集成。", "Vwukkb": "添加 JSON 载荷", + "VxUjLr": "添加应用", "VyvTDF": "屏幕:", "VzzYJk": "创建", "W/V6+Y": "收起", @@ -927,6 +955,7 @@ "WnQ/AZ": "联系我们", "WuGkfJ": "返回", "WuHoZ9": "转化次数", + "X0cqyR": "操作系统版本", "X0ha1a": "保存更改", "X0ppsC": "个网站", "X2PZ7D": "创建目标", @@ -1062,6 +1091,7 @@ "cfiGPy": "新参数", "ciDaNU": "脚本加载时自动跟踪第一次页面浏览", "cj34Wi": "未返回组织 ID", + "ckAea8": "ASN 域名", "ckBJVF": "此操作无法撤销。这将永久删除网站\"{siteName}\"及其所有分析数据。", "cmDzrA": "获取事件时出现问题。请稍后重试。", "cmGAVy": "每页数量:", @@ -1091,6 +1121,7 @@ "dv+Q6J": "在应用中使用生成的标志键,并按变体分支您的 UI 或行为。", "dv4SpB": "尚未导入任何数据", "e/U0Xt": "转化率", + "e/WYt2": "设备型号", "eA5EH7": "搜索组织...", "eDqstJ": "24小时事件", "eGIKNJ": "热门页面", @@ -1099,6 +1130,7 @@ "eKEL/g": "待处理", "eOj4Bs": "事件名称", "eQnbde": "断开 Google Search Console 吗?", + "eXWZOl": "应用版本", "eYX7hS": "更新用于帐户通知的电子邮件地址", "ebEqgQ": "您有有效的订阅。请先取消订阅,再删除组织。", "ekXood": "无限制", @@ -1111,6 +1143,7 @@ "f5efCD": "选择结果", "f7uwif": "将站点组织成团队以控制哪些成员可以访问它们。", "fB09El": "以 /blog/ 开头的路径", + "fCvg+B": "匹配正则", "fDjNLX": "您即将导入一个大文件 ({size})。这可能需要几分钟来处理。您确定要继续吗?", "fGblXC": "或参阅适用于您平台的安装指南", "fItAD9": "群组", @@ -1132,6 +1165,7 @@ "fobMp1": "良好的 TTFB 分数应为 800 毫秒或更短。", "fr4eNi": "$1 的开发测试订阅", "frN4vy": "最后出现", + "fsUN9Y": "最少屏幕浏览量", "fx99AL": "弱", "fz0z4c": "计划", "g12Dke": "增长", @@ -1224,6 +1258,7 @@ "isUnnK": "Web Vitals 收集已启用", "itC9lG": "基本信息", "iv7EVe": "所有网站", + "iwp2S0": "屏幕", "izvHMl": "识别", "j3ehbn": "请使用更强的密码", "j5kA5A": "获取错误时出现问题。请稍后重试。", @@ -1257,6 +1292,7 @@ "jsy7pk": "再试一次", "jt9BUv": "暂无转化", "jvo0vs": "保存", + "jxsBGE": "在侧边栏、站点选择器和站点卡片中显示为网站图标。支持任意图片格式 — 自动调整为 128×128 PNG。最大 50 KB。", "k+kHlN": "团队名称", "k/CG+N": "变体键是必填项", "k1sXXZ": "生成查询失败", @@ -1299,6 +1335,7 @@ "lILp7K": "机器人拦截已禁用", "lKv8ex": "默认", "lRnHfd": "事件验证失败", + "lTavFx": "出口屏幕", "lVP3yI": "无需值", "lXZjmr": "邀请此组织的新成员。", "lYB8jy": "{count} 个用户", @@ -1346,6 +1383,7 @@ "nGQuNF": "每次会话页数", "nIXcfs": "无法复制到剪贴板。请选择密钥并手动复制。", "nIe+Wv": "创建新用户", + "nJNzqM": "图标上传失败", "nJuUfX": "按名称、标识符、域名或成员邮箱搜索...", "nL6Jl0": "留存", "nVYMBG": "创建用户", @@ -1367,6 +1405,7 @@ "o5Jj28": "无法删除帐户", "o5XOA3": "占您组织事件限额的 {percent}%", "o7g8s9": "例如:^/blog/.*", + "o9wmBB": "可选。将调整为 128x128 PNG。", "oA0ogD": "两者", "oAFgOq": "域名", "oCBPQ4": "移除筛选器", @@ -1422,6 +1461,7 @@ "q0NdNV": "显示热门国家/地区", "q2ZLFN": "功能标志已删除", "q4S4cE": "控制台日志", + "qAktpY": "每次会话屏幕数", "qB5AJG": "上传一个 CSV 文件以开始", "qDwvZ4": "未知错误", "qFQkeR": "设置一个新组织以添加网站和用户", @@ -1486,6 +1526,7 @@ "scPXsn": "正则表达式", "sd2ieZ": "周", "siqsij": "只有组织的所有者才能管理订阅。", + "smsDJm": "图标移除失败", "snh6R6": "标签(可选)", "soeWeh": "组织设置", "sy+pv5": "电子邮件", @@ -1499,6 +1540,7 @@ "tGAYL2": "选择一个选项", "tGWccD": "添加大写字母、小写字母、数字或符号以增强密码", "tGuKrF": "新实验", + "tH05Pi": "设备型号:", "tH8S0d": "添加 IP", "tHKpoN": "在选定的时间段内,没有会话在到达此步骤之前流失。", "tHSb8p": "桌面设备", @@ -1655,6 +1697,7 @@ "yi/uLq": "个事件", "yl0JSO": "更改这些备注不会影响分配或追踪。", "ym0FiC": "升级您的计划以继续收集分析数据。", + "ynio5d": "包名格式无效", "ytceSa": "未关联目标", "yunf47": "会话时长", "yxDujp": "添加特征", @@ -1672,10 +1715,12 @@ "zQvVDJ": "全部", "zSOvI0": "过滤器", "zUJjPA": "无团队", + "zUxWZC": "数据中心", "zWgbGg": "今天", "zWkvNO": "时间线", "zY8xSk": "输入您想要追踪的网站域名", "zZE+e+": "实验使用多变量功能标志为每位访客分配一个稳定的变体。", + "zff4lu": "", "znwd1N": "删除漏斗", "zoRFJV": "用于追踪的 Bundle 或包标识符", "zuA3Bg": "YouTube", diff --git a/client/src/api/admin/endpoints/index.ts b/client/src/api/admin/endpoints/index.ts index 5e52672ce..b161b5263 100644 --- a/client/src/api/admin/endpoints/index.ts +++ b/client/src/api/admin/endpoints/index.ts @@ -2,6 +2,7 @@ export { addSite, deleteSite, + deleteSiteIcon, moveSite, updateSiteConfig, fetchSite, @@ -9,6 +10,7 @@ export { fetchSiteHasData, fetchSiteIsPublic, fetchSiteUsage, + uploadSiteIcon, } from "./sites"; export type { SiteResponse, GetSitesFromOrgResponse, SiteUsageResponse } from "./sites"; diff --git a/client/src/api/admin/endpoints/sites.ts b/client/src/api/admin/endpoints/sites.ts index 5b31d7b0d..c934eee9c 100644 --- a/client/src/api/admin/endpoints/sites.ts +++ b/client/src/api/admin/endpoints/sites.ts @@ -1,10 +1,12 @@ import { authedFetch } from "../../utils"; +export type SiteType = "web" | "mobile"; + export type SiteResponse = { id: string | null; siteId: number; name: string; - type: "web" | "mobile" | null; + type: SiteType | null; domain: string; createdAt: string; updatedAt: string; @@ -47,7 +49,7 @@ export type GetSitesFromOrgResponse = { id: string | null; siteId: number; name: string; - type: "web" | "mobile" | null; + type: SiteType | null; domain: string; createdAt: string; updatedAt: string; @@ -79,7 +81,7 @@ export function addSite( name: string, organizationId: string, settings?: { - type?: "web" | "mobile"; + type?: SiteType; isPublic?: boolean; saltUserIds?: boolean; blockBots?: boolean; @@ -141,7 +143,7 @@ export function updateSiteConfig( siteId: number, config: { name?: string; - type?: "web" | "mobile" | null; + type?: SiteType | null; domain?: string; public?: boolean; embedEnabled?: boolean; @@ -206,3 +208,28 @@ export function fetchSiteHasData(siteId: string) { export function fetchSiteIsPublic(siteId: string | number) { return authedFetch<{ isPublic: boolean }>(`/sites/${siteId}/is-public`); } + +export interface VerifyScriptResponse { + scriptTagFound: boolean; + scriptExecuted: boolean; + siteIdMatch: boolean; + issues: string[]; +} + +export function verifyScript(siteId: number | string) { + return authedFetch(`/sites/${siteId}/verify-script`); +} + +export function uploadSiteIcon(siteId: number, icon: string) { + return authedFetch<{ success: boolean }>(`/sites/${siteId}/icon`, undefined, { + method: "PUT", + data: { icon }, + headers: { "Content-Type": "application/json" }, + }); +} + +export function deleteSiteIcon(siteId: number) { + return authedFetch<{ success: boolean }>(`/sites/${siteId}/icon`, undefined, { + method: "DELETE", + }); +} diff --git a/client/src/api/analytics/endpoints/events.ts b/client/src/api/analytics/endpoints/events.ts index 87c3ff045..d58049154 100644 --- a/client/src/api/analytics/endpoints/events.ts +++ b/client/src/api/analytics/endpoints/events.ts @@ -35,6 +35,8 @@ export type Event = { device_type: string; type: string; traits?: Record | null; + device_model: string; + app_version: string; }; // Response types for cursor-based API diff --git a/client/src/api/analytics/endpoints/sessions.ts b/client/src/api/analytics/endpoints/sessions.ts index fb8a5aaea..ba6fe1f1e 100644 --- a/client/src/api/analytics/endpoints/sessions.ts +++ b/client/src/api/analytics/endpoints/sessions.ts @@ -18,6 +18,8 @@ export type GetSessionsResponse = { operating_system_version: string; screen_width: number; screen_height: number; + device_model: string; + app_version: string; referrer: string; channel: string; hostname: string; @@ -56,6 +58,8 @@ export interface SessionDetails { city: string; language: string; device_type: string; + device_model: string; + app_version: string; browser: string; browser_version: string; operating_system: string; diff --git a/client/src/api/analytics/endpoints/users.ts b/client/src/api/analytics/endpoints/users.ts index df461438a..9f87b272d 100644 --- a/client/src/api/analytics/endpoints/users.ts +++ b/client/src/api/analytics/endpoints/users.ts @@ -15,6 +15,8 @@ export type UsersResponse = { browser: string; operating_system: string; device_type: string; + device_model: string; + app_version: string; referrer: string; channel: string; pageviews: number; @@ -74,6 +76,8 @@ export type UserInfo = { city: string; language: string; device_type: string; + device_model: string; + app_version: string; browser: string; browser_version: string; operating_system: string; diff --git a/client/src/app/(home)/SiteCard.tsx b/client/src/app/(home)/SiteCard.tsx index a0bdec57a..eeb5d2dde 100644 --- a/client/src/app/(home)/SiteCard.tsx +++ b/client/src/app/(home)/SiteCard.tsx @@ -1,4 +1,4 @@ -import { Tag, Settings } from "lucide-react"; +import { Tag, Settings, Smartphone } from "lucide-react"; import { useExtracted } from "next-intl"; import Link from "next/link"; import { useRef } from "react"; @@ -26,6 +26,7 @@ interface SiteCardProps { onTagsUpdated?: () => void; selectedTags?: string[]; onTagClick?: (tag: string) => void; + siteType?: "web" | "mobile" | null; } export function SiteCard({ @@ -37,6 +38,7 @@ export function SiteCard({ onTagsUpdated, selectedTags = [], onTagClick, + siteType, }: SiteCardProps) { const t = useExtracted(); const { ref, isInView } = useInView({ @@ -98,7 +100,7 @@ export function SiteCard({ {showSkeleton ? ( <>
- + {name}
@@ -118,8 +120,14 @@ export function SiteCard({ ) : ( <>
- + {name} + {siteType && siteType !== "web" && ( + + + {t("App")} + + )}
e.preventDefault()}> ); })} diff --git a/client/src/app/[site]/components/Header/NoData.tsx b/client/src/app/[site]/components/Header/NoData.tsx index 243243141..ddced299e 100644 --- a/client/src/app/[site]/components/Header/NoData.tsx +++ b/client/src/app/[site]/components/Header/NoData.tsx @@ -6,6 +6,7 @@ import { SiContentful, SiDocusaurus, SiDrupal, + SiFlutter, SiFramer, SiGatsby, SiGhost, @@ -129,6 +130,61 @@ export function NoData() { return null; } + const isApp = siteMetadata?.type === "mobile"; + + if (isApp) { + const appSiteId = siteMetadata?.id ?? siteMetadata?.siteId; + const dartSnippet = `await Rybbit.init(\n host: '${globalThis.location.origin}',\n siteId: '${appSiteId}',\n);`; + return ( +
+
+
+
+ +

+ {t("Waiting for analytics from your app...")} +

+
+

+ {t("Add the Rybbit SDK to your app:")} +

+
+ +
+ +
+ + {t("Flutter SDK documentation")} + +
+
+ +
+

+ {t("Platform Guides")} +

+ +
+
+
+ ); + } + const visibleGuides = showAllPlatforms ? PLATFORM_GUIDES : PLATFORM_GUIDES.slice(0, VISIBLE_PLATFORM_COUNT); const hiddenCount = PLATFORM_GUIDES.length - VISIBLE_PLATFORM_COUNT; diff --git a/client/src/app/[site]/components/Sidebar/MobileSidebar.tsx b/client/src/app/[site]/components/Sidebar/MobileSidebar.tsx index 026e141e9..0b594a153 100644 --- a/client/src/app/[site]/components/Sidebar/MobileSidebar.tsx +++ b/client/src/app/[site]/components/Sidebar/MobileSidebar.tsx @@ -40,7 +40,14 @@ function MobileSidebarContent() { - {site && } + {site && ( + + )}
); } diff --git a/client/src/app/[site]/components/Sidebar/Sidebar.tsx b/client/src/app/[site]/components/Sidebar/Sidebar.tsx index 66e58ce39..d9bc933ce 100644 --- a/client/src/app/[site]/components/Sidebar/Sidebar.tsx +++ b/client/src/app/[site]/components/Sidebar/Sidebar.tsx @@ -92,7 +92,7 @@ function SidebarContent() { /> {IS_CLOUD && ( } diff --git a/client/src/app/[site]/components/Sidebar/SiteSelector.tsx b/client/src/app/[site]/components/Sidebar/SiteSelector.tsx index 528fd4380..14d5ddf7c 100644 --- a/client/src/app/[site]/components/Sidebar/SiteSelector.tsx +++ b/client/src/app/[site]/components/Sidebar/SiteSelector.tsx @@ -1,4 +1,4 @@ -import { Check, ChevronDown, Plus } from "lucide-react"; +import { Check, ChevronDown, Plus, Smartphone } from "lucide-react"; import { useExtracted } from "next-intl"; import { usePathname, useRouter } from "next/navigation"; import { useState, Suspense } from "react"; @@ -22,6 +22,7 @@ type SiteOption = { siteId: number; name: string; domain: string; + type?: "web" | "mobile" | null; sessions?: number; }; @@ -37,10 +38,20 @@ function SiteRow({ site, isSelected }: { site: SiteOption; isSelected: boolean } return ( <> - +
- {site.name} + + {site.name} + {site.type && site.type !== "web" && ( + + )} + {!showDomain && sessionsLabel && ( {sessionsLabel} @@ -115,6 +126,7 @@ function SiteSelectorContent({ onSiteSelect }: { onSiteSelect: () => void }) { siteId: site.siteId, name: site.name, domain: site.domain, + type: site.type, sessions: site.sessionsLast24Hours, })); @@ -199,7 +211,12 @@ function SiteSelectorWrapper() { {site ? (
+ {!isApp && ( +
+ {t("Hostname")} + {event.hostname || "-"} +
+ )}
- {t("Hostname")} - {event.hostname || "-"} -
-
- {t("Path")} + {isApp ? t("Screen") : t("Path")} {buildEventPath(event) || "-"}
-
- {t("Referrer")} - {event.referrer || "-"} -
+ {!isApp && ( +
+ {t("Referrer")} + {event.referrer || "-"} +
+ )}
-
- {t("Browser")} - - - {event.browser || t("Unknown")}{event.browser_version ? ` ${event.browser_version}` : ""} - -
+ {isApp ? ( + <> +
+ {t("Device Model")} + {event.device_model || "-"} +
+
+ {t("App Version")} + {event.app_version ? `v${event.app_version}` : "-"} +
+ + ) : ( + <> +
+ {t("Browser")} + + + {event.browser || t("Unknown")}{event.browser_version ? ` ${event.browser_version}` : ""} + +
+
+ {t("Device")} + + + {event.device_type || t("Unknown")} + +
+ + )}
{t("Operating System")} @@ -130,13 +159,6 @@ export function EventDetailsSheet({ open, onOpenChange, event, site }: EventDeta {event.operating_system || t("Unknown")}{event.operating_system_version ? ` ${event.operating_system_version}` : ""}
-
- {t("Device")} - - - {event.device_type || t("Unknown")} - -
{t("Screen")} {event.screen_width && event.screen_height ? `${event.screen_width} × ${event.screen_height}` : "-"} diff --git a/client/src/app/[site]/events/components/EventLog/EventRow.tsx b/client/src/app/[site]/events/components/EventLog/EventRow.tsx index 877947f7c..2ee0a4d51 100644 --- a/client/src/app/[site]/events/components/EventLog/EventRow.tsx +++ b/client/src/app/[site]/events/components/EventLog/EventRow.tsx @@ -16,6 +16,7 @@ import { CountryFlag } from "../../../components/shared/icons/CountryFlag"; import { OperatingSystem } from "../../../components/shared/icons/OperatingSystem"; import { buildEventPath, getEventTypeLabel, getMainData, parseEventProperties } from "./eventLogUtils"; import { DeviceIcon } from "../../../components/shared/icons/Device"; +import { useGetSite } from "../../../../../api/admin/hooks/useSites"; interface EventRowProps { event: Event; @@ -25,6 +26,8 @@ interface EventRowProps { export function EventRow({ event, site, onClick }: EventRowProps) { const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const getEventDisplayName = useEventDisplayName(); const { locale, hour12, formatRelative } = useDateTimeFormat(); const eventProperties = parseEventProperties(event); @@ -55,7 +58,7 @@ export function EventRow({ event, site, onClick }: EventRowProps) {
- {getEventTypeLabel(event.type)} + {getEventTypeLabel(event.type, isApp)}
@@ -95,16 +98,18 @@ export function EventRow({ event, site, onClick }: EventRowProps) { )} - - -
- -
-
- -

{event.browser || t("Unknown browser")}

-
-
+ {!isApp && ( + + +
+ +
+
+ +

{event.browser || t("Unknown browser")}

+
+
+ )}
@@ -115,29 +120,37 @@ export function EventRow({ event, site, onClick }: EventRowProps) {

{event.operating_system || t("Unknown OS")}

- - -
- -
-
- -

{event.device_type || t("Unknown device")}

-
-
+ {!isApp && ( + + +
+ +
+
+ +

{event.device_type || t("Unknown device")}

+
+
+ )}
- e.stopPropagation()} - className="hover:underline" - title={pagePath} - > - {truncateString(pagePath, 60)} - + {isApp ? ( + + {truncateString(pagePath, 60)} + + ) : ( + e.stopPropagation()} + className="hover:underline" + title={pagePath} + > + {truncateString(pagePath, 60)} + + )}
diff --git a/client/src/app/[site]/events/components/EventLog/eventLogUtils.tsx b/client/src/app/[site]/events/components/EventLog/eventLogUtils.tsx index 8e2269c05..12e9a9dcb 100644 --- a/client/src/app/[site]/events/components/EventLog/eventLogUtils.tsx +++ b/client/src/app/[site]/events/components/EventLog/eventLogUtils.tsx @@ -21,8 +21,9 @@ export function parseEventProperties(event: Event): Record { // EVENT_TYPE_CONFIG labels are display strings ("Pageview", "Input Change"), not // message keys, so they must not be passed to t() — doing so throws MISSING_MESSAGE // and breaks the events page. Return them as-is. -export function getEventTypeLabel(type: string) { - return EVENT_TYPE_CONFIG.find(item => item.value === type)?.label ?? "Event"; +export function getEventTypeLabel(type: string, isMobileSite?: boolean) { + const label = EVENT_TYPE_CONFIG.find(item => item.value === type)?.label ?? "Event"; + return isMobileSite && label === "Pageview" ? "Screenview" : label; } export function buildEventPath(event: Event) { diff --git a/client/src/app/[site]/events/components/EventTypesChart.tsx b/client/src/app/[site]/events/components/EventTypesChart.tsx index 7160fcb1f..af2387383 100644 --- a/client/src/app/[site]/events/components/EventTypesChart.tsx +++ b/client/src/app/[site]/events/components/EventTypesChart.tsx @@ -13,6 +13,7 @@ import { formatChartDateTime, hour12, userLocale } from "@/lib/dateTimeUtils"; import { useNivoTheme } from "@/lib/nivo"; import { getTimezone, useStore } from "@/lib/store"; import { formatter } from "@/lib/utils"; +import { useGetSite } from "../../../../api/admin/hooks/useSites"; import { CardLoader } from "../../../../components/ui/card"; const EVENT_TYPE_CONFIG = [ @@ -27,6 +28,21 @@ const EVENT_TYPE_CONFIG = [ { key: "input_change_count", label: "Input Changes", color: "#f472b6" }, ] as const; +// Translated labels keyed by the raw label +function getTranslatedEventTypeLabels(t: (key: string) => string, isApp: boolean): Record { + return { + Pageviews: isApp ? t("Screenviews") : t("Pageviews"), + "Custom Events": t("Custom Events"), + Performance: t("Performance"), + Outbound: t("Outbound"), + Errors: t("Errors"), + "Button Clicks": t("Button Clicks"), + Copy: t("Copy"), + "Form Submits": t("Form Submits"), + "Input Changes": t("Input Changes"), + }; +} + type EventTypeKey = (typeof EVENT_TYPE_CONFIG)[number]["key"]; type DataPoint = { @@ -43,6 +59,8 @@ type Series = { export function EventTypesChart() { const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { bucket } = useStore(); const { data, isLoading } = useGetSiteEventCount(); const { width } = useWindowSize(); @@ -50,18 +68,6 @@ export function EventTypesChart() { const timezone = getTimezone(); const [hiddenTypes, setHiddenTypes] = useState>(new Set()); - const translatedLabels: Record = { - Pageviews: t("Pageviews"), - "Custom Events": t("Custom Events"), - Performance: t("Performance"), - Outbound: t("Outbound"), - Errors: t("Errors"), - "Button Clicks": t("Button Clicks"), - Copy: t("Copy"), - "Form Submits": t("Form Submits"), - "Input Changes": t("Input Changes"), - }; - const toggleTypeVisibility = (typeLabel: string) => { setHiddenTypes((prev) => { const next = new Set(prev); @@ -74,6 +80,8 @@ export function EventTypesChart() { }); }; + const translatedLabels = getTranslatedEventTypeLabels(t, isApp); + const { series, legendItems, maxValue, totalPoints } = useMemo(() => { if (!data || data.length === 0) { return { series: [] as Series[], legendItems: [], maxValue: 1, totalPoints: 0 }; diff --git a/client/src/app/[site]/events/page.tsx b/client/src/app/[site]/events/page.tsx index 069c42e04..3c6a0a191 100644 --- a/client/src/app/[site]/events/page.tsx +++ b/client/src/app/[site]/events/page.tsx @@ -1,8 +1,9 @@ "use client"; import { useExtracted } from "next-intl"; -import { EVENT_FILTERS } from "@/lib/filterGroups"; +import { getEventFilters } from "@/lib/filterGroups"; import { useGetEventNames } from "../../../api/analytics/hooks/events/useGetEventNames"; +import { useGetSite } from "../../../api/admin/hooks/useSites"; import { DisabledOverlay } from "../../../components/DisabledOverlay"; import { useSetPageTitle } from "../../../hooks/useSetPageTitle"; import { SubHeader } from "../components/SubHeader/SubHeader"; @@ -13,13 +14,15 @@ import { EventsChart } from "./components/EventsChart"; export default function EventsPage() { const t = useExtracted(); useSetPageTitle("Events"); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { data: eventNamesData, isLoading: isLoadingEventNames } = useGetEventNames(); return (
- + {/* diff --git a/client/src/app/[site]/funnels/page.tsx b/client/src/app/[site]/funnels/page.tsx index a1f6fdfd5..d7150bb0a 100644 --- a/client/src/app/[site]/funnels/page.tsx +++ b/client/src/app/[site]/funnels/page.tsx @@ -2,7 +2,8 @@ import { useExtracted } from "next-intl"; import { Input } from "@/components/ui/input"; -import { GOALS_PAGE_FILTERS } from "@/lib/filterGroups"; +import { getFunnelPageFilters } from "@/lib/filterGroups"; +import { useGetSite } from "../../../api/admin/hooks/useSites"; import { useStore } from "@/lib/store"; import { ArrowRight, Funnel } from "lucide-react"; import { Card } from "@/components/ui/card"; @@ -62,6 +63,8 @@ const FunnelRowSkeleton = () => ( export default function FunnelsPage() { const t = useExtracted(); useSetPageTitle("Funnels"); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { site } = useStore(); const { data: funnels, isLoading, error } = useGetFunnels(site); @@ -87,7 +90,7 @@ export default function FunnelsPage() { return (
- +
( export default function GoalsPage() { const t = useExtracted(); useSetPageTitle("Goals"); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { site } = useStore(); const [searchQuery, setSearchQuery] = useState(""); @@ -169,7 +172,7 @@ export default function GoalsPage() { return (
- +
>({}); const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { time } = useStore(); // Fetch path suggestions @@ -54,7 +55,7 @@ export default function JourneysPage() { return (
- +
diff --git a/client/src/app/[site]/main/components/MainSection/MainSection.tsx b/client/src/app/[site]/main/components/MainSection/MainSection.tsx index 3d80809e1..820cf63f0 100644 --- a/client/src/app/[site]/main/components/MainSection/MainSection.tsx +++ b/client/src/app/[site]/main/components/MainSection/MainSection.tsx @@ -6,6 +6,7 @@ import { useExtracted } from "next-intl"; import Link from "next/link"; import { useGetOverview } from "../../../../../api/analytics/hooks/useGetOverview"; import { useGetOverviewBucketed } from "../../../../../api/analytics/hooks/useGetOverviewBucketed"; +import { useGetSite } from "../../../../../api/admin/hooks/useSites"; import { BucketSelection } from "../../../../../components/BucketSelection"; import { RybbitTextLogo } from "../../../../../components/RybbitLogo"; import { useWhiteLabel } from "../../../../../hooks/useIsWhiteLabel"; @@ -25,14 +26,16 @@ export function MainSection() { const { isWhiteLabel } = useWhiteLabel(); const session = authClient.useSession(); const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { selectedStat, time, site, bucket } = useStore(); const getSelectedStatLabel = () => { switch (selectedStat) { - case "pageviews": return t("Pageviews"); + case "pageviews": return isApp ? t("Screenviews") : t("Pageviews"); case "sessions": return t("Sessions"); - case "pages_per_session": return t("Pages per Session"); + case "pages_per_session": return isApp ? t("Screens per Session") : t("Pages per Session"); case "bounce_rate": return t("Bounce Rate"); case "session_duration": return t("Session Duration"); case "users": return t("Users"); diff --git a/client/src/app/[site]/main/components/MainSection/Overview.tsx b/client/src/app/[site]/main/components/MainSection/Overview.tsx index 013345700..3494ef17f 100644 --- a/client/src/app/[site]/main/components/MainSection/Overview.tsx +++ b/client/src/app/[site]/main/components/MainSection/Overview.tsx @@ -9,6 +9,7 @@ import { useExtracted } from "next-intl"; import { useState } from "react"; import { useGetOverview } from "../../../../../api/analytics/hooks/useGetOverview"; import { useGetOverviewBucketed } from "../../../../../api/analytics/hooks/useGetOverviewBucketed"; +import { useGetSite } from "../../../../../api/admin/hooks/useSites"; import { StatType, useStore } from "../../../../../lib/store"; import { SparklinesChart } from "./SparklinesChart"; @@ -156,6 +157,8 @@ const Stat = ({ export function Overview() { const { site } = useStore(); const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; // Current period - automatically handles both regular time-based and past-minutes queries const { @@ -198,14 +201,14 @@ export function Overview() { [] = [ { value: "browsers", - label: t("Browsers"), - section: { - filterParameter: "browser", - title: t("Browsers"), - getValue: e => e.value, - getKey: e => e.value, - getLabel: e => ( -
- - {e.value || t("Other")} -
- ), - getSubrowLabel: e => { - const justBrowser = e.value.split(" ").slice(0, -1).join(" "); - return ( -
- - {e.value || t("Other")} -
- ); - }, - hasSubrow: true, - }, + label: isApp ? t("App Versions") : t("Browsers"), + section: isApp + ? { + filterParameter: "app_version", + title: t("App Versions"), + getValue: e => e.value, + getKey: e => e.value, + getLabel: e => ( +
+ + {e.value || t("Other")} +
+ ), + } + : { + filterParameter: "browser", + title: t("Browsers"), + getValue: e => e.value, + getKey: e => e.value, + getLabel: e => ( +
+ + {e.value || t("Other")} +
+ ), + getSubrowLabel: e => { + const justBrowser = e.value.split(" ").slice(0, -1).join(" "); + return ( +
+ + {e.value || t("Other")} +
+ ); + }, + hasSubrow: true, + }, }, { value: "devices", label: t("Devices"), - section: { - filterParameter: "device_type", - title: t("Devices"), - getValue: e => e.value, - getKey: e => e.value, - getLabel: e => ( -
- - {e.value || t("Other")} -
- ), - }, + section: isApp + ? { + filterParameter: "device_model", + title: t("Devices"), + getValue: e => e.value, + getKey: e => e.value, + getLabel: e => ( +
+ + {e.value || t("Other")} +
+ ), + } + : { + filterParameter: "device_type", + title: t("Devices"), + getValue: e => e.value, + getKey: e => e.value, + getLabel: e => ( +
+ + {e.value || t("Other")} +
+ ), + }, }, { value: "os", diff --git a/client/src/app/[site]/main/components/sections/Events.tsx b/client/src/app/[site]/main/components/sections/Events.tsx index bc46faf2a..13923e68e 100644 --- a/client/src/app/[site]/main/components/sections/Events.tsx +++ b/client/src/app/[site]/main/components/sections/Events.tsx @@ -11,6 +11,7 @@ import { OutboundLinksList } from "../../../events/components/OutboundLinksList" import { TabbedSectionCard, type TabbedSectionItem } from "../../../components/shared/TabbedSectionCard"; import { AutocaptureEventsDialogBody } from "./AutocaptureEventsDialog"; import { OutboundLinksDialogBody } from "./OutboundLinksDialog"; +import { useGetSite } from "../../../../../api/admin/hooks/useSites"; type Tab = "events" | "outbound" | "buttons" | "forms" | "copies"; @@ -119,6 +120,8 @@ function AutocaptureDialogContent({ export function Events() { const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const tabs: TabbedSectionItem[] = [ { @@ -126,13 +129,21 @@ export function Events() { label: t("Custom Events"), content: , }, - { + ]; + + if (!isApp) { + tabs.push({ value: "outbound", label: t("Outbound"), content: , dialogContent: , dialogTitle: t("Outbound Links"), - }, + }); + } + + // Autocapture tabs apply to both platforms — the native SDKs emit + // button_click/form_submit/copy events just like the web script does. + tabs.push( { value: "buttons", label: t("Buttons"), @@ -174,8 +185,8 @@ export function Events() { /> ), dialogTitle: t("Copies"), - }, - ]; + } + ); return ; } diff --git a/client/src/app/[site]/main/components/sections/Pages.tsx b/client/src/app/[site]/main/components/sections/Pages.tsx index 6498d38d3..7e3a4758c 100644 --- a/client/src/app/[site]/main/components/sections/Pages.tsx +++ b/client/src/app/[site]/main/components/sections/Pages.tsx @@ -13,21 +13,26 @@ type Tab = "pages" | "page_title" | "entry_pages" | "exit_pages" | "hostname"; export function Pages() { const { data: siteMetadata } = useGetSite(); const t = useExtracted(); + const isApp = siteMetadata?.type === "mobile"; + + const getPageLink = isApp + ? undefined + : (e: { value: string; hostname?: string }) => { + const host = e.hostname || siteMetadata?.domain; + return host ? `https://${host}${e.value}` : "#"; + }; const tabs: StandardSectionTab[] = [ { value: "pages", - label: t("Pages"), + label: isApp ? t("Screens") : t("Pages"), section: { filterParameter: "pathname", - title: t("Pages"), + title: isApp ? t("Screens") : t("Pages"), getValue: e => e.value, getKey: e => e.value, getLabel: e => truncateString(e.value, 50) || t("Other"), - getLink: e => { - const host = e.hostname || siteMetadata?.domain; - return host ? `https://${host}${e.value}` : "#"; - }, + getLink: getPageLink, }, }, { @@ -43,35 +48,32 @@ export function Pages() { }, { value: "entry_pages", - label: t("Entries"), + label: isApp ? t("Entry Screens") : t("Entries"), section: { filterParameter: "entry_page", - title: t("Entry Pages"), + title: isApp ? t("Entry Screens") : t("Entry Pages"), getValue: e => e.value, getKey: e => e.value, getLabel: e => e.value || t("Other"), - getLink: e => { - const host = e.hostname || siteMetadata?.domain; - return host ? `https://${host}${e.value}` : "#"; - }, + getLink: getPageLink, }, }, { value: "exit_pages", - label: t("Exits"), + label: isApp ? t("Exit Screens") : t("Exits"), section: { filterParameter: "exit_page", - title: t("Exit Pages"), + title: isApp ? t("Exit Screens") : t("Exit Pages"), getValue: e => e.value, getKey: e => e.value, getLabel: e => e.value || t("Other"), - getLink: e => { - const host = e.hostname || siteMetadata?.domain; - return host ? `https://${host}${e.value}` : "#"; - }, + getLink: getPageLink, }, }, - { + ]; + + if (!isApp) { + tabs.push({ value: "hostname", label: t("Hostnames"), section: { @@ -81,8 +83,8 @@ export function Pages() { getKey: e => e.value, getLabel: e => e.value, }, - }, - ]; + }); + } return ; } diff --git a/client/src/app/[site]/main/components/sections/Weekdays.tsx b/client/src/app/[site]/main/components/sections/Weekdays.tsx index 185c8c8e5..6390e48f4 100644 --- a/client/src/app/[site]/main/components/sections/Weekdays.tsx +++ b/client/src/app/[site]/main/components/sections/Weekdays.tsx @@ -8,6 +8,7 @@ import { Card, CardContent, CardLoader } from "../../../../../components/ui/card import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../../../components/ui/select"; import { Tooltip, TooltipContent, TooltipTrigger } from "../../../../../components/ui/tooltip"; import { getTimezone, StatType, useStore } from "../../../../../lib/store"; +import { useGetSite } from "../../../../../api/admin/hooks/useSites"; import { cn } from "../../../../../lib/utils"; import { formatLocalTime, hourLabels, longDayNames, shortDayNames } from "../../../../../lib/dateTimeUtils"; @@ -17,6 +18,8 @@ export function Weekdays() { const [metric, setMetric] = useState("users"); const timezone = getTimezone(); const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { data, isFetching, error } = useGetOverviewBucketed({ site, @@ -128,13 +131,13 @@ export function Weekdays() { case "users": return t("Unique Visitors"); case "pageviews": - return t("Pageviews"); + return isApp ? t("Screenviews") : t("Pageviews"); case "sessions": return t("Sessions"); case "bounce_rate": return t("Bounce Rate"); case "pages_per_session": - return t("Pages per Session"); + return isApp ? t("Screens per Session") : t("Pages per Session"); case "session_duration": return t("Session Duration"); default: @@ -159,10 +162,10 @@ export function Weekdays() { {t("Unique Visitors")} - {t("Pageviews")} + {isApp ? t("Screenviews") : t("Pageviews")} {t("Sessions")} {t("Bounce Rate")} - {t("Pages per Session")} + {isApp ? t("Screens per Session") : t("Pages per Session")} {t("Session Duration")} diff --git a/client/src/app/[site]/main/page.tsx b/client/src/app/[site]/main/page.tsx index 266d2766b..ca65f8db6 100644 --- a/client/src/app/[site]/main/page.tsx +++ b/client/src/app/[site]/main/page.tsx @@ -1,5 +1,6 @@ "use client"; import { ReactNode } from "react"; +import { useGetSite } from "../../../api/admin/hooks/useSites"; import { useGetLiveUserCount } from "../../../api/analytics/hooks/useGetLiveUserCount"; import { useInView } from "../../../hooks/useInView"; import { useSetPageTitle } from "../../../hooks/useSetPageTitle"; @@ -19,10 +20,18 @@ import { Referrers } from "./components/sections/Referrers"; import { SearchConsole } from "./components/sections/SearchConsole"; import { Weekdays } from "./components/sections/Weekdays"; -function LazySection({ children, height = "405px" }: { children: ReactNode; height?: string }) { +function LazySection({ + children, + height = "405px", + className, +}: { + children: ReactNode; + height?: string; + className?: string; +}) { const { ref, isInView } = useInView({ persistVisibility: true, rootMargin: "100px 0px" }); return ( -
+
{isInView ? children : null}
); @@ -40,6 +49,8 @@ export default function MainPage() { function MainPageContent() { const { data } = useGetLiveUserCount(5); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; useSetPageTitle(`${data?.count ?? "…"} user${data?.count === 1 ? "" : "s"} online`); @@ -71,9 +82,11 @@ function MainPageContent() {
- - - + {!isApp && ( + + + + )} @@ -86,10 +99,10 @@ function MainPageContent() { - + - {IS_CLOUD && ( + {IS_CLOUD && !isApp && ( diff --git a/client/src/app/[site]/pages/components/PagesTable.tsx b/client/src/app/[site]/pages/components/PagesTable.tsx index 08bc4b9b8..67f7527c8 100644 --- a/client/src/app/[site]/pages/components/PagesTable.tsx +++ b/client/src/app/[site]/pages/components/PagesTable.tsx @@ -129,7 +129,8 @@ export function PagesTable() { const item = info.row.original; const title = item.value; const pathname = item.pathname; - const pageUrl = siteMetadata?.domain ? `https://${siteMetadata.domain}${pathname}` : ""; + const isApp = (siteMetadata?.type ?? "web") !== "web"; + const pageUrl = isApp ? "" : siteMetadata?.domain ? `https://${siteMetadata.domain}${pathname}` : ""; return (
diff --git a/client/src/app/[site]/sessions/page.tsx b/client/src/app/[site]/sessions/page.tsx index 3ef590d57..4141115fb 100644 --- a/client/src/app/[site]/sessions/page.tsx +++ b/client/src/app/[site]/sessions/page.tsx @@ -12,14 +12,17 @@ import { Label } from "../../../components/ui/label"; import { Switch } from "../../../components/ui/switch"; import { Tooltip, TooltipContent, TooltipTrigger } from "../../../components/ui/tooltip"; import { useSetPageTitle } from "../../../hooks/useSetPageTitle"; -import { SESSION_PAGE_FILTERS } from "../../../lib/filterGroups"; +import { getSessionPageFilters } from "../../../lib/filterGroups"; import { SubHeader } from "../components/SubHeader/SubHeader"; +import { useGetSite } from "../../../api/admin/hooks/useSites"; const LIMIT = 100; export default function SessionsPage() { const t = useExtracted(); useSetPageTitle("Sessions"); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const [page, setPage] = useState(1); const [identifiedOnly, setIdentifiedOnly] = useState(false); const [minPageviews, setMinPageviews] = useState(undefined); @@ -91,7 +94,7 @@ export default function SessionsPage() { htmlFor="min-pageviews" className="text-sm text-neutral-600 dark:text-neutral-400 whitespace-nowrap" > - {t("Min pageviews")} + {isApp ? t("Min screenviews") : t("Min pageviews")}
- + string; }) { const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isMobileSite = siteMetadata?.type === "mobile"; if (isLoading) { return ( @@ -199,6 +202,24 @@ export function LocationDevices({ /> )} /> + ) : isMobileSite ? ( + <> + + + } + label={t("OS")} + value={ + data?.operating_system + ? `${data.operating_system}${data.operating_system_version ? ` v${data.operating_system_version}` : ""}` + : "—" + } + /> + + ) : ( <> - {/* Acquisition (first-touch attribution) */} + {/* Acquisition (first-touch attribution). Channels, referrers and landing + pages are web concepts — native app traffic has none of them. */} + {!isApp && ( {isLoading ? ( @@ -118,6 +123,7 @@ export function UserSidebar({ data, isLoading, sessionCount, isLoadingCalendar,
)} + )} {/* Location & Device Info */} @@ -158,7 +164,7 @@ export function UserSidebar({ data, isLoading, sessionCount, isLoadingCalendar, {/* Web Vitals (p75 across this user's performance events) */} - {vitals && vitalsToShow.length > 0 && ( + {!isApp && vitals && vitalsToShow.length > 0 && ( } - label={t("Pageviews")} + label={isMobileSite ? t("Screenviews") : t("Pageviews")} value={count(data?.pageviews)} title={countTitle(data?.pageviews)} isLoading={isLoading} diff --git a/client/src/app/[site]/users/components/UsersTable.tsx b/client/src/app/[site]/users/components/UsersTable.tsx index 81111dbe3..6f75a386a 100644 --- a/client/src/app/[site]/users/components/UsersTable.tsx +++ b/client/src/app/[site]/users/components/UsersTable.tsx @@ -38,6 +38,7 @@ import { Browser } from "../../components/shared/icons/Browser"; import { CountryFlag } from "../../components/shared/icons/CountryFlag"; import { OperatingSystem } from "../../components/shared/icons/OperatingSystem"; import { DeviceIcon } from "../../components/shared/icons/Device"; +import { useGetSite } from "../../../../api/admin/hooks/useSites"; const columnHelper = createColumnHelper(); const TIME_COLUMN_WIDTH = "10rem"; @@ -72,6 +73,8 @@ const SortHeader = ({ column, children }: any) => { export function UsersTable() { const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const { formatRelative, formatDateTime } = useDateTimeFormat(); const formatRelativeTime = (dateStr: string) => { @@ -175,52 +178,69 @@ export function UsersTable() { ); }, }), - columnHelper.accessor("referrer", { - header: t("Channel"), - cell: info => { - const channel = info.row.original.channel; - const referrer = info.getValue(); - const domain = extractDomain(referrer); + ...(isApp ? [ + columnHelper.accessor("device_model", { + header: t("Device Model"), + cell: info => { + const model = info.getValue(); + return ( +
handleFilterClick(e, "device_model", model)} + > + {model || t("Unknown")} +
+ ); + }, + }), + ] : [ + columnHelper.accessor("referrer", { + header: t("Channel"), + cell: info => { + const channel = info.row.original.channel; + const referrer = info.getValue(); + const domain = extractDomain(referrer); + + if (domain) { + const displayName = getDisplayName(domain); + return ( +
handleFilterClick(e, "channel", channel)} + > + + {displayName} +
+ ); + } - if (domain) { - const displayName = getDisplayName(domain); return (
handleFilterClick(e, "channel", channel)} > - - {displayName} + + {channel}
); - } - - return ( -
handleFilterClick(e, "channel", channel)} - > - - {channel} -
- ); - }, - }), - columnHelper.accessor("browser", { - header: t("Browser"), - cell: info => { - const browser = info.getValue(); - return ( -
handleFilterClick(e, "browser", browser)} - > - - {browser || t("Unknown")} -
- ); - }, - }), + }, + }), + columnHelper.accessor("browser", { + header: t("Browser"), + cell: info => { + const browser = info.getValue(); + return ( +
handleFilterClick(e, "browser", browser)} + > + + {browser || t("Unknown")} +
+ ); + }, + }), + ]), columnHelper.accessor("operating_system", { header: t("OS"), cell: info => { @@ -236,7 +256,13 @@ export function UsersTable() { ); }, }), - columnHelper.accessor("device_type", { + ...(isApp ? [ + columnHelper.accessor("app_version", { + header: t("App Version"), + cell: info =>
{info.getValue() || "—"}
, + }), + ] : []), + ...(!isApp ? [columnHelper.accessor("device_type", { header: t("Device"), cell: info => { const deviceType = info.getValue(); @@ -250,9 +276,9 @@ export function UsersTable() {
); }, - }), + })] : []), columnHelper.accessor("pageviews", { - header: ({ column }) => {t("Pageviews")}, + header: ({ column }) => {isApp ? t("Screenviews") : t("Pageviews")}, cell: info =>
{info.getValue().toLocaleString()}
, }), columnHelper.accessor("events", { diff --git a/client/src/app/[site]/users/page.tsx b/client/src/app/[site]/users/page.tsx index 04fb6ef1f..e6bce7684 100644 --- a/client/src/app/[site]/users/page.tsx +++ b/client/src/app/[site]/users/page.tsx @@ -4,7 +4,8 @@ import { useExtracted } from "next-intl"; import { DisabledOverlay } from "../../../components/DisabledOverlay"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../components/ui/basic-tabs"; import { useSetPageTitle } from "../../../hooks/useSetPageTitle"; -import { USER_PAGE_FILTERS } from "../../../lib/filterGroups"; +import { getUserPageFilters } from "../../../lib/filterGroups"; +import { useGetSite } from "../../../api/admin/hooks/useSites"; import { SubHeader } from "../components/SubHeader/SubHeader"; import { TraitsExplorer } from "./components/TraitsExplorer"; import { UsersTable } from "./components/UsersTable"; @@ -12,11 +13,13 @@ import { UsersTable } from "./components/UsersTable"; export default function UsersPage() { useSetPageTitle("Users"); const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; return (
- + {t("Users")} diff --git a/client/src/app/components/AddSite.tsx b/client/src/app/components/AddSite.tsx index bb85ae85b..3df2f9b4e 100644 --- a/client/src/app/components/AddSite.tsx +++ b/client/src/app/components/AddSite.tsx @@ -4,9 +4,10 @@ import { AlertCircle, AppWindow, ChevronRight, Globe2, Plus, Smartphone } from " import { useExtracted } from "next-intl"; import { useRouter } from "next/navigation"; import { ReactNode, useState } from "react"; -import { addSite } from "../../api/admin/endpoints"; +import { addSite, uploadSiteIcon } from "../../api/admin/endpoints"; import { useGetSitesFromOrg } from "../../api/admin/hooks/useSites"; import { SettingRow } from "../../components/SiteSettings/SettingsSection"; +import { resizeImageToIcon } from "../../lib/imageUtils"; import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert"; import { Badge } from "../../components/ui/badge"; import { @@ -84,6 +85,8 @@ export function AddSite({ trigger, disabled }: { trigger?: React.ReactNode; disa const [showAdvanced, setShowAdvanced] = useState(false); const [toggles, setToggles] = useState({ ...DEFAULT_TOGGLES }); const [error, setError] = useState(""); + const [iconPreview, setIconPreview] = useState(null); + const [iconBase64, setIconBase64] = useState(null); const isMobile = siteType === "mobile"; const setToggle = (key: ToggleKey, checked: boolean) => setToggles(prev => ({ ...prev, [key]: checked })); @@ -236,6 +239,14 @@ export function AddSite({ trigger, disabled }: { trigger?: React.ReactNode; disa trackFormInteractions: toggles.trackFormInteractions && !standardFeaturesDisabled, }); + if (siteType === "mobile" && iconBase64) { + try { + await uploadSiteIcon(site.siteId, iconBase64); + } catch (iconError) { + console.warn("Site created but icon upload failed", iconError); + } + } + resetStore(); setSite(site.siteId.toString()); router.push(`/${site.siteId}`); @@ -255,6 +266,20 @@ export function AddSite({ trigger, disabled }: { trigger?: React.ReactNode; disa setError(""); setShowAdvanced(false); setToggles({ ...DEFAULT_TOGGLES }); + setIconPreview(null); + setIconBase64(null); + }; + + const handleIconSelect = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + try { + const base64 = await resizeImageToIcon(file); + setIconBase64(base64); + setIconPreview(`data:image/png;base64,${base64}`); + } catch { + setError(t("Failed to process icon image")); + } }; const renderToggleGroup = (title: string, groupToggles: CreateToggle[]) => { @@ -344,8 +369,8 @@ export function AddSite({ trigger, disabled }: { trigger?: React.ReactNode; disa - - {t("Add Site")} + {siteType === "web" ? : } + {siteType === "web" ? t("Add Website") : t("Add App")} {t("Track analytics for a new website or React Native app in your organization")} @@ -355,7 +380,11 @@ export function AddSite({ trigger, disabled }: { trigger?: React.ReactNode; disa
setSiteType(value as SiteType)} + onValueChange={value => { + setSiteType(value as SiteType); + setDomain(""); + setError(""); + }} className="grid grid-cols-2 gap-3" >
+ {siteType === "mobile" && ( +
+ +
+ {iconPreview ? ( + App icon preview + ) : ( +
+ +
+ )} +
+ +

{t("Optional. Resized to 128x128 PNG.")}

+
+
+
+ )}
@@ -208,23 +243,27 @@ export function SessionCard({ session, onClick, userId, expandedByDefault, highl onClick={e => handleFilterClick(e, "country", session.country)} /> )} - handleFilterClick(e, "browser", session.browser)} - /> + {!isApp && ( + handleFilterClick(e, "browser", session.browser)} + /> + )} handleFilterClick(e, "operating_system", session.operating_system)} /> - handleFilterClick(e, "device_type", session.device_type)} - /> - {session.has_replay === 1 && ( + {!isApp && ( + handleFilterClick(e, "device_type", session.device_type)} + /> + )} + {!isApp && session.has_replay === 1 && ( {formatter(session.pageviews)} - {t("Pageviews")} + {isApp ? t("Screenviews") : t("Pageviews")} @@ -258,11 +297,39 @@ export function SessionCard({ session, onClick, userId, expandedByDefault, highl {t("Events")} - handleFilterClick(e, "channel", session.channel)} - /> + {isApp ? ( + session.device_model || session.app_version ? ( + <> + {session.device_model && ( + handleFilterClick(e, "device_model", session.device_model)} + > + + {session.device_model} + + )} + {session.app_version && ( + handleFilterClick(e, "app_version", session.app_version)} + > + v{session.app_version} + + )} + + ) : ( + + + + + ) + ) : ( + handleFilterClick(e, "channel", session.channel)} + /> + )}
{/* Pages section with tooltips for long paths */} diff --git a/client/src/components/Sessions/SessionDetails/SessionInfoTab.tsx b/client/src/components/Sessions/SessionDetails/SessionInfoTab.tsx index fd6618114..26ff1d116 100644 --- a/client/src/components/Sessions/SessionDetails/SessionInfoTab.tsx +++ b/client/src/components/Sessions/SessionDetails/SessionInfoTab.tsx @@ -10,6 +10,7 @@ import { getCountryName, getLanguageName } from "../../../lib/utils"; import { Avatar, generateName } from "../../Avatar"; import { IdentifiedBadge } from "../../IdentifiedBadge"; import { DeviceIcon } from "../../../app/[site]/components/shared/icons/Device"; +import { useGetSite } from "../../../api/admin/hooks/useSites"; interface SessionInfoTabProps { session: GetSessionsResponse[number]; @@ -20,6 +21,8 @@ interface SessionInfoTabProps { region?: string; city?: string; device_type?: string; + device_model?: string; + app_version?: string; browser?: string; browser_version?: string; operating_system?: string; @@ -39,6 +42,8 @@ export function SessionInfoTab({ }: SessionInfoTabProps) { const { getRegionName } = useGetRegionName(); const t = useExtracted(); + const { data: siteMetadata } = useGetSite(); + const isApp = siteMetadata?.type === "mobile"; const isIdentified = !!session.identified_user_id; return ( @@ -151,34 +156,61 @@ export function SessionInfoTab({ {t("Device Information")}
-
- - {t("Device:")} - -
- - {sessionDetails?.device_type || t("Unknown")} + {!isApp && ( +
+ + {t("Device:")} + +
+ + {sessionDetails?.device_type || t("Unknown")} +
-
+ )} -
- - {t("Browser:")} - -
- - - {sessionDetails?.browser || t("Unknown")} - {sessionDetails?.browser_version && ( - - v{sessionDetails.browser_version} + {isApp ? ( + <> + {sessionDetails?.device_model && ( +
+ + {t("Device Model:")} - )} + + {sessionDetails.device_model} + +
+ )} + {sessionDetails?.app_version && ( +
+ + {t("App Version:")} + + + {sessionDetails.app_version} + +
+ )} + + ) : ( +
+ + {t("Browser:")} +
+ + + {sessionDetails?.browser || t("Unknown")} + {sessionDetails?.browser_version && ( + + v{sessionDetails.browser_version} + + )} + +
-
+ )}
@@ -227,30 +259,34 @@ export function SessionInfoTab({ {/* Source Information */}

- {t("Source Information")} + {isApp ? t("Session") : t("Source Information")}

-
- - {t("Channel:")} - -
- {sessionDetails?.channel || t("None")} + {!isApp && ( +
+ + {t("Channel:")} + +
+ {sessionDetails?.channel || t("None")} +
-
+ )} -
- - {t("Referrer:")} - -
- {sessionDetails?.referrer || t("None")} + {!isApp && ( +
+ + {t("Referrer:")} + +
+ {sessionDetails?.referrer || t("None")} +
-
+ )}
- {t("Entry Page:")} + {isApp ? t("Entry Screen:") : t("Entry Page:")}
{sessionDetails?.entry_page || t("None")} diff --git a/client/src/components/SiteSettings/GeneralTab.tsx b/client/src/components/SiteSettings/GeneralTab.tsx index 74a9e9f3a..58cc3732e 100644 --- a/client/src/components/SiteSettings/GeneralTab.tsx +++ b/client/src/components/SiteSettings/GeneralTab.tsx @@ -1,7 +1,7 @@ "use client"; import { useQueryClient } from "@tanstack/react-query"; -import { AlertTriangle } from "lucide-react"; +import { AlertTriangle, Smartphone, Trash2, Upload } from "lucide-react"; import { useExtracted } from "next-intl"; import { useRouter } from "next/navigation"; import { useState, useCallback, ReactNode } from "react"; @@ -24,10 +24,12 @@ import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; -import { deleteSite, moveSite, updateSiteConfig, SiteResponse } from "@/api/admin/endpoints"; +import { deleteSite, deleteSiteIcon, moveSite, updateSiteConfig, uploadSiteIcon, SiteResponse } from "@/api/admin/endpoints"; import { useUserOrganizations } from "@/api/admin/hooks/useOrganizations"; import { useGetSitesFromOrg } from "@/api/admin/hooks/useSites"; -import { normalizeDomain } from "@/lib/utils"; +import { BACKEND_URL } from "@/lib/const"; +import { resizeImageToIcon } from "@/lib/imageUtils"; +import { isValidDomain, isValidPackageName, normalizeDomain } from "@/lib/utils"; import { SettingRow, SettingsSection, SettingsSections } from "./SettingsSection"; @@ -66,6 +68,10 @@ export function GeneralTab({ siteMetadata, disabled = false, onClose, onPublicCh const [isDeleting, setIsDeleting] = useState(false); const [targetOrgId, setTargetOrgId] = useState(""); const [isMoving, setIsMoving] = useState(false); + const [iconVersion, setIconVersion] = useState(0); + const [isUploadingIcon, setIsUploadingIcon] = useState(false); + const [isDeletingIcon, setIsDeletingIcon] = useState(false); + const isMutatingIcon = isUploadingIcon || isDeletingIcon; // Organizations the user can move the site into: those they administer, // excluding the site's current organization. @@ -140,6 +146,16 @@ export function GeneralTab({ siteMetadata, disabled = false, onClose, onPublicCh return; } + if (isMobileSite) { + if (!isValidPackageName(newDomain)) { + toast.error(t("Invalid package name format")); + return; + } + } else if (!isValidDomain(newDomain)) { + toast.error(t("Invalid domain format")); + return; + } + try { setIsChangingDomain(true); const normalizedDomain = isMobileSite ? newDomain.trim() : normalizeDomain(newDomain); @@ -212,15 +228,19 @@ export function GeneralTab({ siteMetadata, disabled = false, onClose, onPublicCh enabledMessage: t("User ID salting enabled"), disabledMessage: t("User ID salting disabled"), }, - { - id: "blockBots", - label: t("Block Bot Traffic"), - description: t("Traffic from known bots and crawlers will not be tracked"), - value: toggleStates.blockBots, - key: "blockBots", - enabledMessage: t("Bot blocking enabled"), - disabledMessage: t("Bot blocking disabled"), - }, + ...(!isMobileSite + ? [ + { + id: "blockBots", + label: t("Block Bot Traffic"), + description: t("Traffic from known bots and crawlers will not be tracked"), + value: toggleStates.blockBots, + key: "blockBots", + enabledMessage: t("Bot blocking enabled"), + disabledMessage: t("Bot blocking disabled"), + } as ToggleConfig, + ] + : []), { id: "firstPartyProxy", label: t("First-Party Proxy"), @@ -297,6 +317,92 @@ export function GeneralTab({ siteMetadata, disabled = false, onClose, onPublicCh
+ {isMobileSite && ( + +

+ {t( + "Displayed as a favicon in the sidebar, site selector, and site cards. Accepts any image format — automatically resized to 128×128 PNG. Max 50 KB." + )} +

+
+
+ +
+ {t("App { + (e.target as HTMLImageElement).style.display = "none"; + (e.target as HTMLImageElement).nextElementSibling?.classList.remove("hidden"); + }} + onLoad={e => { + (e.target as HTMLImageElement).style.display = ""; + (e.target as HTMLImageElement).nextElementSibling?.classList.add("hidden"); + }} + /> +
+ +
+
+
+
+ + +
+
+
+ )} + {privacyToggles.map(toggle => ( diff --git a/client/src/lib/filterGroups.ts b/client/src/lib/filterGroups.ts index 6661f3bb0..b2477cb95 100644 --- a/client/src/lib/filterGroups.ts +++ b/client/src/lib/filterGroups.ts @@ -1,96 +1,148 @@ import { FilterParameter } from "@rybbit/shared"; -const BASE_FILTERS: FilterParameter[] = [ +const WEB_ONLY_FILTERS: FilterParameter[] = [ "hostname", - "browser", - "browser_version", - "operating_system", - "operating_system_version", - "language", - "country", - "region", - "city", - "device_type", - "referrer", - "page_title", "querystring", "channel", + "referrer", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", - "dimensions", - "user_id", - "lat", - "lon", "tag", ]; -export const SESSION_PAGE_FILTERS: FilterParameter[] = [...BASE_FILTERS, "pathname", "entry_page", "exit_page", "event_name"]; - -// Single-user detail page: the page is already scoped to one user, so the -// user_id filter is excluded. -export const USER_DETAIL_PAGE_FILTERS: FilterParameter[] = SESSION_PAGE_FILTERS.filter(f => f !== "user_id"); - -export const EVENT_FILTERS: FilterParameter[] = [ - ...BASE_FILTERS, - "pathname", - "page_title", - "event_name", - "entry_page", - "exit_page", -]; - -export const GOALS_PAGE_FILTERS: FilterParameter[] = [...BASE_FILTERS]; - -export const FUNNEL_PAGE_FILTERS: FilterParameter[] = [...BASE_FILTERS]; - -export const USER_PAGE_FILTERS: FilterParameter[] = [ - ...BASE_FILTERS, - "pathname", - "entry_page", - "exit_page", +const APP_ONLY_FILTERS: FilterParameter[] = [ + "device_model", + "app_version", ]; -export const JOURNEY_PAGE_FILTERS: FilterParameter[] = [ - "hostname", +const COMMON_FILTERS: FilterParameter[] = [ "browser", + "browser_version", "operating_system", + "operating_system_version", "language", "country", "region", "city", "device_type", - "referrer", - // "channel", - // "utm_source", - // "utm_medium", - // "utm_campaign", - // "utm_term", - // "utm_content", - "entry_page", - "exit_page", + "page_title", "dimensions", - "browser_version", - "operating_system_version", "user_id", "lat", "lon", ]; -export const SESSION_REPLAY_PAGE_FILTERS: FilterParameter[] = [ - "hostname", - "browser", - "browser_version", - "operating_system", - "operating_system_version", - "language", - "country", - "region", - "city", - "device_type", - "referrer", - "channel", - "user_id", -]; +function getBaseFilters(isApp: boolean): FilterParameter[] { + if (isApp) { + return [...APP_ONLY_FILTERS, ...COMMON_FILTERS]; + } + return [...WEB_ONLY_FILTERS, ...COMMON_FILTERS]; +} + +export function getSessionPageFilters(isApp: boolean): FilterParameter[] { + return [...getBaseFilters(isApp), "pathname", "entry_page", "exit_page", "event_name"]; +} + +export function getEventFilters(isApp: boolean): FilterParameter[] { + return [...getBaseFilters(isApp), "pathname", "event_name", "entry_page", "exit_page"]; +} + +export function getGoalsPageFilters(isApp: boolean): FilterParameter[] { + return [...getBaseFilters(isApp)]; +} + +export function getFunnelPageFilters(isApp: boolean): FilterParameter[] { + return [...getBaseFilters(isApp)]; +} + +export function getUserPageFilters(isApp: boolean): FilterParameter[] { + const base: FilterParameter[] = [ + "browser", + "browser_version", + "operating_system", + "operating_system_version", + "language", + "country", + "region", + "city", + "device_type", + "user_id", + ]; + if (isApp) { + return ["device_model", "app_version", ...base, "pathname", "entry_page", "exit_page"]; + } + return ["hostname", "referrer", ...base, "pathname", "entry_page", "exit_page"]; +} + +export function getJourneyPageFilters(isApp: boolean): FilterParameter[] { + const base: FilterParameter[] = [ + "browser", + "operating_system", + "language", + "country", + "region", + "city", + "device_type", + "entry_page", + "exit_page", + "dimensions", + "browser_version", + "operating_system_version", + "user_id", + "lat", + "lon", + ]; + if (isApp) { + return ["device_model", "app_version", ...base]; + } + return [ + "hostname", + "referrer", + // "channel", + // "utm_source", + // "utm_medium", + // "utm_campaign", + // "utm_term", + // "utm_content", + ...base, + ]; +} + +export function getSessionReplayPageFilters(isApp: boolean): FilterParameter[] { + const base: FilterParameter[] = [ + "browser", + "browser_version", + "operating_system", + "operating_system_version", + "language", + "country", + "region", + "city", + "device_type", + "user_id", + ]; + if (isApp) { + return ["device_model", "app_version", ...base]; + } + return ["hostname", "referrer", "channel", ...base]; +} + +// Static exports used by API hooks - include ALL filters (web + app union) +// so that any active filter passes through regardless of site type. +// The SubHeader UI uses the dynamic getter functions above to show only relevant filters. +function allFilters(...arrays: FilterParameter[][]): FilterParameter[] { + return [...new Set(arrays.flat())]; +} +export const SESSION_PAGE_FILTERS: FilterParameter[] = allFilters(getSessionPageFilters(false), getSessionPageFilters(true)); +// Single-user detail page: the page is already scoped to one user, so the +// user_id filter is excluded. +export const USER_DETAIL_PAGE_FILTERS: FilterParameter[] = SESSION_PAGE_FILTERS.filter(f => f !== "user_id"); +export const EVENT_FILTERS: FilterParameter[] = allFilters(getEventFilters(false), getEventFilters(true)); +export const GOALS_PAGE_FILTERS: FilterParameter[] = allFilters(getGoalsPageFilters(false), getGoalsPageFilters(true)); +export const FUNNEL_PAGE_FILTERS: FilterParameter[] = allFilters(getFunnelPageFilters(false), getFunnelPageFilters(true)); +export const USER_PAGE_FILTERS: FilterParameter[] = allFilters(getUserPageFilters(false), getUserPageFilters(true)); +export const JOURNEY_PAGE_FILTERS: FilterParameter[] = allFilters(getJourneyPageFilters(false), getJourneyPageFilters(true)); +export const SESSION_REPLAY_PAGE_FILTERS: FilterParameter[] = allFilters(getSessionReplayPageFilters(false), getSessionReplayPageFilters(true)); diff --git a/client/src/lib/imageUtils.ts b/client/src/lib/imageUtils.ts new file mode 100644 index 000000000..9f4787add --- /dev/null +++ b/client/src/lib/imageUtils.ts @@ -0,0 +1,45 @@ +/** + * Resize an image file to a square icon using canvas. + * Returns a base64-encoded PNG string (without data URI prefix). + */ +export function resizeImageToIcon( + file: File, + size = 128 +): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + const url = URL.createObjectURL(file); + + img.onload = () => { + URL.revokeObjectURL(url); + + const canvas = document.createElement("canvas"); + canvas.width = size; + canvas.height = size; + + const ctx = canvas.getContext("2d"); + if (!ctx) { + reject(new Error("Could not get canvas context")); + return; + } + + // Fit the image inside the square canvas, preserving aspect ratio + const scale = Math.min(size / img.width, size / img.height); + const drawWidth = img.width * scale; + const drawHeight = img.height * scale; + ctx.drawImage(img, (size - drawWidth) / 2, (size - drawHeight) / 2, drawWidth, drawHeight); + + const dataUrl = canvas.toDataURL("image/png"); + // Strip the "data:image/png;base64," prefix + const base64 = dataUrl.split(",")[1]; + resolve(base64); + }; + + img.onerror = () => { + URL.revokeObjectURL(url); + reject(new Error("Failed to load image")); + }; + + img.src = url; + }); +} diff --git a/client/src/lib/utils.ts b/client/src/lib/utils.ts index 5131e4407..138b2565a 100644 --- a/client/src/lib/utils.ts +++ b/client/src/lib/utils.ts @@ -120,6 +120,12 @@ export function isValidDomain(domain: string): boolean { return domainRegex.test(domain); } +export function isValidPackageName(name: string): boolean { + // Android package segments allow letters, digits and underscores; iOS bundle + // identifiers additionally allow hyphens. Accept the union of both. + return /^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)+$/.test(name); +} + export function getUserDisplayName< T extends { identified_user_id?: string; traits?: Record | null; user_id?: string }, >(data: T) { diff --git a/docs/content/docs/(docs)/sdks/flutter.mdx b/docs/content/docs/(docs)/sdks/flutter.mdx new file mode 100644 index 000000000..8bacea1e0 --- /dev/null +++ b/docs/content/docs/(docs)/sdks/flutter.mdx @@ -0,0 +1,151 @@ +--- +title: Flutter +description: Community Flutter SDK for Rybbit Analytics +--- + +import { Callout } from 'fumadocs-ui/components/callout'; + +The `rybbit_flutter_sdk` package tracks screen views, events, errors, and users in Flutter apps across Android, iOS, macOS, Windows, Linux, and web. Like React Native, mobile sites get the App Analytics dashboard mode with mobile-specific labels. + + +This is a community-maintained SDK, not an official Rybbit package. Source and issues: [nks-hub/rybbit-flutter-sdk](https://github.com/nks-hub/rybbit-flutter-sdk). + + +## Creating a Mobile App Site + +Before installing the SDK, create a site for your app in the Rybbit dashboard: + +1. Click **Add Site** and pick the mobile app option instead of Website. +2. Enter an app identifier (e.g. `com.example.app`). Letters, numbers, dots, hyphens, and underscores are allowed. +3. Copy the Site ID — you'll pass it to `Rybbit.init()`. + +## Installation + +```yaml +dependencies: + rybbit_flutter_sdk: + git: + url: https://github.com/nks-hub/rybbit-flutter-sdk.git + ref: main +``` + +## Initialization + +Call `Rybbit.init()` once, before `runApp()`. Events tracked before initialization completes are queued (up to 100) and replayed once the SDK is ready. + +```dart +import 'package:rybbit_flutter_sdk/rybbit_flutter_sdk.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await Rybbit.init( + host: 'https://app.rybbit.io', + siteId: '1', + ); + + runApp(const MyApp()); +} +``` + +### Configuration Options + +| Option | Type | Default | Description | +| --------------------- | --------------------- | ------- | -------------------------------------------------------------- | +| `host` | `String` | — | **Required.** Base URL of your Rybbit instance. | +| `siteId` | `String` | — | **Required.** Site ID from the dashboard. | +| `debug` | `bool` | `false` | Verbose logging. | +| `dryRun` | `bool` | `false` | Log payloads without sending them. | +| `autoTrackLifecycle` | `bool` | `true` | Emit `app_open`, `app_foreground`, `app_background`. | +| `autoTrackErrors` | `bool` | `true` | Capture uncaught Flutter and Dart errors. | +| `autoUploadIcon` | `bool` | `true` | Attempt to upload the launcher icon on first run. See below. | +| `iconAssetPath` | `String?` | `null` | Asset to upload instead of the platform launcher icon. | +| `globalProperties` | `Map`| `{}` | Merged into every event's properties. | +| `maxOfflineEvents` | `int` | `1000` | Offline queue size. | +| `offlineTtlDays` | `int` | `7` | How long queued events stay valid. | +| `flushInterval` | `Duration` | `10s` | Buffer flush period. | +| `flushThreshold` | `int` | `20` | Flush immediately after this many buffered events. | + +## Tracking Screens + +```dart +Rybbit.instance.screenView('/checkout', title: 'Checkout'); +``` + +For automatic tracking, attach the navigator observer: + +```dart +MaterialApp( + navigatorObservers: [RybbitNavigatorObserver()], +); +``` + +## Tracking Custom Events + +```dart +Rybbit.instance.event('button_click', properties: {'button': 'cta'}); +``` + +The GA4 extension adds typed helpers for common events: + +```dart +import 'package:rybbit_flutter_sdk/ga4_events.dart'; + +Rybbit.instance.trackPurchase(transactionId: 'tx-1', value: 299.0, currency: 'CZK'); +Rybbit.instance.trackLogin(method: 'google'); +Rybbit.instance.trackSearch(searchTerm: 'query', resultsCount: 42); +``` + +## Error Tracking + +Uncaught errors are captured automatically when `autoTrackErrors` is on. To report one yourself: + +```dart +try { + await risky(); +} catch (error, stackTrace) { + Rybbit.instance.trackError(error, stackTrace, context: {'screen': '/checkout'}); +} +``` + +To also catch errors thrown outside the Flutter zone, start the app through the SDK: + +```dart +Rybbit.runApp(const MyApp()); +``` + +## User Identification + +```dart +Rybbit.instance.identify('user-123', traits: {'plan': 'pro'}); +Rybbit.instance.setTraits({'plan': 'enterprise'}); +Rybbit.instance.clearUserId(); +``` + +## Device and App Metadata + +Every event carries the app version and device model, which the dashboard surfaces as filters and on the user detail page. The SDK derives them from `package_info_plus` and `device_info_plus` and sends them both as explicit fields and inside an RFC 7231 User-Agent: + +``` +MyApp/1.4.2 (com.example.app; Android 14; SM-G991B) RybbitFlutter/0.2.4 +``` + +The server prefers the explicit fields and falls back to parsing the User-Agent, so older SDK versions keep reporting correctly. + +## App Icon + +Mobile sites can carry an icon, shown as their favicon in the sidebar, site selector, and site cards. Upload it from **Site Settings → General → App Icon**. + + +The SDK's `autoUploadIcon` option tries to upload the launcher icon on first run, but `PUT /api/sites/:siteId/icon` requires an authenticated site admin — an app shipping to end users has no such session, so the request is rejected and the SDK logs a warning. Set `autoUploadIcon: false` and upload the icon from the dashboard instead. + + +## Offline Handling and Retries + +Events are buffered in memory and flushed on a timer or once the buffer fills. Failed sends move to a Hive-backed persistent queue that survives app restarts, retries up to three times, and drains automatically when connectivity returns. + +## Differences from the Web SDK + +- **Session replay** and **Web Vitals** are web-only and not available for Flutter apps. +- No automatic outbound link, button click, form, or copy tracking (there is no DOM). +- The anonymous visitor ID is generated by the SDK and persisted locally, rather than derived from browser fingerprinting. diff --git a/docs/content/docs/(docs)/sdks/meta.json b/docs/content/docs/(docs)/sdks/meta.json index 546713caf..b3a4c6667 100644 --- a/docs/content/docs/(docs)/sdks/meta.json +++ b/docs/content/docs/(docs)/sdks/meta.json @@ -3,6 +3,7 @@ "pages": [ "web", "react-native", + "flutter", "node" ] } diff --git a/server/drizzle/0014_site_icon.sql b/server/drizzle/0014_site_icon.sql new file mode 100644 index 000000000..499af35d9 --- /dev/null +++ b/server/drizzle/0014_site_icon.sql @@ -0,0 +1 @@ +ALTER TABLE "sites" ADD COLUMN IF NOT EXISTS "icon" bytea; diff --git a/server/drizzle/meta/_journal.json b/server/drizzle/meta/_journal.json index 3b232a627..14a30d3a2 100644 --- a/server/drizzle/meta/_journal.json +++ b/server/drizzle/meta/_journal.json @@ -99,6 +99,13 @@ "when": 1784659838680, "tag": "0013_square_bloodaxe", "breakpoints": true + }, + { + "idx": 14, + "version": "7", + "when": 1784659838681, + "tag": "0014_site_icon", + "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/server/src/api/analytics/events/getEvents.ts b/server/src/api/analytics/events/getEvents.ts index 7bcc87a40..eb3908b14 100644 --- a/server/src/api/analytics/events/getEvents.ts +++ b/server/src/api/analytics/events/getEvents.ts @@ -30,6 +30,8 @@ export type GetEventsResponse = { device_type: string; type: string; page_title: string; + device_model: string; + app_version: string; }[]; interface GetEventsRequest { @@ -68,7 +70,9 @@ const EVENT_COLUMNS = ` screen_width, screen_height, device_type, - type + type, + device_model, + app_version `; const EVENT_TYPE_FILTER = `AND type IN ('custom_event', 'pageview', 'outbound', 'button_click', 'copy', 'form_submit', 'input_change')`; diff --git a/server/src/api/analytics/sessions/getSession.ts b/server/src/api/analytics/sessions/getSession.ts index 71946b3d0..7c0ef5628 100644 --- a/server/src/api/analytics/sessions/getSession.ts +++ b/server/src/api/analytics/sessions/getSession.ts @@ -10,6 +10,8 @@ export interface SessionDetails { city: string; language: string; device_type: string; + device_model: string; + app_version: string; browser: string; browser_version: string; operating_system: string; @@ -82,6 +84,8 @@ SELECT any(city) as city, any(language) as language, any(device_type) as device_type, + any(device_model) as device_model, + any(app_version) as app_version, any(browser) as browser, any(browser_version) as browser_version, any(operating_system) as operating_system, diff --git a/server/src/api/analytics/sessions/getSessions.ts b/server/src/api/analytics/sessions/getSessions.ts index 963f5a46e..a82fea86b 100644 --- a/server/src/api/analytics/sessions/getSessions.ts +++ b/server/src/api/analytics/sessions/getSessions.ts @@ -22,6 +22,8 @@ export type GetSessionsResponse = { operating_system_version: string; screen_width: number; screen_height: number; + device_model: string; + app_version: string; referrer: string; channel: string; hostname: string; @@ -123,6 +125,8 @@ export const buildSessionsQuery = (query: GetSessionsRequest["Querystring"], sit argMax(operating_system_version, timestamp) AS operating_system_version, argMax(screen_width, timestamp) AS screen_width, argMax(screen_height, timestamp) AS screen_height, + argMax(device_model, timestamp) AS device_model, + argMax(app_version, timestamp) AS app_version, ${SESSION_REFERRER_AGG} AS referrer, ${SESSION_CHANNEL_AGG} AS channel, argMin(hostname, timestamp) AS hostname, diff --git a/server/src/api/analytics/users/getUserInfo.ts b/server/src/api/analytics/users/getUserInfo.ts index 876cb40cd..055e386a8 100644 --- a/server/src/api/analytics/users/getUserInfo.ts +++ b/server/src/api/analytics/users/getUserInfo.ts @@ -19,6 +19,8 @@ interface UserPageviewData { city: string; language: string; device_type: string; + device_model: string; + app_version: string; browser: string; browser_version: string; operating_system: string; @@ -116,6 +118,8 @@ export const buildUserInfoQueries = (query: FilterParams, siteId: number) => { argMax(city, timestamp) AS city, argMax(language, timestamp) AS language, argMax(device_type, timestamp) AS device_type, + argMax(device_model, timestamp) AS device_model, + argMax(app_version, timestamp) AS app_version, argMax(browser, timestamp) AS browser, argMax(browser_version, timestamp) AS browser_version, argMax(operating_system, timestamp) AS operating_system, @@ -152,6 +156,8 @@ export const buildUserInfoQueries = (query: FilterParams, siteId: number) => { any(city) AS city, any(language) AS language, any(device_type) AS device_type, + any(device_model) AS device_model, + any(app_version) AS app_version, any(browser) AS browser, any(browser_version) AS browser_version, any(operating_system) AS operating_system, diff --git a/server/src/api/analytics/users/getUsers.ts b/server/src/api/analytics/users/getUsers.ts index 6e3238dfd..c8b311eab 100644 --- a/server/src/api/analytics/users/getUsers.ts +++ b/server/src/api/analytics/users/getUsers.ts @@ -19,6 +19,8 @@ export type GetUsersResponse = { browser: string; operating_system: string; device_type: string; + device_model: string; + app_version: string; pageviews: number; events: number; sessions: number; @@ -119,6 +121,8 @@ WITH AggregatedUsers AS ( argMax(operating_system, timestamp) AS operating_system, argMax(operating_system_version, timestamp) AS operating_system_version, argMax(device_type, timestamp) AS device_type, + argMax(device_model, timestamp) AS device_model, + argMax(app_version, timestamp) AS app_version, argMax(screen_width, timestamp) AS screen_width, argMax(screen_height, timestamp) AS screen_height, ${SESSION_REFERRER_AGG} AS referrer, diff --git a/server/src/api/analytics/utils/query-validation.ts b/server/src/api/analytics/utils/query-validation.ts index 13d4efcc6..1d8749920 100644 --- a/server/src/api/analytics/utils/query-validation.ts +++ b/server/src/api/analytics/utils/query-validation.ts @@ -265,6 +265,8 @@ const baseFilterParamSchema = z.enum([ "region", "city", "device_type", + "device_model", + "app_version", "referrer", "hostname", "pathname", diff --git a/server/src/api/sites/index.ts b/server/src/api/sites/index.ts index af28fb376..2792aaa86 100644 --- a/server/src/api/sites/index.ts +++ b/server/src/api/sites/index.ts @@ -29,3 +29,6 @@ export { getSiteImports } from "./getSiteImports.js"; export { createSiteImport } from "./createSiteImport.js"; export { batchImportEvents } from "./batchImportEvents.js"; export { deleteSiteImport } from "./deleteSiteImport.js"; + +// Site Icon +export { getSiteIcon, uploadSiteIcon, deleteSiteIcon } from "./siteIcon.js"; diff --git a/server/src/api/sites/siteIcon.test.ts b/server/src/api/sites/siteIcon.test.ts new file mode 100644 index 000000000..d3dbf93e8 --- /dev/null +++ b/server/src/api/sites/siteIcon.test.ts @@ -0,0 +1,93 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const findFirst = vi.fn(); + +vi.mock("../../db/postgres/postgres.js", () => ({ + db: { query: { sites: { findFirst: () => findFirst() } } }, +})); + +const { getSiteIcon } = await import("./siteIcon.js"); + +const ICON = Buffer.from("fake-png-bytes"); + +function replyStub() { + const headers: Record = {}; + const reply: any = { + statusCode: 200, + body: undefined as unknown, + header(name: string, value: string) { + headers[name] = value; + return reply; + }, + status(code: number) { + reply.statusCode = code; + return reply; + }, + send(payload: unknown) { + reply.body = payload; + return reply; + }, + headers, + }; + return reply; +} + +function request(ifNoneMatch?: string) { + return { + params: { siteId: "42" }, + headers: ifNoneMatch ? { "if-none-match": ifNoneMatch } : {}, + } as any; +} + +describe("getSiteIcon", () => { + beforeEach(() => { + findFirst.mockReset(); + }); + + it("serves the icon with a revalidating ETag", async () => { + findFirst.mockResolvedValue({ icon: ICON }); + const reply = replyStub(); + + await getSiteIcon(request(), reply); + + expect(reply.statusCode).toBe(200); + expect(reply.body).toEqual(ICON); + expect(reply.headers["ETag"]).toMatch(/^"[a-f0-9]{40}"$/); + expect(reply.headers["Cache-Control"]).toContain("must-revalidate"); + }); + + it("returns 304 when the client already has that icon", async () => { + findFirst.mockResolvedValue({ icon: ICON }); + const first = replyStub(); + await getSiteIcon(request(), first); + + findFirst.mockResolvedValue({ icon: ICON }); + const second = replyStub(); + await getSiteIcon(request(first.headers["ETag"]), second); + + expect(second.statusCode).toBe(304); + }); + + it("serves a re-uploaded icon instead of a 304", async () => { + findFirst.mockResolvedValue({ icon: ICON }); + const before = replyStub(); + await getSiteIcon(request(), before); + + // Same URL, different bytes — the stale ETag must no longer match. + findFirst.mockResolvedValue({ icon: Buffer.from("different-png-bytes") }); + const after = replyStub(); + await getSiteIcon(request(before.headers["ETag"]), after); + + expect(after.statusCode).toBe(200); + expect(after.headers["ETag"]).not.toBe(before.headers["ETag"]); + }); + + it("404s when the site has no icon", async () => { + findFirst.mockResolvedValue({ icon: null }); + const reply = replyStub(); + + await getSiteIcon(request(), reply); + + expect(reply.statusCode).toBe(404); + }); +}); diff --git a/server/src/api/sites/siteIcon.ts b/server/src/api/sites/siteIcon.ts new file mode 100644 index 000000000..1100ef421 --- /dev/null +++ b/server/src/api/sites/siteIcon.ts @@ -0,0 +1,128 @@ +import { createHash } from "crypto"; +import { eq } from "drizzle-orm"; +import { FastifyReply, FastifyRequest } from "fastify"; +import { db } from "../../db/postgres/postgres.js"; +import { sites } from "../../db/postgres/schema.js"; + +interface SiteIdParams { + Params: { + siteId: string; + }; +} + +const MAX_ICON_SIZE = 50 * 1024; // 50KB + +export async function getSiteIcon( + request: FastifyRequest, + reply: FastifyReply +) { + const parsedSiteId = Number.parseInt(request.params.siteId, 10); + if (!Number.isInteger(parsedSiteId) || parsedSiteId <= 0) { + return reply.status(400).send({ error: "Invalid site ID" }); + } + + try { + const site = await db.query.sites.findFirst({ + where: eq(sites.siteId, parsedSiteId), + columns: { icon: true }, + }); + + if (!site?.icon) { + return reply.status(404).send({ error: "No icon found" }); + } + + const icon = Buffer.from(site.icon); + // The URL is fixed, so without revalidation a re-uploaded icon would stay + // stale in every sidebar and site card until the cache expired. + const etag = `"${createHash("sha1").update(icon).digest("hex")}"`; + + if (request.headers["if-none-match"] === etag) { + return reply.header("ETag", etag).header("Cache-Control", "public, max-age=0, must-revalidate").status(304).send(); + } + + return reply + .header("Content-Type", "image/png") + .header("ETag", etag) + .header("Cache-Control", "public, max-age=0, must-revalidate") + .send(icon); + } catch (error) { + console.error("Error retrieving site icon:", error); + return reply.status(500).send({ error: "Internal server error" }); + } +} + +export async function uploadSiteIcon( + request: FastifyRequest, + reply: FastifyReply +) { + const parsedSiteId = Number.parseInt(request.params.siteId, 10); + if (!Number.isInteger(parsedSiteId) || parsedSiteId <= 0) { + return reply.status(400).send({ error: "Invalid site ID" }); + } + + if (!request.body) { + return reply.status(400).send({ error: "Request body is required" }); + } + + const { icon } = request.body as { icon: string }; + + if (!icon) { + return reply.status(400).send({ error: "icon field is required" }); + } + + try { + const buffer = Buffer.from(icon, "base64"); + + if (buffer.length > MAX_ICON_SIZE) { + return reply.status(400).send({ error: "Icon exceeds 50KB limit" }); + } + + // Validate PNG magic bytes + const pngMagic = Buffer.from([0x89, 0x50, 0x4e, 0x47]); + if (buffer.length < 4 || !buffer.subarray(0, 4).equals(pngMagic)) { + return reply.status(400).send({ error: "Icon must be a PNG image" }); + } + + const result = await db + .update(sites) + .set({ icon: buffer }) + .where(eq(sites.siteId, parsedSiteId)) + .returning({ siteId: sites.siteId }); + + if (result.length === 0) { + return reply.status(404).send({ error: "Site not found" }); + } + + return reply.status(200).send({ success: true }); + } catch (error) { + console.error("Error uploading site icon:", error); + return reply.status(500).send({ error: "Internal server error" }); + } +} + +export async function deleteSiteIcon( + request: FastifyRequest, + reply: FastifyReply +) { + const parsedSiteId = Number.parseInt(request.params.siteId, 10); + if (!Number.isInteger(parsedSiteId) || parsedSiteId <= 0) { + return reply.status(400).send({ error: "Invalid site ID" }); + } + + try { + const result = await db + .update(sites) + .set({ icon: null }) + .where(eq(sites.siteId, parsedSiteId)) + .returning({ siteId: sites.siteId }); + + if (result.length === 0) { + return reply.status(404).send({ error: "Site not found" }); + } + + return reply.status(200).send({ success: true }); + } catch (error) { + console.error("Error deleting site icon:", error); + return reply.status(500).send({ error: "Internal server error" }); + } +} diff --git a/server/src/db/clickhouse/schema/core.ts b/server/src/db/clickhouse/schema/core.ts index cd0421a34..f525397e1 100644 --- a/server/src/db/clickhouse/schema/core.ts +++ b/server/src/db/clickhouse/schema/core.ts @@ -20,6 +20,9 @@ const EVENTS_COLUMNS_TO_ENSURE: ColumnDefinition[] = [ { name: "asn", definition: "asn Nullable(UInt32)" }, { name: "asn_org", definition: "asn_org LowCardinality(String) DEFAULT ''" }, { name: "is_datacenter_asn", definition: "is_datacenter_asn UInt8 DEFAULT 0" }, + // Reported by native SDKs on mobile sites; empty for browser traffic. + { name: "app_version", definition: "app_version LowCardinality(String) DEFAULT ''" }, + { name: "device_model", definition: "device_model LowCardinality(String) DEFAULT ''" }, ]; const BOT_EVENTS_COLUMNS_TO_ENSURE: ColumnDefinition[] = [ @@ -121,7 +124,9 @@ export async function initializeCoreTables() { feature_flags Map(String, String) DEFAULT map(), asn Nullable(UInt32), asn_org LowCardinality(String) DEFAULT '', - is_datacenter_asn UInt8 DEFAULT 0 + is_datacenter_asn UInt8 DEFAULT 0, + app_version LowCardinality(String) DEFAULT '', + device_model LowCardinality(String) DEFAULT '' ) ENGINE = MergeTree() PARTITION BY toYYYYMM(timestamp) diff --git a/server/src/db/postgres/schema.ts b/server/src/db/postgres/schema.ts index 999190e5d..d542802bf 100644 --- a/server/src/db/postgres/schema.ts +++ b/server/src/db/postgres/schema.ts @@ -3,6 +3,7 @@ import type { DashboardConfig } from "@rybbit/shared"; import { boolean, check, + customType, foreignKey, index, integer, @@ -18,6 +19,12 @@ import { uuid, } from "drizzle-orm/pg-core"; +const bytea = customType<{ data: Buffer }>({ + dataType() { + return "bytea"; + }, +}); + // User table (BetterAuth) export const user = pgTable( "user", @@ -100,6 +107,7 @@ export const sites = pgTable( apiKey: text("api_key"), // Format: rb_{64_hex_chars} = 67 chars total privateLinkKey: text("private_link_key"), tags: jsonb("tags").default([]).$type(), + icon: bytea("icon"), }, table => [check("sites_type_check", sql`${table.type} IS NULL OR ${table.type} IN ('web', 'mobile')`)] ); diff --git a/server/src/index.ts b/server/src/index.ts index a0c261dca..c4269a60b 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -114,6 +114,7 @@ import { createSiteImport, deleteSite, deleteSiteImport, + deleteSiteIcon, getEmbedStats, getSite, getSiteExcludedCountries, @@ -124,6 +125,7 @@ import { getSiteExcludedASNs, getSiteExcludedQueryParams, getSiteHasData, + getSiteIcon, getSiteImports, getSiteIsPublic, getSiteUsage, @@ -133,6 +135,7 @@ import { moveSite, updateSiteConfig, updateSitePrivateLinkConfig, + uploadSiteIcon, } from "./api/sites/index.js"; import { createCheckoutSession, @@ -446,6 +449,11 @@ async function sitesRoutes(fastify: FastifyInstance) { // Site Usage fastify.get("/sites/:siteId/usage", authSitesRead, getSiteUsage); + // Site Icon (GET is fully public - it's just a favicon) + fastify.get("/sites/:siteId/icon", { preHandler: [resolveSiteId] as any }, getSiteIcon); + fastify.put("/sites/:siteId/icon", adminSitesWrite, uploadSiteIcon); + fastify.delete("/sites/:siteId/icon", adminSitesWrite, deleteSiteIcon); + // Site Imports fastify.get("/sites/:siteId/imports", adminSitesRead, getSiteImports); fastify.post("/sites/:siteId/imports", adminSitesWrite, createSiteImport); diff --git a/server/src/lib/auth-utils.test.ts b/server/src/lib/auth-utils.test.ts index b41b31802..a06a939ed 100644 --- a/server/src/lib/auth-utils.test.ts +++ b/server/src/lib/auth-utils.test.ts @@ -110,7 +110,8 @@ CREATE TABLE "sites" ( "trackFormInteractions" boolean DEFAULT false, "api_key" text, "private_link_key" text, - "tags" jsonb DEFAULT '[]' + "tags" jsonb DEFAULT '[]', + "icon" bytea ); `; diff --git a/server/src/services/tracker/pageviewQueue.ts b/server/src/services/tracker/pageviewQueue.ts index 259f4128a..bbdaa211b 100644 --- a/server/src/services/tracker/pageviewQueue.ts +++ b/server/src/services/tracker/pageviewQueue.ts @@ -3,7 +3,7 @@ import { clickhouse } from "../../db/clickhouse/clickhouse.js"; import { lookupAsn } from "../../db/geolocation/asn.js"; import { getLocation } from "../../db/geolocation/geolocation.js"; import { createServiceLogger } from "../../lib/logger/logger.js"; -import { getDeviceType } from "../../utils.js"; +import { getDeviceType, parseSDKUserAgent } from "../../utils.js"; import { isDatacenterAsn } from "./botBlocking/datacenterAsns.js"; import { getChannel } from "./getChannel.js"; import { clearSelfReferrer, getAllUrlParams, TotalTrackingPayload } from "./utils.js"; @@ -64,6 +64,7 @@ class PageviewQueue { const longitude = dataForIp?.longitude || 0; const city = dataForIp?.city || ""; const timezone = dataForIp?.timeZone || ""; + const sdkUA = parseSDKUserAgent(pv.ua.ua || ""); // Same MaxMind lookup already used to decide identity bucketing // (bucketIpForIdentity) and bot-detection eligibility; stored here purely @@ -90,14 +91,18 @@ class PageviewQueue { page_title: pv.page_title || "", referrer: referrer, channel: getChannel(referrer, pv.querystring, pv.hostname), - browser: pv.ua.browser.name || "", - browser_version: pv.ua.browser.major || "", - operating_system: pv.ua.os.name || "", - operating_system_version: pv.ua.os.version || "", + browser: sdkUA ? sdkUA.browser : (pv.ua.browser.name || ""), + browser_version: sdkUA ? sdkUA.browserVersion : (pv.ua.browser.major || ""), + operating_system: sdkUA ? sdkUA.os : (pv.ua.os.name || ""), + operating_system_version: sdkUA ? sdkUA.osVersion : (pv.ua.os.version || ""), language: pv.language || "", screen_width: pv.screenWidth || 0, screen_height: pv.screenHeight || 0, - device_type: getDeviceType(pv.screenWidth, pv.screenHeight, pv.ua), + device_type: getDeviceType( + pv.screenWidth, + pv.screenHeight, + sdkUA ? { ...pv.ua, os: { ...pv.ua.os, name: sdkUA.os } } : pv.ua + ), country: countryCode, region: countryCode && regionCode ? countryCode + "-" + regionCode : "", city: city || "", @@ -123,6 +128,8 @@ class PageviewQueue { asn: asnInfo?.asn ?? null, asn_org: asnInfo?.organization || "", is_datacenter_asn: asnInfo && isDatacenterAsn(asnInfo.asn) ? 1 : 0, + app_version: pv.app_version || "", + device_model: pv.device_model || sdkUA?.deviceModel || "", }; }); diff --git a/server/src/services/tracker/trackEvent.nativeSdk.test.ts b/server/src/services/tracker/trackEvent.nativeSdk.test.ts new file mode 100644 index 000000000..05aedf94f --- /dev/null +++ b/server/src/services/tracker/trackEvent.nativeSdk.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; +import { trackingPayloadSchema } from "./trackEvent.js"; + +// The payload schema is strict, so app_version/device_model only survive +// because baseEventFields declares them. Native SDKs send both on every event; +// dropping the declaration turns each one into a 400 with no server-side +// symptom other than traffic quietly disappearing. + +const UA = "MyApp/1.4.2 (cz.nkshub.myapp; Android 14; SM-G991B) RybbitFlutter/0.2.4"; + +const nativeBase = { + site_id: "42", + hostname: "cz.nkshub.myapp", + pathname: "/home", + screenWidth: 1080, + screenHeight: 2400, + language: "cs-CZ", + page_title: "Home", + user_id: "user-123", + user_agent: UA, + app_version: "1.4.2", + device_model: "SM-G991B", +}; + +describe("native SDK payloads", () => { + it.each([ + ["screen view", { type: "pageview" }], + ["custom event", { type: "custom_event", event_name: "app_open", properties: '{"source":"cold"}' }], + [ + "error with stack", + { + type: "error", + event_name: "StateError", + properties: '{"message":"Bad state: no element","stack":"#0 main (file:///app.dart:1:1)"}', + }, + ], + ])("keeps app_version and device_model on a %s", (_label, event) => { + const result = trackingPayloadSchema.safeParse({ ...nativeBase, ...event }); + + expect(result.success).toBe(true); + if (!result.success) return; + expect(result.data).toMatchObject({ app_version: "1.4.2", device_model: "SM-G991B" }); + }); + + it("still accepts browser payloads that omit both fields", () => { + const result = trackingPayloadSchema.safeParse({ + site_id: "42", + type: "pageview", + hostname: "example.com", + pathname: "/", + user_agent: "Mozilla/5.0", + }); + + expect(result.success).toBe(true); + }); + + it("rejects fields that were never declared", () => { + const result = trackingPayloadSchema.safeParse({ ...nativeBase, type: "pageview", bogus_field: "x" }); + + expect(result.success).toBe(false); + }); +}); diff --git a/server/src/services/tracker/trackEvent.ts b/server/src/services/tracker/trackEvent.ts index 7f54c6b39..3eb6b47c9 100644 --- a/server/src/services/tracker/trackEvent.ts +++ b/server/src/services/tracker/trackEvent.ts @@ -32,6 +32,9 @@ const baseEventFields = { user_agent: z.string().max(512).optional(), _bs: z.number().int().min(0).max(10).optional(), _bsm: z.number().int().min(0).max(2047).optional(), + // Sent by native SDKs (rybbit-flutter and friends); browsers never set these. + app_version: z.string().max(50).optional(), + device_model: z.string().max(200).optional(), }; // Default event_name and properties used by pageview and performance diff --git a/server/src/services/tracker/utils.ts b/server/src/services/tracker/utils.ts index c20252b55..8777b6399 100644 --- a/server/src/services/tracker/utils.ts +++ b/server/src/services/tracker/utils.ts @@ -26,6 +26,8 @@ export type TotalTrackingPayload = TrackingPayload & { ttfb?: number; tag?: string; feature_flags?: Record; + app_version?: string; + device_model?: string; }; // Infer type from Zod schema diff --git a/server/src/utils.ts b/server/src/utils.ts index 4946e68c0..ffc852b6c 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -123,6 +123,47 @@ export function getDeviceType(screenWidth: number, screenHeight: number, ua: UAP return "Mobile"; } +// RFC 7231 §5.5.3 User-Agent parser for SDK clients. +// 3-part: AppName/Version (packageName; Platform OS; deviceModel) SDKName/Version +// 2-part: AppName/Version (Platform OS; deviceModel) SDKName/Version +export function parseSDKUserAgent(userAgent: string): { + browser: string; + browserVersion: string; + os: string; + osVersion: string; + deviceModel: string; +} | null { + // 3-part format (with packageName) + const match3 = userAgent.match( + /^(.+?)\/(\S+)\s+\([^;]+;\s*(\w+)\s+([^;]+);\s*([^)]+)\)\s+\S+\/\S+$/ + ); + if (match3) { + return { + browser: match3[1], + browserVersion: match3[2], + os: match3[3], + osVersion: match3[4].trim(), + deviceModel: match3[5].trim(), + }; + } + + // 2-part format (without packageName) + const match2 = userAgent.match( + /^(.+?)\/(\S+)\s+\((\w+)\s+([^;]+);\s*([^)]+)\)\s+\S+\/\S+$/ + ); + if (match2) { + return { + browser: match2[1], + browserVersion: match2[2], + os: match2[3], + osVersion: match2[4].trim(), + deviceModel: match2[5].trim(), + }; + } + + return null; +} + // Extract site ID from path export const extractSiteId = (path: string) => { // Remove query parameters if present diff --git a/shared/src/filters.ts b/shared/src/filters.ts index ad606e7c9..a2ae74b95 100644 --- a/shared/src/filters.ts +++ b/shared/src/filters.ts @@ -22,6 +22,8 @@ export type FilterParameter = | "region" | "city" | "device_type" + | "device_model" + | "app_version" | "referrer" | "hostname" | "pathname"