From b2331cf22ffa01e00c55511395359f22cdb93394 Mon Sep 17 00:00:00 2001 From: Matt Marcin Date: Fri, 12 Jun 2026 07:22:04 -0700 Subject: [PATCH] feat(capture): add Npcap capture method and fix CJK font display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Npcap as a user-selectable alternative to Windows raw-socket capture in Settings -> Network, for users whose antivirus (e.g. Avast Web Shield) blocks raw sockets. The app loads the user's installed wpcap.dll at runtime and never bundles or downloads Npcap. Also fix the language picker rendering CJK native names (한국어, 简体中文, 繁體中文, 日本語) as tofu, by lazily loading the needed CJK fonts the first time the language dropdown opens. Upgrade rust-i18n 3.1.2 -> 4.1.0, which drops the unmaintained and unsound serde_yml/libyml YAML crates (Dependabot high + medium) in favor of serde_yaml. Locale catalogs are JSON and load unchanged. Bumps the version to 0.2.0. --- Cargo.lock | 118 ++++----- Cargo.toml | 4 +- README.md | 21 +- THIRD-PARTY-NOTICES.md | 9 + locales/da.json | 26 ++ locales/de.json | 26 ++ locales/en.json | 7 + locales/es.json | 26 ++ locales/fr.json | 26 ++ locales/he.json | 26 ++ locales/hr.json | 26 ++ locales/it.json | 26 ++ locales/ja.json | 26 ++ locales/ko.json | 26 ++ locales/no.json | 26 ++ locales/pl.json | 26 ++ locales/pt-BR.json | 26 ++ locales/pt.json | 26 ++ locales/ru.json | 26 ++ locales/sr.json | 26 ++ locales/tr.json | 26 ++ locales/uk.json | 26 ++ locales/zh-CN.json | 26 ++ locales/zh-TW.json | 26 ++ src/app.rs | 161 +++++++++++- src/capture.rs | 83 +++++- src/capture_backend.rs | 26 +- src/config.rs | 8 + src/fonts.rs | 95 ++++++- src/i18n.rs | 9 + src/lib.rs | 1 + src/npcap.rs | 555 +++++++++++++++++++++++++++++++++++++++++ src/widgets.rs | 17 +- 33 files changed, 1516 insertions(+), 92 deletions(-) create mode 100644 src/npcap.rs diff --git a/Cargo.lock b/Cargo.lock index ea3e9d1..7693c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ [[package]] name = "arctracker-sync" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "base64", @@ -1741,7 +1741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", @@ -2458,12 +2458,6 @@ dependencies = [ "x11", ] -[[package]] -name = "libyml" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3422,11 +3416,10 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime 0.6.3", "toml_edit 0.20.2", ] @@ -3690,12 +3683,11 @@ dependencies = [ [[package]] name = "rust-i18n" -version = "3.1.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039f57d22229db401af3458ca939300178e99e88b938573cea12b7c2b0f09724" +checksum = "55691a65892c33ee2de49c15ea5600c6f4a70e8eeb8e6c3cd96d2a231d230c40" dependencies = [ "globwalk", - "once_cell", "regex", "rust-i18n-macro", "rust-i18n-support", @@ -3704,41 +3696,36 @@ dependencies = [ [[package]] name = "rust-i18n-macro" -version = "3.1.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde5c022360a2e54477882843d56b6f9bcb4bc62f504b651a2f497f0028d174f" +checksum = "30de488acadcf767d97cd48518a8da8ea9777b1c9a5beca4eab78bbf77d07309" dependencies = [ "glob", - "once_cell", "proc-macro2", "quote", "rust-i18n-support", "serde", "serde_json", - "serde_yml", + "serde_yaml", "syn 2.0.117", ] [[package]] name = "rust-i18n-support" -version = "3.1.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75d2844d36f62b5d6b66f9cf8f8cbdbbbdcdb5fd37a473a9cc2fb45fdcf485d2" +checksum = "aea0fef8a93c06326b66392c95a115120e609674cb2132d37d276a6b05b545b4" dependencies = [ "arc-swap", "base62", "globwalk", "itertools", - "lazy_static", "normpath", - "once_cell", - "proc-macro2", - "regex", "serde", "serde_json", - "serde_yml", + "serde_yaml", "siphasher", - "toml 0.7.8", + "toml 0.8.23", "triomphe", ] @@ -3969,20 +3956,16 @@ dependencies = [ ] [[package]] -name = "serde_yml" -version = "0.0.11" +name = "serde_yaml" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", - "libyml", - "log 0.4.32", - "memchr", "ryu", "serde", - "serde_json", - "tempfile", + "unsafe-libyaml", ] [[package]] @@ -4226,7 +4209,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.2", + "toml 0.8.23", "version-compare", ] @@ -4372,26 +4355,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "toml_edit 0.19.15", -] - -[[package]] -name = "toml" -version = "0.8.2" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "toml_edit 0.20.2", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] @@ -4411,9 +4382,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] @@ -4434,9 +4405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.3", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -4445,12 +4414,24 @@ name = "toml_edit" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "winnow 0.5.40", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", ] [[package]] @@ -4474,6 +4455,12 @@ dependencies = [ "winnow 1.0.3", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" version = "1.1.1+spec-1.1.0" @@ -4626,6 +4613,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -5662,6 +5655,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index e29c395..0c555f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "arctracker-sync" -version = "0.1.0" +version = "0.2.0" edition = "2021" publish = false description = "Windows desktop helper that syncs your ARC Raiders inventory to ARCTracker." @@ -28,7 +28,7 @@ getrandom = "0.2" hex = "0.4" pcapsql-core = { path = "vendor/pcapsql-core", default-features = false } rfd = "0.15" -rust-i18n = "3" +rust-i18n = "4" self-replace = "1.5" semver = "1" serde = { version = "1", features = ["derive"] } diff --git a/README.md b/README.md index 799699c..9302c04 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,21 @@ ARCTracker sign-in is kept in Windows Credential Manager. To notice your game account connecting, ARCTracker Sync watches your own computer's network traffic using Windows raw sockets, which Windows only allows -with Administrator rights. There's no kernel driver, bundled network library, or -extra capture tool to install — and it only ever looks at traffic on your own -machine. +with Administrator rights. By default there's no kernel driver, bundled network +library, or extra capture tool to install — and it only ever looks at traffic on +your own machine. + +### Optional: Npcap capture + +Some antivirus products (Avast Web Shield, for example) interfere with +raw-socket capture. If sync won't connect on your machine, Settings → Network +lets you switch the capture method to **Npcap**, a separately installed +packet-capture driver from . ARCTracker Sync never bundles, +downloads, or installs Npcap — its license forbids redistribution — so if you +select Npcap without installing it, the app tells you and links to npcap.com. +With Npcap selected, the app loads Npcap's `wpcap.dll` at runtime and captures +through Npcap's kernel driver instead of raw sockets. It still only looks at +traffic on your own machine, and Administrator is still required. --- @@ -85,7 +97,8 @@ cargo run The app ships a manifest that requests elevation, so it runs as Administrator (required for the raw-socket capture described above). No kernel driver or -external tooling is needed. +external tooling is needed for the default raw-socket capture; the optional +Npcap capture method uses Npcap's driver, installed separately by the user. ## Contributing diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md index ce222a4..71b7481 100644 --- a/THIRD-PARTY-NOTICES.md +++ b/THIRD-PARTY-NOTICES.md @@ -14,6 +14,15 @@ listed below, each of which remains under its own license. TLS decryption and protocol-registration code). See [`vendor/pcapsql-core/LOCAL-MODIFICATIONS.md`](vendor/pcapsql-core/LOCAL-MODIFICATIONS.md). +## Optional runtime dependency (not distributed) + +- **Npcap** — + - An optional packet-capture driver the user can select in Settings as an + alternative to the built-in raw-socket capture. ARCTracker Sync never + bundles, downloads, or installs Npcap (its free license does not permit + redistribution); when selected, the app loads the user's own installed + `wpcap.dll` at runtime. Npcap remains under its own license. + ## Statically linked dependencies The compiled `arctracker-sync.exe` statically links a number of open-source Rust diff --git a/locales/da.json b/locales/da.json index 398fbae..a3566bd 100644 --- a/locales/da.json +++ b/locales/da.json @@ -6,6 +6,17 @@ "SyncApp.progress.signedIn": "Logget ind", "SyncApp.progress.launcherReady": "Launcher klar", "SyncApp.progress.playing": "Spiller", + "SyncApp.phase.account.label": "Konto", + "SyncApp.phase.account.sub": "Forbind ARCTracker", + "SyncApp.phase.launcher.label": "Launcher", + "SyncApp.phase.launcher.sub": "Forbered %{launcher}", + "SyncApp.phase.play.label": "Spil", + "SyncApp.phase.play.sub": "Start ARC Raiders", + "SyncApp.phase.sync.label": "Synkronisering", + "SyncApp.phase.sync.sub": "Hold inventaret opdateret", + "SyncApp.hero.step": "Trin %{n} af 4", + "SyncApp.footer.synced": "Synkroniseret · %{account}", + "SyncApp.footer.syncedLabel": "Synkroniseret ·", "SyncApp.progress.synced": "Synkroniseret", "SyncApp.footer.signedInAs": "Logget ind som %{account}", "SyncApp.footer.notSignedIn": "Ikke logget ind", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker genstarter %{launcher}, så det kan læse dit ARC Raiders-inventar mens du spiller. Dine %{launcher}-spil, downloads og logins røres ikke.", "SyncApp.state.preparingLauncher.title": "Forbereder %{launcher}…", "SyncApp.state.preparingLauncher.body": "Lukker og genstarter %{launcher}. Dette tager et par sekunder.", + "SyncApp.state.preparingLauncher.waiting": "Forbereder %{launcher}…", "SyncApp.state.closeLauncher.title": "Luk %{launcher} for at fortsætte", "SyncApp.state.closeLauncher.body": "%{launcher} skal lukkes, så ARCTracker kan forberede det. Gem alt, der pågår, og luk det derefter.", "SyncApp.state.launcherReady.title": "%{launcher} er klar. Gå og spil.", "SyncApp.state.launcherReady.body": "Start ARC Raiders fra %{launcher} når som helst. Dit inventar synkroniseres automatisk mens du spiller.", "SyncApp.state.connecting.title": "Leder efter din ARC Raiders-konto…", "SyncApp.state.connecting.body": "Hold ARC Raiders åbent. Dette forbindes normalt et par sekunder efter, du når hovedmenuen.", + "SyncApp.state.connecting.waiting": "Venter på ARC Raiders…", "SyncApp.state.updating.title": "Opdaterer ARCTracker…", "SyncApp.state.updating.body": "Næsten færdig. Gemmer din konto.", + "SyncApp.state.updating.waiting": "Gemmer dit inventar…", "SyncApp.state.synced.title": "Du er synkroniseret", "SyncApp.state.synced.body": "Spiller som %{account}. Dit ARC Raiders-inventar er opdateret på ARCTracker.", "SyncApp.state.synced.session": "Dit inventar forbliver synkroniseret indtil %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} blev genstartet på egen hånd, så ARCTracker kan ikke læse dit inventar lige nu. Forbered det igen for at fortsætte synkroniseringen.", "SyncApp.state.needsAttention.title": "Synkronisering kræver opmærksomhed", "SyncApp.state.needsAttention.body": "ARCTracker Sync stødte på et problem med tilslutningen. Åbn fejlfinding for detaljer.", + "SyncApp.state.needsAttention.npcapBody": "Opsamlingsmetoden er sat til Npcap, men Npcap er ikke installeret på denne pc. Installer det fra npcap.com, eller skift tilbage til raw sockets i Indstillinger.", "SyncApp.action.signIn": "Log ind på ARCTracker", "SyncApp.action.cancel": "Annuller", "SyncApp.action.chooseGame": "Vælg ARC Raiders", "SyncApp.action.prepareLauncher": "Forbered %{launcher}", "SyncApp.action.whatDoesThisDo": "Hvad gør dette?", "SyncApp.action.closeLauncher": "Luk %{launcher}", + "SyncApp.action.viewStash": "Se dit inventar", "SyncApp.action.hideToTray": "Skjul i systemstatusfeltet", "SyncApp.action.getHelp": "Få hjælp", "SyncApp.action.tryAgain": "Prøv igen", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Sprog", "SyncApp.settings.displayLanguage": "Visningssprog", "SyncApp.settings.matchesWindows": "Svarer til Windows som standard", + "SyncApp.settings.matchWindows": "Svar til Windows", "SyncApp.settings.network": "Netværk", "SyncApp.settings.networkAdapter": "Netværkskort", "SyncApp.settings.networkAdapterSub": "Auto-valgt. Skift kun hvis synkronisering ikke forbindes.", "SyncApp.settings.refresh": "Opdater", + "SyncApp.settings.captureMethod": "Opsamlingsmetode", + "SyncApp.settings.captureMethodSub": "Sådan læser Sync din pc's egen spiltrafik. Skift til Npcap, hvis antivirussoftware blokerer den indbyggede metode — det kan også fungere bedre, hvis du bruger en VPN eller proxy.", + "SyncApp.settings.captureRawSockets": "Windows raw sockets (indbygget)", + "SyncApp.settings.captureNpcap": "Npcap (installeres separat)", + "SyncApp.settings.npcapMissing": "Npcap er ikke installeret, så synkronisering kan ikke opsamle trafik. Installer det, og brug derefter Prøv igen på hovedskærmen.", + "SyncApp.settings.npcapLink": "Hent Npcap (gratis) på npcap.com", "SyncApp.settings.troubleshooting": "Fejlfinding", "SyncApp.settings.activityLog": "Aktivitetslog", "SyncApp.settings.activityLogSub": "Seneste begivenheder til support", "SyncApp.settings.view": "Vis", + "SyncApp.settings.hide": "Skjul", "SyncApp.settings.copyDiagnostics": "Kopier diagnose", "SyncApp.settings.copyDiagnosticsSub": "Del med ARCTracker support", "SyncApp.settings.copy": "Kopier", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Kontroller for opdateringer", + "SyncApp.settings.whatIsSync": "Hvad gør synkronisering?", + "SyncApp.settings.quit": "Afslut ARCTracker Sync", "SyncApp.tray.open": "Åbn ARCTracker Sync", "SyncApp.tray.pause": "Pause synkronisering", "SyncApp.tray.resume": "Genoptag synkronisering", diff --git a/locales/de.json b/locales/de.json index e306b12..01331ba 100644 --- a/locales/de.json +++ b/locales/de.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Starter bereit", "SyncApp.progress.playing": "Spielt", "SyncApp.progress.synced": "Synchronisiert", + "SyncApp.phase.account.label": "Konto", + "SyncApp.phase.account.sub": "Mit ARCTracker verbinden", + "SyncApp.phase.launcher.label": "Starter", + "SyncApp.phase.launcher.sub": "Bereite %{launcher} vor", + "SyncApp.phase.play.label": "Spielen", + "SyncApp.phase.play.sub": "Starte ARC Raiders", + "SyncApp.phase.sync.label": "Synchronisieren", + "SyncApp.phase.sync.sub": "Halte dein Inventar auf dem neuesten Stand", + "SyncApp.hero.step": "Schritt %{n} von 4", "SyncApp.footer.signedInAs": "Angemeldet als %{account}", + "SyncApp.footer.synced": "Synchronisiert · %{account}", + "SyncApp.footer.syncedLabel": "Synchronisiert ·", "SyncApp.footer.notSignedIn": "Nicht angemeldet", "SyncApp.footer.settings": "Einstellungen", "SyncApp.state.needsAdmin.title": "Zunächst eine kurze Berechtigung", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker startet %{launcher} neu, damit es dein ARC Raiders-Inventar während des Spiels lesen kann. Deine %{launcher}-Spiele, Downloads und Anmeldungen werden nicht beeinträchtigt.", "SyncApp.state.preparingLauncher.title": "%{launcher} wird vorbereitet…", "SyncApp.state.preparingLauncher.body": "%{launcher} wird geschlossen und neu geöffnet. Das dauert nur wenige Sekunden.", + "SyncApp.state.preparingLauncher.waiting": "Bereite %{launcher} vor…", "SyncApp.state.closeLauncher.title": "Schließe %{launcher}, um fortzufahren", "SyncApp.state.closeLauncher.body": "%{launcher} muss geschlossen werden, damit ARCTracker es vorbereiten kann. Speichere alles, woran du arbeitest, und schließe es dann.", "SyncApp.state.launcherReady.title": "%{launcher} ist bereit. Auf zum Spielen.", "SyncApp.state.launcherReady.body": "Starte ARC Raiders über %{launcher}, wann immer du möchtest. Dein Inventar wird während des Spiels automatisch synchronisiert.", "SyncApp.state.connecting.title": "Suche nach deinem ARC Raiders-Konto…", "SyncApp.state.connecting.body": "Halte ARC Raiders offen. Dies verbindet sich normalerweise wenige Sekunden nach dem Erreichen des Hauptmenüs.", + "SyncApp.state.connecting.waiting": "Warte auf ARC Raiders…", "SyncApp.state.updating.title": "ARCTracker wird aktualisiert…", "SyncApp.state.updating.body": "Fast geschafft. Dein Konto wird gespeichert.", + "SyncApp.state.updating.waiting": "Speichere dein Inventar…", "SyncApp.state.synced.title": "Du bist synchronisiert", "SyncApp.state.synced.body": "Spielt als %{account}. Dein ARC Raiders-Inventar ist auf ARCTracker aktuell.", "SyncApp.state.synced.session": "Dein Inventar bleibt bis %{time} synchronisiert.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} wurde von selbst neu gestartet, daher kann ARCTracker dein Inventar momentan nicht lesen. Bereite es erneut vor, um die Synchronisierung weiterhin zu gewährleisten.", "SyncApp.state.needsAttention.title": "Synchronisierung benötigt Aufmerksamkeit", "SyncApp.state.needsAttention.body": "ARCTracker Sync hatte ein Problem beim Verbinden. Öffne die Fehlerbehebung für weitere Details.", + "SyncApp.state.needsAttention.npcapBody": "Die Erfassungsmethode ist auf Npcap eingestellt, aber Npcap ist auf diesem PC nicht installiert. Installiere es von npcap.com oder wechsle in den Einstellungen zurück zu Raw Sockets.", "SyncApp.action.signIn": "Bei ARCTracker anmelden", "SyncApp.action.cancel": "Abbrechen", "SyncApp.action.chooseGame": "ARC Raiders wählen", "SyncApp.action.prepareLauncher": "%{launcher} vorbereiten", "SyncApp.action.whatDoesThisDo": "Was macht das?", "SyncApp.action.closeLauncher": "%{launcher} schließen", + "SyncApp.action.viewStash": "Zeige dein Inventar", "SyncApp.action.hideToTray": "In den Hintergrund minimieren", "SyncApp.action.getHelp": "Hilfe erhalten", "SyncApp.action.tryAgain": "Nochmal versuchen", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Sprache", "SyncApp.settings.displayLanguage": "Anzeigesprache", "SyncApp.settings.matchesWindows": "Entspricht standardmäßig Windows", + "SyncApp.settings.matchWindows": "Windows abgleichen", "SyncApp.settings.network": "Netzwerk", "SyncApp.settings.networkAdapter": "Netzwerkadapter", "SyncApp.settings.networkAdapterSub": "Automatisch ausgewählt. Nur ändern, wenn Sync sich nicht verbindet.", "SyncApp.settings.refresh": "Aktualisieren", + "SyncApp.settings.captureMethod": "Erfassungsmethode", + "SyncApp.settings.captureMethodSub": "Wie Sync den eigenen Spielverkehr deines PCs liest. Wechsle zu Npcap, wenn Antivirensoftware die integrierte Methode blockiert — es kann auch besser funktionieren, wenn du ein VPN oder einen Proxy verwendest.", + "SyncApp.settings.captureRawSockets": "Windows Raw Sockets (integriert)", + "SyncApp.settings.captureNpcap": "Npcap (separat installiert)", + "SyncApp.settings.npcapMissing": "Npcap ist nicht installiert, daher kann die Synchronisierung keinen Datenverkehr erfassen. Installiere es und nutze dann „Nochmal versuchen“ auf dem Hauptbildschirm.", + "SyncApp.settings.npcapLink": "Npcap (kostenlos) auf npcap.com herunterladen", "SyncApp.settings.troubleshooting": "Fehlerbehebung", "SyncApp.settings.activityLog": "Aktivitätsprotokoll", "SyncApp.settings.activityLogSub": "Aktuelle Ereignisse für den Support", "SyncApp.settings.view": "Anzeigen", + "SyncApp.settings.hide": "Ausblenden", "SyncApp.settings.copyDiagnostics": "Diagnose kopieren", "SyncApp.settings.copyDiagnosticsSub": "Mit dem ARCTracker-Support teilen", "SyncApp.settings.copy": "Kopieren", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Nach Updates suchen", + "SyncApp.settings.whatIsSync": "Was macht Sync?", + "SyncApp.settings.quit": "ARCTracker Sync beenden", "SyncApp.tray.open": "ARCTracker Sync öffnen", "SyncApp.tray.pause": "Synchronisierung anhalten", "SyncApp.tray.resume": "Synchronisierung fortsetzen", diff --git a/locales/en.json b/locales/en.json index 784f60c..9bf5afd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -53,6 +53,7 @@ "SyncApp.state.needsLauncher.body": "%{launcher} was restarted on its own, so ARCTracker can't read your inventory right now. Prepare it again to keep syncing.", "SyncApp.state.needsAttention.title": "Sync needs attention", "SyncApp.state.needsAttention.body": "ARCTracker Sync hit a problem connecting. Open troubleshooting for details.", + "SyncApp.state.needsAttention.npcapBody": "The capture method is set to Npcap, but Npcap isn't installed on this PC. Install it from npcap.com, or switch back to raw sockets in Settings.", "SyncApp.action.signIn": "Sign in to ARCTracker", "SyncApp.action.cancel": "Cancel", "SyncApp.action.chooseGame": "Choose ARC Raiders", @@ -87,6 +88,12 @@ "SyncApp.settings.networkAdapter": "Network adapter", "SyncApp.settings.networkAdapterSub": "Auto-selected. Only change it if sync won't connect.", "SyncApp.settings.refresh": "Refresh", + "SyncApp.settings.captureMethod": "Capture method", + "SyncApp.settings.captureMethodSub": "How Sync reads your PC's own game traffic. Switch to Npcap if antivirus software blocks the built-in method — it may also work better if you use a VPN or proxy.", + "SyncApp.settings.captureRawSockets": "Windows raw sockets (built in)", + "SyncApp.settings.captureNpcap": "Npcap (installed separately)", + "SyncApp.settings.npcapMissing": "Npcap isn't installed, so sync can't capture traffic. Install it, then use Try again on the main screen.", + "SyncApp.settings.npcapLink": "Get Npcap (free) at npcap.com", "SyncApp.settings.troubleshooting": "Troubleshooting", "SyncApp.settings.activityLog": "Activity log", "SyncApp.settings.activityLogSub": "Recent events, for support", diff --git a/locales/es.json b/locales/es.json index d4f579d..2a6b837 100644 --- a/locales/es.json +++ b/locales/es.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Lanzador listo", "SyncApp.progress.playing": "Jugando", "SyncApp.progress.synced": "Sincronizado", + "SyncApp.phase.account.label": "Cuenta", + "SyncApp.phase.account.sub": "Conectar ARCTracker", + "SyncApp.phase.launcher.label": "Lanzador", + "SyncApp.phase.launcher.sub": "Prepara %{launcher}", + "SyncApp.phase.play.label": "Jugar", + "SyncApp.phase.play.sub": "Lanza ARC Raiders", + "SyncApp.phase.sync.label": "Sincronizar", + "SyncApp.phase.sync.sub": "Mantén tu inventario actualizado", + "SyncApp.hero.step": "Paso %{n} de 4", "SyncApp.footer.signedInAs": "Sesión iniciada como %{account}", + "SyncApp.footer.synced": "Sincronizado · %{account}", + "SyncApp.footer.syncedLabel": "Sincronizado ·", "SyncApp.footer.notSignedIn": "Sesión no iniciada", "SyncApp.footer.settings": "Configuración", "SyncApp.state.needsAdmin.title": "Un permiso rápido primero", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker reinicia %{launcher} para poder leer tu inventario de ARC Raiders mientras juegas. Tus juegos, descargas e inicios de sesión de %{launcher} no se ven afectados.", "SyncApp.state.preparingLauncher.title": "Preparando %{launcher}…", "SyncApp.state.preparingLauncher.body": "Cerrando y reabriendo %{launcher}. Esto tarda unos segundos.", + "SyncApp.state.preparingLauncher.waiting": "Preparando %{launcher}…", "SyncApp.state.closeLauncher.title": "Cierra %{launcher} para continuar", "SyncApp.state.closeLauncher.body": "%{launcher} debe cerrarse para que ARCTracker lo pueda preparar. Guarda cualquier progreso en marcha y luego ciérralo.", "SyncApp.state.launcherReady.title": "%{launcher} está listo. Ve a jugar.", "SyncApp.state.launcherReady.body": "Inicia ARC Raiders desde %{launcher} cuando quieras. Tu inventario se sincroniza automáticamente mientras juegas.", "SyncApp.state.connecting.title": "Buscando tu cuenta de ARC Raiders…", "SyncApp.state.connecting.body": "Mantén ARC Raiders abierto. Normalmente se conecta unos segundos después de que llegues al menú principal.", + "SyncApp.state.connecting.waiting": "Esperando ARC Raiders…", "SyncApp.state.updating.title": "Actualizando ARCTracker…", "SyncApp.state.updating.body": "Ya casi. Guardando tu cuenta.", + "SyncApp.state.updating.waiting": "Guardando tu inventario…", "SyncApp.state.synced.title": "Estás sincronizado", "SyncApp.state.synced.body": "Jugando como %{account}. Tu inventario de ARC Raiders está actualizado en ARCTracker.", "SyncApp.state.synced.session": "Tu inventario permanece sincronizado hasta las %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} se reinició por sí solo, así que ARCTracker no puede leer tu inventario ahora. Prepáralo de nuevo para seguir sincronizando.", "SyncApp.state.needsAttention.title": "La sincronización necesita atención", "SyncApp.state.needsAttention.body": "ARCTracker Sync tuvo un problema al conectar. Abre la solución de problemas para obtener más detalles.", + "SyncApp.state.needsAttention.npcapBody": "El método de captura está configurado en Npcap, pero Npcap no está instalado en este PC. Instálalo desde npcap.com o vuelve a los sockets sin procesar en Configuración.", "SyncApp.action.signIn": "Iniciar sesión en ARCTracker", "SyncApp.action.cancel": "Cancelar", "SyncApp.action.chooseGame": "Elige ARC Raiders", "SyncApp.action.prepareLauncher": "Prepara %{launcher}", "SyncApp.action.whatDoesThisDo": "¿Qué hace esto?", "SyncApp.action.closeLauncher": "Cierra %{launcher}", + "SyncApp.action.viewStash": "Ver tu inventario", "SyncApp.action.hideToTray": "Ocultar en la bandeja", "SyncApp.action.getHelp": "Obtener ayuda", "SyncApp.action.tryAgain": "Intentar de nuevo", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Idioma", "SyncApp.settings.displayLanguage": "Idioma de visualización", "SyncApp.settings.matchesWindows": "Coincide con Windows de forma predeterminada", + "SyncApp.settings.matchWindows": "Coincidir con Windows", "SyncApp.settings.network": "Red", "SyncApp.settings.networkAdapter": "Adaptador de red", "SyncApp.settings.networkAdapterSub": "Seleccionado automáticamente. Solo cambia si la sincronización no se conecta.", "SyncApp.settings.refresh": "Actualizar", + "SyncApp.settings.captureMethod": "Método de captura", + "SyncApp.settings.captureMethodSub": "Cómo Sync lee el tráfico de juego de tu propio PC. Cambia a Npcap si un antivirus bloquea el método integrado — también puede funcionar mejor si utilizas un VPN o proxy.", + "SyncApp.settings.captureRawSockets": "Sockets sin procesar de Windows (integrado)", + "SyncApp.settings.captureNpcap": "Npcap (se instala por separado)", + "SyncApp.settings.npcapMissing": "Npcap no está instalado, así que la sincronización no puede capturar el tráfico. Instálalo y luego usa Intentar de nuevo en la pantalla principal.", + "SyncApp.settings.npcapLink": "Consigue Npcap (gratis) en npcap.com", "SyncApp.settings.troubleshooting": "Solución de problemas", "SyncApp.settings.activityLog": "Registro de actividad", "SyncApp.settings.activityLogSub": "Eventos recientes, para soporte", "SyncApp.settings.view": "Ver", + "SyncApp.settings.hide": "Ocultar", "SyncApp.settings.copyDiagnostics": "Copiar diagnósticos", "SyncApp.settings.copyDiagnosticsSub": "Compartir con el soporte de ARCTracker", "SyncApp.settings.copy": "Copiar", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Buscar actualizaciones", + "SyncApp.settings.whatIsSync": "¿Qué hace Sync?", + "SyncApp.settings.quit": "Salir de ARCTracker Sync", "SyncApp.tray.open": "Abrir ARCTracker Sync", "SyncApp.tray.pause": "Pausar sincronización", "SyncApp.tray.resume": "Reanudar sincronización", diff --git a/locales/fr.json b/locales/fr.json index 8b334e7..ed95e2d 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Lanceur prêt", "SyncApp.progress.playing": "En jeu", "SyncApp.progress.synced": "Synchronisé", + "SyncApp.phase.account.label": "Compte", + "SyncApp.phase.account.sub": "Connectez ARCTracker", + "SyncApp.phase.launcher.label": "Lanceur", + "SyncApp.phase.launcher.sub": "Préparer %{launcher}", + "SyncApp.phase.play.label": "Jouer", + "SyncApp.phase.play.sub": "Lancez ARC Raiders", + "SyncApp.phase.sync.label": "Synchronisation", + "SyncApp.phase.sync.sub": "Gardez votre inventaire à jour", + "SyncApp.hero.step": "Étape %{n} sur 4", "SyncApp.footer.signedInAs": "Connecté en tant que %{account}", + "SyncApp.footer.synced": "Synchronisé · %{account}", + "SyncApp.footer.syncedLabel": "Synchronisé ·", "SyncApp.footer.notSignedIn": "Non connecté", "SyncApp.footer.settings": "Paramètres", "SyncApp.state.needsAdmin.title": "Une permission rapide d'abord", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker redémarre %{launcher} pour pouvoir lire votre inventaire ARC Raiders pendant que vous jouez. Vos jeux %{launcher}, téléchargements et identifiants ne sont pas affectés.", "SyncApp.state.preparingLauncher.title": "Préparation de %{launcher}…", "SyncApp.state.preparingLauncher.body": "Fermeture et réouverture de %{launcher}. Cela prend quelques secondes.", + "SyncApp.state.preparingLauncher.waiting": "Préparation de %{launcher}…", "SyncApp.state.closeLauncher.title": "Fermez %{launcher} pour continuer", "SyncApp.state.closeLauncher.body": "%{launcher} doit se fermer pour qu'ARCTracker puisse le préparer. Sauvegardez vos progrès, puis fermez-le.", "SyncApp.state.launcherReady.title": "%{launcher} est prêt. Allez jouer.", "SyncApp.state.launcherReady.body": "Lancez ARC Raiders à partir de %{launcher} quand vous le souhaitez. Votre inventaire se synchronise automatiquement pendant que vous jouez.", "SyncApp.state.connecting.title": "Recherche de votre compte ARC Raiders…", "SyncApp.state.connecting.body": "Gardez ARC Raiders ouvert. Cela se connecte généralement quelques secondes après que vous atteignez le menu principal.", + "SyncApp.state.connecting.waiting": "Attente d'ARC Raiders…", "SyncApp.state.updating.title": "Mise à jour d'ARCTracker…", "SyncApp.state.updating.body": "Presque terminé. Enregistrement de votre compte.", + "SyncApp.state.updating.waiting": "Enregistrement de votre inventaire…", "SyncApp.state.synced.title": "Vous êtes synchronisé", "SyncApp.state.synced.body": "Vous jouez en tant que %{account}. Votre inventaire ARC Raiders est à jour sur ARCTracker.", "SyncApp.state.synced.session": "Votre inventaire reste synchronisé jusqu'à %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} a été redémarré de lui-même, donc ARCTracker ne peut pas lire votre inventaire maintenant. Préparez-le à nouveau pour continuer la synchronisation.", "SyncApp.state.needsAttention.title": "La synchronisation nécessite une attention", "SyncApp.state.needsAttention.body": "ARCTracker Sync a rencontré un problème de connexion. Ouvrez le dépannage pour plus de détails.", + "SyncApp.state.needsAttention.npcapBody": "La méthode de capture est définie sur Npcap, mais Npcap n'est pas installé sur ce PC. Installez-le depuis npcap.com, ou revenez aux sockets bruts dans les Paramètres.", "SyncApp.action.signIn": "Connectez-vous à ARCTracker", "SyncApp.action.cancel": "Annuler", "SyncApp.action.chooseGame": "Sélectionnez ARC Raiders", "SyncApp.action.prepareLauncher": "Préparer %{launcher}", "SyncApp.action.whatDoesThisDo": "À quoi ça sert ?", "SyncApp.action.closeLauncher": "Fermer %{launcher}", + "SyncApp.action.viewStash": "Afficher votre inventaire", "SyncApp.action.hideToTray": "Masquer dans la barre d'état système", "SyncApp.action.getHelp": "Obtenir de l'aide", "SyncApp.action.tryAgain": "Réessayer", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Langue", "SyncApp.settings.displayLanguage": "Langue d'affichage", "SyncApp.settings.matchesWindows": "Correspond à Windows par défaut", + "SyncApp.settings.matchWindows": "Correspondre à Windows", "SyncApp.settings.network": "Réseau", "SyncApp.settings.networkAdapter": "Adaptateur réseau", "SyncApp.settings.networkAdapterSub": "Sélectionné automatiquement. Ne changez que si la synchronisation ne se connecte pas.", "SyncApp.settings.refresh": "Actualiser", + "SyncApp.settings.captureMethod": "Méthode de capture", + "SyncApp.settings.captureMethodSub": "Comment Sync lit le trafic de jeu de votre propre PC. Passez à Npcap si un antivirus bloque la méthode intégrée — cela peut également mieux fonctionner si vous utilisez un VPN ou un proxy.", + "SyncApp.settings.captureRawSockets": "Sockets bruts Windows (intégré)", + "SyncApp.settings.captureNpcap": "Npcap (installé séparément)", + "SyncApp.settings.npcapMissing": "Npcap n'est pas installé, donc la synchronisation ne peut pas capturer le trafic. Installez-le, puis utilisez « Réessayer » sur l'écran principal.", + "SyncApp.settings.npcapLink": "Obtenez Npcap (gratuit) sur npcap.com", "SyncApp.settings.troubleshooting": "Dépannage", "SyncApp.settings.activityLog": "Journal d'activité", "SyncApp.settings.activityLogSub": "Événements récents, pour le support", "SyncApp.settings.view": "Afficher", + "SyncApp.settings.hide": "Masquer", "SyncApp.settings.copyDiagnostics": "Copier les diagnostics", "SyncApp.settings.copyDiagnosticsSub": "Partagez avec le support ARCTracker", "SyncApp.settings.copy": "Copier", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Vérifier les mises à jour", + "SyncApp.settings.whatIsSync": "À quoi sert Sync ?", + "SyncApp.settings.quit": "Quitter ARCTracker Sync", "SyncApp.tray.open": "Ouvrir ARCTracker Sync", "SyncApp.tray.pause": "Mettre en pause la synchronisation", "SyncApp.tray.resume": "Reprendre la synchronisation", diff --git a/locales/he.json b/locales/he.json index 5ee578b..e3e65f3 100644 --- a/locales/he.json +++ b/locales/he.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "משקיף מוכן", "SyncApp.progress.playing": "משחק", "SyncApp.progress.synced": "סונכרן", + "SyncApp.phase.account.label": "חשבון", + "SyncApp.phase.account.sub": "חבר ARCTracker", + "SyncApp.phase.launcher.label": "משקיף", + "SyncApp.phase.launcher.sub": "הכן %{launcher}", + "SyncApp.phase.play.label": "משחק", + "SyncApp.phase.play.sub": "הפעל ARC Raiders", + "SyncApp.phase.sync.label": "סנכרון", + "SyncApp.phase.sync.sub": "שמור מלאי עדכני", + "SyncApp.hero.step": "שלב %{n} מתוך 4", "SyncApp.footer.signedInAs": "מחובר כ-%{account}", + "SyncApp.footer.synced": "סונכרן · %{account}", + "SyncApp.footer.syncedLabel": "סונכרן ·", "SyncApp.footer.notSignedIn": "לא מחובר", "SyncApp.footer.settings": "הגדרות", "SyncApp.state.needsAdmin.title": "רשות אחת מהירה בבקשה", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker יפעיל מחדש את %{launcher} כך שיוכל לקרוא את המלאי ARC Raiders שלכם בזמן משחק. משחקי %{launcher}, הורדות והתחברויות שלכם לא יושפעו.", "SyncApp.state.preparingLauncher.title": "הכנת %{launcher}…", "SyncApp.state.preparingLauncher.body": "סגירה ופתיחה מחדש של %{launcher}. זה לוקח כמה שניות.", + "SyncApp.state.preparingLauncher.waiting": "הכנת %{launcher}…", "SyncApp.state.closeLauncher.title": "סגרו את %{launcher} כדי להמשיך", "SyncApp.state.closeLauncher.body": "%{launcher} צריך להיסגר כדי שARCTracker יוכל להכין אותו. שמרו כל דבר בעיצומו, ואז סגרו אותו.", "SyncApp.state.launcherReady.title": "%{launcher} מוכן. בואו תשחקו.", "SyncApp.state.launcherReady.body": "התחילו את ARC Raiders מ-%{launcher} בכל זמן שתרצו. המלאי שלכם יסונכרן באופן אוטומטי בזמן משחק.", "SyncApp.state.connecting.title": "חיפוש חשבון ARC Raiders שלכם…", "SyncApp.state.connecting.body": "שמרו את ARC Raiders פתוח. בדרך כלל זה מתחבר כמה שניות אחרי שתגיעו לתפריט הראשי.", + "SyncApp.state.connecting.waiting": "ממתינים ל-ARC Raiders…", "SyncApp.state.updating.title": "עדכון ARCTracker…", "SyncApp.state.updating.body": "כמעט סיימנו. שמירת החשבון שלכם.", + "SyncApp.state.updating.waiting": "שמירת המלאי שלכם…", "SyncApp.state.synced.title": "אתם סונכרנים", "SyncApp.state.synced.body": "משחק כ-%{account}. המלאי של ARC Raiders שלכם עדכני ב-ARCTracker.", "SyncApp.state.synced.session": "המלאי שלכם יישאר סונכרן עד %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} הופעל מחדש מעצמו, כך שARCTracker לא יכול לקרוא את המלאי שלכם כעת. הכינו אותו שוב כדי להמשיך בסנכרון.", "SyncApp.state.needsAttention.title": "סנכרון זקוק לתשומת לב", "SyncApp.state.needsAttention.body": "ARCTracker Sync נתקל בבעיה בהתחברות. פתחו פתרון בעיות לפרטים נוספים.", + "SyncApp.state.needsAttention.npcapBody": "שיטת הלכידה מוגדרת ל-Npcap, אבל Npcap לא מותקן במחשב הזה. התקינו אותו מ-npcap.com, או חזרו ל-raw sockets בהגדרות.", "SyncApp.action.signIn": "היכנסו ל-ARCTracker", "SyncApp.action.cancel": "ביטול", "SyncApp.action.chooseGame": "בחרו ARC Raiders", "SyncApp.action.prepareLauncher": "הכנת %{launcher}", "SyncApp.action.whatDoesThisDo": "מה זה עושה?", "SyncApp.action.closeLauncher": "סגור את %{launcher}", + "SyncApp.action.viewStash": "צפה במלאי שלך", "SyncApp.action.hideToTray": "הסתר לשרייה", "SyncApp.action.getHelp": "קבל עזרה", "SyncApp.action.tryAgain": "נסה שוב", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "שפה", "SyncApp.settings.displayLanguage": "שפת תצוגה", "SyncApp.settings.matchesWindows": "תואם ל-Windows כברירת מחדל", + "SyncApp.settings.matchWindows": "תאם ל-Windows", "SyncApp.settings.network": "רשת", "SyncApp.settings.networkAdapter": "מתאם רשת", "SyncApp.settings.networkAdapterSub": "נבחר באופן אוטומטי. שנה רק אם סנכרון לא מתחבר.", "SyncApp.settings.refresh": "רענן", + "SyncApp.settings.captureMethod": "שיטת לכידה", + "SyncApp.settings.captureMethodSub": "כיצד Sync קורא את תעבורת המשחק של המחשב שלך. עבור ל-Npcap אם תוכנת אנטי-וירוס חוסמת את השיטה המובנית — זה גם עשוי לעבוד טוב יותר אם אתה משתמש ב-VPN או פרוקסי.", + "SyncApp.settings.captureRawSockets": "Windows raw sockets (מובנה)", + "SyncApp.settings.captureNpcap": "Npcap (מותקן בנפרד)", + "SyncApp.settings.npcapMissing": "Npcap לא מותקן, ולכן הסנכרון לא יכול ללכוד תעבורה. התקן אותו, ואז השתמש ב\"נסה שוב\" במסך הראשי.", + "SyncApp.settings.npcapLink": "קבל את Npcap (בחינם) ב-npcap.com", "SyncApp.settings.troubleshooting": "פתרון בעיות", "SyncApp.settings.activityLog": "רישום פעילות", "SyncApp.settings.activityLogSub": "אירועים עדכניים, לתמיכה", "SyncApp.settings.view": "צפה", + "SyncApp.settings.hide": "הסתר", "SyncApp.settings.copyDiagnostics": "העתק אבחון", "SyncApp.settings.copyDiagnosticsSub": "שתף עם תמיכת ARCTracker", "SyncApp.settings.copy": "העתק", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "חפש עדכונים", + "SyncApp.settings.whatIsSync": "מה עושה סנכרון?", + "SyncApp.settings.quit": "צא מ-ARCTracker Sync", "SyncApp.tray.open": "פתח ARCTracker Sync", "SyncApp.tray.pause": "השהה סנכרון", "SyncApp.tray.resume": "חזור סנכרון", diff --git a/locales/hr.json b/locales/hr.json index 9f94540..9170c1b 100644 --- a/locales/hr.json +++ b/locales/hr.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Pokretač je spreman", "SyncApp.progress.playing": "Igraš", "SyncApp.progress.synced": "Sinhroniziran", + "SyncApp.phase.account.label": "Račun", + "SyncApp.phase.account.sub": "Povežite ARCTracker", + "SyncApp.phase.launcher.label": "Pokretač", + "SyncApp.phase.launcher.sub": "Pripremite %{launcher}", + "SyncApp.phase.play.label": "Igraj", + "SyncApp.phase.play.sub": "Pokrenite ARC Raiders", + "SyncApp.phase.sync.label": "Sinhronizacija", + "SyncApp.phase.sync.sub": "Održavajte inventar ažurnim", + "SyncApp.hero.step": "Korak %{n} od 4", "SyncApp.footer.signedInAs": "Prijavljen kao %{account}", + "SyncApp.footer.synced": "Sinhroniziran · %{account}", + "SyncApp.footer.syncedLabel": "Sinhroniziran ·", "SyncApp.footer.notSignedIn": "Niste prijavljeni", "SyncApp.footer.settings": "Postavke", "SyncApp.state.needsAdmin.title": "Prvo je potrebna dozvola", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker restartuje %{launcher} kako bi mogao čitati vaš ARC Raiders inventar tijekom igranja. Vaši %{launcher} igre, preuzimanja i prijave nisu dodirnuti.", "SyncApp.state.preparingLauncher.title": "Priprema %{launcher}…", "SyncApp.state.preparingLauncher.body": "Zatvaranje i ponovno otvaranje %{launcher}. Ovo traje nekoliko sekundi.", + "SyncApp.state.preparingLauncher.waiting": "Priprema %{launcher}…", "SyncApp.state.closeLauncher.title": "Zatvorite %{launcher} da biste nastavili", "SyncApp.state.closeLauncher.body": "%{launcher} se mora zatvoriti kako bi ARCTracker mogao to pripremiti. Spremi sve što je u tijeku, zatim ga zatvorite.", "SyncApp.state.launcherReady.title": "%{launcher} je spreman. Hajde da igraš.", "SyncApp.state.launcherReady.body": "Pokrenite ARC Raiders iz %{launcher} kada god želite. Vaš inventar se automatski sinhronizira tijekom igranja.", "SyncApp.state.connecting.title": "Tražim vaš ARC Raiders račun…", "SyncApp.state.connecting.body": "Održavajte ARC Raiders otvorenim. Obično se povezuje nekoliko sekundi nakon što dosegnete glavnib meni.", + "SyncApp.state.connecting.waiting": "Čekam ARC Raiders…", "SyncApp.state.updating.title": "Ažuriranje ARCTracker…", "SyncApp.state.updating.body": "Gotovo je. Spremam vaš račun.", + "SyncApp.state.updating.waiting": "Čuvanje vašeg inventara…", "SyncApp.state.synced.title": "Sinhroniziran ste", "SyncApp.state.synced.body": "Igrate kao %{account}. Vaš ARC Raiders inventar je ažuran na ARCTracker-u.", "SyncApp.state.synced.session": "Vaš inventar ostaje sinhroniziran do %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} je samostalno restartovan, pa ARCTracker sada ne može čitati vaš inventar. Pripremite ga ponovno da biste nastavili sinhronizaciju.", "SyncApp.state.needsAttention.title": "Sinhronizacija trebanja pažnje", "SyncApp.state.needsAttention.body": "ARCTracker Sync je naišao na problem pri povezivanju. Otvorite rješavanje problema za detalje.", + "SyncApp.state.needsAttention.npcapBody": "Metoda hvatanja postavljena je na Npcap, ali Npcap nije instaliran na ovom PC-u. Instalirajte ga s npcap.com ili se vratite na sirove sockete u Postavkama.", "SyncApp.action.signIn": "Prijavite se na ARCTracker", "SyncApp.action.cancel": "Otkaži", "SyncApp.action.chooseGame": "Odaberite ARC Raiders", "SyncApp.action.prepareLauncher": "Pripremite %{launcher}", "SyncApp.action.whatDoesThisDo": "Što to radi?", "SyncApp.action.closeLauncher": "Zatvorite %{launcher}", + "SyncApp.action.viewStash": "Pogledajte svoj inventar", "SyncApp.action.hideToTray": "Sakrij u trac", "SyncApp.action.getHelp": "Potražite pomoć", "SyncApp.action.tryAgain": "Pokušajte ponovno", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Jezik", "SyncApp.settings.displayLanguage": "Jezik prikaza", "SyncApp.settings.matchesWindows": "Poklapa se sa Windows-om prema zadanom", + "SyncApp.settings.matchWindows": "Uskladi sa Windows-om", "SyncApp.settings.network": "Mreža", "SyncApp.settings.networkAdapter": "Mrežni adapter", "SyncApp.settings.networkAdapterSub": "Automatski odabran. Mijenjajte samo ako sinhronizacija neće biti povezana.", "SyncApp.settings.refresh": "Osvježi", + "SyncApp.settings.captureMethod": "Metoda hvatanja", + "SyncApp.settings.captureMethodSub": "Kako Sync čita vlastiti promet igre vašeg PC-a. Prebacite se na Npcap ako antivirusni softver blokira ugrađenu metodu — može i bolje raditi ako koristite VPN ili proxy.", + "SyncApp.settings.captureRawSockets": "Windows sirovi socketi (ugrađeno)", + "SyncApp.settings.captureNpcap": "Npcap (instalira se zasebno)", + "SyncApp.settings.npcapMissing": "Npcap nije instaliran, pa sinhronizacija ne može hvatati promet. Instalirajte ga, a zatim upotrijebite Pokušajte ponovno na glavnom zaslonu.", + "SyncApp.settings.npcapLink": "Preuzmite Npcap (besplatno) na npcap.com", "SyncApp.settings.troubleshooting": "Rješavanje problema", "SyncApp.settings.activityLog": "Dnevnik aktivnosti", "SyncApp.settings.activityLogSub": "Nedavni događaji za podršku", "SyncApp.settings.view": "Prikazi", + "SyncApp.settings.hide": "Sakrij", "SyncApp.settings.copyDiagnostics": "Kopirajte dijagnostiku", "SyncApp.settings.copyDiagnosticsSub": "Podijelite sa ARCTracker podrškom", "SyncApp.settings.copy": "Kopiraj", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Provjeri ažuriranja", + "SyncApp.settings.whatIsSync": "Što Sync radi?", + "SyncApp.settings.quit": "Izlaz iz ARCTracker Sync", "SyncApp.tray.open": "Otvorite ARCTracker Sync", "SyncApp.tray.pause": "Pauzira sinhronizaciju", "SyncApp.tray.resume": "Nastavi sinhronizaciju", diff --git a/locales/it.json b/locales/it.json index 1ba516e..0b6aad3 100644 --- a/locales/it.json +++ b/locales/it.json @@ -6,6 +6,17 @@ "SyncApp.progress.signedIn": "Acceduto", "SyncApp.progress.launcherReady": "Launcher pronto", "SyncApp.progress.playing": "In gioco", + "SyncApp.phase.account.label": "Account", + "SyncApp.phase.account.sub": "Connetti ARCTracker", + "SyncApp.phase.launcher.label": "Launcher", + "SyncApp.phase.launcher.sub": "Prepara %{launcher}", + "SyncApp.phase.play.label": "Gioca", + "SyncApp.phase.play.sub": "Avvia ARC Raiders", + "SyncApp.phase.sync.label": "Sincronizzazione", + "SyncApp.phase.sync.sub": "Mantieni l'inventario aggiornato", + "SyncApp.hero.step": "Passaggio %{n} di 4", + "SyncApp.footer.synced": "Sincronizzato · %{account}", + "SyncApp.footer.syncedLabel": "Sincronizzato ·", "SyncApp.progress.synced": "Sincronizzato", "SyncApp.footer.signedInAs": "Acceduto come %{account}", "SyncApp.footer.notSignedIn": "Non acceduto", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker riavvia %{launcher} per leggere il tuo inventario di ARC Raiders mentre giochi. I tuoi giochi, download e accessi su %{launcher} non vengono toccati.", "SyncApp.state.preparingLauncher.title": "Preparazione di %{launcher}…", "SyncApp.state.preparingLauncher.body": "Chiusura e riapertura di %{launcher}. Questo richiede pochi secondi.", + "SyncApp.state.preparingLauncher.waiting": "Preparazione di %{launcher}…", "SyncApp.state.closeLauncher.title": "Chiudi %{launcher} per continuare", "SyncApp.state.closeLauncher.body": "%{launcher} deve chiudersi in modo che ARCTracker possa prepararlo. Salva tutto quello che hai in corso, quindi chiudilo.", "SyncApp.state.launcherReady.title": "%{launcher} è pronto. Vai a giocare.", "SyncApp.state.launcherReady.body": "Avvia ARC Raiders da %{launcher} quando vuoi. L'inventario si sincronizza automaticamente mentre giochi.", "SyncApp.state.connecting.title": "Ricerca del tuo account ARC Raiders…", "SyncApp.state.connecting.body": "Mantieni ARC Raiders aperto. Di solito si connette pochi secondi dopo aver raggiunto il menu principale.", + "SyncApp.state.connecting.waiting": "In attesa di ARC Raiders…", "SyncApp.state.updating.title": "Aggiornamento di ARCTracker…", "SyncApp.state.updating.body": "Quasi fatto. Salvataggio del tuo account.", + "SyncApp.state.updating.waiting": "Salvataggio del tuo inventario…", "SyncApp.state.synced.title": "Sei sincronizzato", "SyncApp.state.synced.body": "Giocando come %{account}. L'inventario di ARC Raiders è aggiornato su ARCTracker.", "SyncApp.state.synced.session": "L'inventario rimane sincronizzato fino a %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} è stato riavviato autonomamente, quindi ARCTracker non riesce a leggere l'inventario in questo momento. Preparalo di nuovo per continuare la sincronizzazione.", "SyncApp.state.needsAttention.title": "La sincronizzazione necessita attenzione", "SyncApp.state.needsAttention.body": "ARCTracker Sync ha riscontrato un problema durante la connessione. Apri la risoluzione dei problemi per i dettagli.", + "SyncApp.state.needsAttention.npcapBody": "Il metodo di acquisizione è impostato su Npcap, ma Npcap non è installato su questo PC. Installalo da npcap.com, oppure torna ai socket raw nelle Impostazioni.", "SyncApp.action.signIn": "Accedi a ARCTracker", "SyncApp.action.cancel": "Annulla", "SyncApp.action.chooseGame": "Scegli ARC Raiders", "SyncApp.action.prepareLauncher": "Prepara %{launcher}", "SyncApp.action.whatDoesThisDo": "Che cosa fa questo?", "SyncApp.action.closeLauncher": "Chiudi %{launcher}", + "SyncApp.action.viewStash": "Visualizza il tuo inventario", "SyncApp.action.hideToTray": "Nascondi nel vassoio", "SyncApp.action.getHelp": "Ottieni aiuto", "SyncApp.action.tryAgain": "Riprova", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Lingua", "SyncApp.settings.displayLanguage": "Lingua di visualizzazione", "SyncApp.settings.matchesWindows": "Corrisponde a Windows per impostazione predefinita", + "SyncApp.settings.matchWindows": "Abbina a Windows", "SyncApp.settings.network": "Rete", "SyncApp.settings.networkAdapter": "Adattatore di rete", "SyncApp.settings.networkAdapterSub": "Auto-selezionato. Modifica solo se la sincronizzazione non si connette.", "SyncApp.settings.refresh": "Aggiorna", + "SyncApp.settings.captureMethod": "Metodo di acquisizione", + "SyncApp.settings.captureMethodSub": "Come Sync legge il traffico di gioco del tuo PC. Passa a Npcap se il software antivirus blocca il metodo integrato — potrebbe funzionare meglio anche se utilizzi una VPN o un proxy.", + "SyncApp.settings.captureRawSockets": "Socket raw di Windows (integrati)", + "SyncApp.settings.captureNpcap": "Npcap (installato separatamente)", + "SyncApp.settings.npcapMissing": "Npcap non è installato, quindi la sincronizzazione non può acquisire il traffico. Installalo, poi usa Riprova nella schermata principale.", + "SyncApp.settings.npcapLink": "Ottieni Npcap (gratuito) su npcap.com", "SyncApp.settings.troubleshooting": "Risoluzione dei problemi", "SyncApp.settings.activityLog": "Registro attività", "SyncApp.settings.activityLogSub": "Eventi recenti, per il supporto", "SyncApp.settings.view": "Visualizza", + "SyncApp.settings.hide": "Nascondi", "SyncApp.settings.copyDiagnostics": "Copia diagnostica", "SyncApp.settings.copyDiagnosticsSub": "Condividi con il supporto ARCTracker", "SyncApp.settings.copy": "Copia", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Verifica disponibilità aggiornamenti", + "SyncApp.settings.whatIsSync": "Cosa fa la Sincronizzazione?", + "SyncApp.settings.quit": "Esci da ARCTracker Sync", "SyncApp.tray.open": "Apri ARCTracker Sync", "SyncApp.tray.pause": "Sospendi sincronizzazione", "SyncApp.tray.resume": "Riprendi sincronizzazione", diff --git a/locales/ja.json b/locales/ja.json index bcb116b..a1adcae 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "ランチャー準備完了", "SyncApp.progress.playing": "プレイ中", "SyncApp.progress.synced": "同期完了", + "SyncApp.phase.account.label": "アカウント", + "SyncApp.phase.account.sub": "ARCTracker を接続", + "SyncApp.phase.launcher.label": "ランチャー", + "SyncApp.phase.launcher.sub": "%{launcher} を準備する", + "SyncApp.phase.play.label": "プレイ", + "SyncApp.phase.play.sub": "ARC Raiders を起動", + "SyncApp.phase.sync.label": "同期", + "SyncApp.phase.sync.sub": "インベントリを最新に保つ", + "SyncApp.hero.step": "ステップ %{n} / 4", "SyncApp.footer.signedInAs": "%{account} でサインイン中", + "SyncApp.footer.synced": "同期完了 · %{account}", + "SyncApp.footer.syncedLabel": "同期完了 ·", "SyncApp.footer.notSignedIn": "サインインしていません", "SyncApp.footer.settings": "設定", "SyncApp.state.needsAdmin.title": "クイック権限設定を確認してください", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker は %{launcher} を再起動して、プレイ中の ARC Raiders インベントリを読み込めるようにします。%{launcher} のゲーム、ダウンロード、ログインには影響しません。", "SyncApp.state.preparingLauncher.title": "%{launcher} を準備中…", "SyncApp.state.preparingLauncher.body": "%{launcher} を閉じて再起動しています。数秒かかります。", + "SyncApp.state.preparingLauncher.waiting": "%{launcher} を準備中…", "SyncApp.state.closeLauncher.title": "続行するには %{launcher} を閉じてください", "SyncApp.state.closeLauncher.body": "ARCTracker が %{launcher} を準備するために、%{launcher} を閉じる必要があります。進行中の作業を保存してから閉じてください。", "SyncApp.state.launcherReady.title": "%{launcher} の準備が完了しました。プレイ開始", "SyncApp.state.launcherReady.body": "%{launcher} から好きなときに ARC Raiders を起動してください。プレイ中にインベントリは自動的に同期されます。", "SyncApp.state.connecting.title": "ARC Raiders アカウントを検索中…", "SyncApp.state.connecting.body": "ARC Raiders を起動したままにしてください。通常、メインメニューに到達してから数秒で接続されます。", + "SyncApp.state.connecting.waiting": "ARC Raiders を待機中…", "SyncApp.state.updating.title": "ARCTracker を更新中…", "SyncApp.state.updating.body": "もうすぐです。アカウントを保存しています。", + "SyncApp.state.updating.waiting": "ストレージを保存中…", "SyncApp.state.synced.title": "同期完了", "SyncApp.state.synced.body": "%{account} でプレイ中です。ARCTracker はインベントリを最新に保っています。", "SyncApp.state.synced.session": "インベントリは %{time} まで同期が続きます。", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} が独自に再起動されたため、ARCTracker はインベントリを読み込めません。同期を続行するには再度準備してください。", "SyncApp.state.needsAttention.title": "同期に注意が必要です", "SyncApp.state.needsAttention.body": "ARCTracker Sync が接続時に問題が発生しました。トラブルシューティングを開いて詳細を確認してください。", + "SyncApp.state.needsAttention.npcapBody": "キャプチャ方式が Npcap に設定されていますが、この PC には Npcap がインストールされていません。npcap.com からインストールするか、設定で RAW ソケットに戻してください。", "SyncApp.action.signIn": "ARCTracker にサインイン", "SyncApp.action.cancel": "キャンセル", "SyncApp.action.chooseGame": "ARC Raiders を選択", "SyncApp.action.prepareLauncher": "%{launcher} を準備", "SyncApp.action.whatDoesThisDo": "これは何をするのですか?", "SyncApp.action.closeLauncher": "%{launcher} を閉じる", + "SyncApp.action.viewStash": "ストレージを表示", "SyncApp.action.hideToTray": "トレイに隠す", "SyncApp.action.getHelp": "ヘルプを表示", "SyncApp.action.tryAgain": "もう一度試す", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "言語", "SyncApp.settings.displayLanguage": "表示言語", "SyncApp.settings.matchesWindows": "デフォルトで Windows に合わせます", + "SyncApp.settings.matchWindows": "Windows に合わせる", "SyncApp.settings.network": "ネットワーク", "SyncApp.settings.networkAdapter": "ネットワークアダプター", "SyncApp.settings.networkAdapterSub": "自動選択。同期が接続されない場合のみ変更してください。", "SyncApp.settings.refresh": "更新", + "SyncApp.settings.captureMethod": "キャプチャ方式", + "SyncApp.settings.captureMethodSub": "Sync が PC 自身のゲームトラフィックを読み取る方法です。ウイルス対策ソフトが内蔵の方式をブロックする場合は Npcap に切り替えてください。VPN またはプロキシを使用している場合は、さらに動作が良くなる可能性があります。", + "SyncApp.settings.captureRawSockets": "Windows RAW ソケット(内蔵)", + "SyncApp.settings.captureNpcap": "Npcap(別途インストール)", + "SyncApp.settings.npcapMissing": "Npcap がインストールされていないため、同期はトラフィックをキャプチャできません。インストール後、メイン画面の「もう一度試す」を使用してください。", + "SyncApp.settings.npcapLink": "npcap.com で Npcap(無料)を入手", "SyncApp.settings.troubleshooting": "トラブルシューティング", "SyncApp.settings.activityLog": "アクティビティログ", "SyncApp.settings.activityLogSub": "最近のイベント(サポート用)", "SyncApp.settings.view": "表示", + "SyncApp.settings.hide": "非表示", "SyncApp.settings.copyDiagnostics": "診断情報をコピー", "SyncApp.settings.copyDiagnosticsSub": "ARCTracker サポートと共有する", "SyncApp.settings.copy": "コピー", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "アップデートを確認", + "SyncApp.settings.whatIsSync": "同期は何をするのですか?", + "SyncApp.settings.quit": "ARCTracker Sync を終了", "SyncApp.tray.open": "ARCTracker Sync を開く", "SyncApp.tray.pause": "同期を一時停止", "SyncApp.tray.resume": "同期を再開", diff --git a/locales/ko.json b/locales/ko.json index 32d0c0e..97d9c9d 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "런처 준비됨", "SyncApp.progress.playing": "플레이 중", "SyncApp.progress.synced": "동기화됨", + "SyncApp.phase.account.label": "계정", + "SyncApp.phase.account.sub": "ARCTracker 연결", + "SyncApp.phase.launcher.label": "런처", + "SyncApp.phase.launcher.sub": "%{launcher} 준비", + "SyncApp.phase.play.label": "플레이", + "SyncApp.phase.play.sub": "ARC Raiders 시작", + "SyncApp.phase.sync.label": "동기화", + "SyncApp.phase.sync.sub": "인벤토리 최신 상태 유지", + "SyncApp.hero.step": "%{n}단계 / 4", "SyncApp.footer.signedInAs": "%{account}으로 로그인됨", + "SyncApp.footer.synced": "동기화됨 · %{account}", + "SyncApp.footer.syncedLabel": "동기화됨 ·", "SyncApp.footer.notSignedIn": "로그인하지 않음", "SyncApp.footer.settings": "설정", "SyncApp.state.needsAdmin.title": "먼저 권한이 필요합니다", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker는 플레이 중 ARC Raiders 인벤토리를 읽을 수 있도록 %{launcher}를 재시작합니다. %{launcher} 게임, 다운로드 및 로그인은 영향을 받지 않습니다.", "SyncApp.state.preparingLauncher.title": "%{launcher} 준비 중…", "SyncApp.state.preparingLauncher.body": "%{launcher}을 닫고 다시 열고 있습니다. 이는 몇 초가 걸립니다.", + "SyncApp.state.preparingLauncher.waiting": "%{launcher} 준비 중…", "SyncApp.state.closeLauncher.title": "계속하려면 %{launcher}을 닫으세요", "SyncApp.state.closeLauncher.body": "ARCTracker가 %{launcher}를 준비할 수 있도록 %{launcher}을 닫아야 합니다. 진행 중인 작업을 저장한 후 닫으세요.", "SyncApp.state.launcherReady.title": "%{launcher}이 준비되었습니다. 플레이하세요.", "SyncApp.state.launcherReady.body": "언제든지 %{launcher}에서 ARC Raiders를 시작하세요. 플레이 중에 인벤토리가 자동으로 동기화됩니다.", "SyncApp.state.connecting.title": "ARC Raiders 계정을 찾는 중…", "SyncApp.state.connecting.body": "ARC Raiders를 열어 두세요. 보통 메인 메뉴에 도달한 후 몇 초 후에 연결됩니다.", + "SyncApp.state.connecting.waiting": "ARC Raiders를 기다리는 중…", "SyncApp.state.updating.title": "ARCTracker 업데이트 중…", "SyncApp.state.updating.body": "거의 다 왔습니다. 계정을 저장하는 중입니다.", + "SyncApp.state.updating.waiting": "보관함을 저장하는 중…", "SyncApp.state.synced.title": "동기화되었습니다", "SyncApp.state.synced.body": "%{account}(으)로 플레이 중입니다. ARC Raiders 인벤토리가 ARCTracker에서 최신 상태입니다.", "SyncApp.state.synced.session": "인벤토리는 %{time}까지 동기화된 상태를 유지합니다.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher}이 자체적으로 재시작되었기 때문에 ARCTracker가 지금 인벤토리를 읽을 수 없습니다. 동기화를 계속하려면 다시 준비하세요.", "SyncApp.state.needsAttention.title": "동기화에 주의가 필요합니다", "SyncApp.state.needsAttention.body": "ARCTracker Sync에서 연결 문제가 발생했습니다. 문제 해결을 열어 세부 정보를 확인하세요.", + "SyncApp.state.needsAttention.npcapBody": "캡처 방식이 Npcap으로 설정되어 있지만 이 PC에는 Npcap이 설치되어 있지 않습니다. npcap.com에서 설치하거나 설정에서 원시 소켓으로 다시 전환하세요.", "SyncApp.action.signIn": "ARCTracker에 로그인", "SyncApp.action.cancel": "취소", "SyncApp.action.chooseGame": "ARC Raiders 선택", "SyncApp.action.prepareLauncher": "%{launcher} 준비", "SyncApp.action.whatDoesThisDo": "이것이 무엇을 하나요?", "SyncApp.action.closeLauncher": "%{launcher} 닫기", + "SyncApp.action.viewStash": "보관함 보기", "SyncApp.action.hideToTray": "트레이로 숨기기", "SyncApp.action.getHelp": "도움말 보기", "SyncApp.action.tryAgain": "다시 시도", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "언어", "SyncApp.settings.displayLanguage": "표시 언어", "SyncApp.settings.matchesWindows": "기본적으로 Windows와 일치", + "SyncApp.settings.matchWindows": "Windows와 일치", "SyncApp.settings.network": "네트워크", "SyncApp.settings.networkAdapter": "네트워크 어댑터", "SyncApp.settings.networkAdapterSub": "자동 선택. 동기화가 연결되지 않으면 변경하세요.", "SyncApp.settings.refresh": "새로 고침", + "SyncApp.settings.captureMethod": "캡처 방식", + "SyncApp.settings.captureMethodSub": "Sync가 PC 자체의 게임 트래픽을 읽는 방식입니다. 바이러스 백신 소프트웨어가 기본 제공 방식을 차단하는 경우 Npcap으로 전환하세요. VPN 또는 프록시를 사용하는 경우 더 잘 작동할 수 있습니다.", + "SyncApp.settings.captureRawSockets": "Windows 원시 소켓(기본 제공)", + "SyncApp.settings.captureNpcap": "Npcap(별도 설치)", + "SyncApp.settings.npcapMissing": "Npcap이 설치되어 있지 않아 동기화가 트래픽을 캡처할 수 없습니다. 설치한 후 메인 화면에서 '다시 시도'를 사용하세요.", + "SyncApp.settings.npcapLink": "npcap.com에서 Npcap(무료) 받기", "SyncApp.settings.troubleshooting": "문제 해결", "SyncApp.settings.activityLog": "활동 로그", "SyncApp.settings.activityLogSub": "최근 이벤트, 지원용", "SyncApp.settings.view": "보기", + "SyncApp.settings.hide": "숨기기", "SyncApp.settings.copyDiagnostics": "진단 복사", "SyncApp.settings.copyDiagnosticsSub": "ARCTracker 지원팀과 공유", "SyncApp.settings.copy": "복사", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "업데이트 확인", + "SyncApp.settings.whatIsSync": "동기화는 무엇을 하나요?", + "SyncApp.settings.quit": "ARCTracker Sync 종료", "SyncApp.tray.open": "ARCTracker Sync 열기", "SyncApp.tray.pause": "동기화 일시 중지", "SyncApp.tray.resume": "동기화 재개", diff --git a/locales/no.json b/locales/no.json index af77bbf..8f58d48 100644 --- a/locales/no.json +++ b/locales/no.json @@ -6,6 +6,17 @@ "SyncApp.progress.signedIn": "Logget inn", "SyncApp.progress.launcherReady": "Launcher klar", "SyncApp.progress.playing": "Spiller", + "SyncApp.phase.account.label": "Konto", + "SyncApp.phase.account.sub": "Koble til ARCTracker", + "SyncApp.phase.launcher.label": "Launcher", + "SyncApp.phase.launcher.sub": "Forbered %{launcher}", + "SyncApp.phase.play.label": "Spill", + "SyncApp.phase.play.sub": "Start ARC Raiders", + "SyncApp.phase.sync.label": "Synk", + "SyncApp.phase.sync.sub": "Hold inventaret oppdatert", + "SyncApp.hero.step": "Trinn %{n} av 4", + "SyncApp.footer.synced": "Synkronisert · %{account}", + "SyncApp.footer.syncedLabel": "Synkronisert ·", "SyncApp.progress.synced": "Synkronisert", "SyncApp.footer.signedInAs": "Logget inn som %{account}", "SyncApp.footer.notSignedIn": "Ikke logget inn", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker starter %{launcher} på nytt slik at det kan lese ARC Raiders-inventaret ditt mens du spiller. %{launcher}-spillene dine, nedlastingene og påloggingene berøres ikke.", "SyncApp.state.preparingLauncher.title": "Forbereder %{launcher}…", "SyncApp.state.preparingLauncher.body": "Lukker og åpner %{launcher} på nytt. Dette tar et par sekunder.", + "SyncApp.state.preparingLauncher.waiting": "Forbereder %{launcher}…", "SyncApp.state.closeLauncher.title": "Lukk %{launcher} for å fortsette", "SyncApp.state.closeLauncher.body": "%{launcher} må lukkes slik at ARCTracker kan forberede det. Lagre alt som er i gang, og lukk det deretter.", "SyncApp.state.launcherReady.title": "%{launcher} er klar. Gå og spill.", "SyncApp.state.launcherReady.body": "Start ARC Raiders fra %{launcher} når som helst. Inventaret ditt synkroniseres automatisk mens du spiller.", "SyncApp.state.connecting.title": "Søker etter ARC Raiders-kontoen din…", "SyncApp.state.connecting.body": "Hold ARC Raiders åpen. Dette kobles vanligvis til et par sekunder etter at du når hovedmenyen.", + "SyncApp.state.connecting.waiting": "Venter på ARC Raiders…", "SyncApp.state.updating.title": "Oppdaterer ARCTracker…", "SyncApp.state.updating.body": "Nesten ferdig. Lagrer kontoen din.", + "SyncApp.state.updating.waiting": "Lagrer inventaret ditt…", "SyncApp.state.synced.title": "Du er synkronisert", "SyncApp.state.synced.body": "Spiller som %{account}. ARC Raiders-inventaret ditt er oppdatert på ARCTracker.", "SyncApp.state.synced.session": "Inventaret ditt forblir synkronisert til %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} ble startet på nytt av seg selv, så ARCTracker kan ikke lese inventaret ditt nå. Forbered det igjen for å fortsette synkroniseringen.", "SyncApp.state.needsAttention.title": "Synkronisering trenger oppmerksomhet", "SyncApp.state.needsAttention.body": "ARCTracker Sync fikk et problem med tilkobling. Åpne feilsøking for detaljer.", + "SyncApp.state.needsAttention.npcapBody": "Fangstmetoden er satt til Npcap, men Npcap er ikke installert på denne PC-en. Installer det fra npcap.com, eller bytt tilbake til raw sockets i Innstillinger.", "SyncApp.action.signIn": "Logg inn på ARCTracker", "SyncApp.action.cancel": "Avbryt", "SyncApp.action.chooseGame": "Velg ARC Raiders", "SyncApp.action.prepareLauncher": "Forbered %{launcher}", "SyncApp.action.whatDoesThisDo": "Hva gjør dette?", "SyncApp.action.closeLauncher": "Lukk %{launcher}", + "SyncApp.action.viewStash": "Vis inventaret ditt", "SyncApp.action.hideToTray": "Skjul i systemkurv", "SyncApp.action.getHelp": "Få hjelp", "SyncApp.action.tryAgain": "Prøv igjen", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Språk", "SyncApp.settings.displayLanguage": "Visningsspråk", "SyncApp.settings.matchesWindows": "Samsvarer med Windows som standard", + "SyncApp.settings.matchWindows": "Samsvarer med Windows", "SyncApp.settings.network": "Nettverk", "SyncApp.settings.networkAdapter": "Nettverkskort", "SyncApp.settings.networkAdapterSub": "Automatisk valgt. Endre bare hvis synkronisering ikke kobles til.", "SyncApp.settings.refresh": "Oppdater", + "SyncApp.settings.captureMethod": "Fangstmetode", + "SyncApp.settings.captureMethodSub": "Hvordan Sync leser PC-ens egen spilltrafikk. Bytt til Npcap hvis antivirusprogramvare blokkerer den innebygde metoden — det kan også fungere bedre hvis du bruker en VPN eller proxy.", + "SyncApp.settings.captureRawSockets": "Windows raw sockets (innebygd)", + "SyncApp.settings.captureNpcap": "Npcap (installeres separat)", + "SyncApp.settings.npcapMissing": "Npcap er ikke installert, så synkronisering kan ikke fange opp trafikk. Installer det, og bruk deretter Prøv igjen på hovedskjermen.", + "SyncApp.settings.npcapLink": "Hent Npcap (gratis) på npcap.com", "SyncApp.settings.troubleshooting": "Feilsøking", "SyncApp.settings.activityLog": "Aktivitetslogg", "SyncApp.settings.activityLogSub": "Nylige hendelser, for kundestøtte", "SyncApp.settings.view": "Vis", + "SyncApp.settings.hide": "Skjul", "SyncApp.settings.copyDiagnostics": "Kopier diagnostikk", "SyncApp.settings.copyDiagnosticsSub": "Del med ARCTracker-kundestøtte", "SyncApp.settings.copy": "Kopier", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Se etter oppdateringer", + "SyncApp.settings.whatIsSync": "Hva gjør Synk?", + "SyncApp.settings.quit": "Avslutt ARCTracker Sync", "SyncApp.tray.open": "Åpne ARCTracker Sync", "SyncApp.tray.pause": "Pause synkronisering", "SyncApp.tray.resume": "Gjenoppta synkronisering", diff --git a/locales/pl.json b/locales/pl.json index 7d750d1..eb01bf7 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -6,6 +6,17 @@ "SyncApp.progress.signedIn": "Zalogowany", "SyncApp.progress.launcherReady": "Launcher gotowy", "SyncApp.progress.playing": "Grając", + "SyncApp.phase.account.label": "Konto", + "SyncApp.phase.account.sub": "Połącz ARCTracker", + "SyncApp.phase.launcher.label": "Launcher", + "SyncApp.phase.launcher.sub": "Przygotuj %{launcher}", + "SyncApp.phase.play.label": "Graj", + "SyncApp.phase.play.sub": "Uruchom ARC Raiders", + "SyncApp.phase.sync.label": "Synchronizacja", + "SyncApp.phase.sync.sub": "Utrzymuj ekwipunek aktualny", + "SyncApp.hero.step": "Krok %{n} z 4", + "SyncApp.footer.synced": "Zsynchronizowany · %{account}", + "SyncApp.footer.syncedLabel": "Zsynchronizowany ·", "SyncApp.progress.synced": "Zsynchronizowany", "SyncApp.footer.signedInAs": "Zalogowany jako %{account}", "SyncApp.footer.notSignedIn": "Niezalogowany", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker uruchamia %{launcher} ponownie, aby mógł czytać twój ekwipunek ARC Raiders podczas gry. Twoje gry %{launcher}, pobierania i logowania nie będą dotknięte.", "SyncApp.state.preparingLauncher.title": "Przygotowywanie %{launcher}…", "SyncApp.state.preparingLauncher.body": "Zamykanie i ponowne otwieranie %{launcher}. To zajmuje kilka sekund.", + "SyncApp.state.preparingLauncher.waiting": "Przygotowywanie %{launcher}…", "SyncApp.state.closeLauncher.title": "Zamknij %{launcher}, aby kontynuować", "SyncApp.state.closeLauncher.body": "%{launcher} musi się zamknąć, aby ARCTracker mógł go przygotować. Zapisz wszystko w toku, a następnie zamknij.", "SyncApp.state.launcherReady.title": "%{launcher} jest gotowy. Czas grać.", "SyncApp.state.launcherReady.body": "Uruchom ARC Raiders z %{launcher} kiedy chcesz. Twój ekwipunek synchronizuje się automatycznie podczas gry.", "SyncApp.state.connecting.title": "Szukanie twojego konta ARC Raiders…", "SyncApp.state.connecting.body": "Trzymaj ARC Raiders otwarte. Zwykle łączy się kilka sekund po dotarciu do głównego menu.", + "SyncApp.state.connecting.waiting": "Oczekiwanie na ARC Raiders…", "SyncApp.state.updating.title": "Aktualizowanie ARCTracker…", "SyncApp.state.updating.body": "Prawie gotowe. Zapisywanie twojego konta.", + "SyncApp.state.updating.waiting": "Zapisywanie twojego ekwipunku…", "SyncApp.state.synced.title": "Jesteś zsynchronizowany", "SyncApp.state.synced.body": "Grasz jako %{account}. Twój ekwipunek ARC Raiders jest aktualny w ARCTracker.", "SyncApp.state.synced.session": "Twój ekwipunek pozostaje zsynchronizowany do %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} został uruchomiony ponownie samodzielnie, więc ARCTracker nie może w tej chwili czytać twojego ekwipunku. Przygotuj go ponownie, aby kontynuować synchronizowanie.", "SyncApp.state.needsAttention.title": "Synchronizowanie wymaga uwagi", "SyncApp.state.needsAttention.body": "ARCTracker Sync napotkał problem przy łączeniu. Otwórz rozwiązywanie problemów, aby uzyskać szczegóły.", + "SyncApp.state.needsAttention.npcapBody": "Metoda przechwytywania jest ustawiona na Npcap, ale Npcap nie jest zainstalowany na tym komputerze. Zainstaluj go ze strony npcap.com lub przełącz z powrotem na surowe gniazda w Ustawieniach.", "SyncApp.action.signIn": "Zaloguj się do ARCTracker", "SyncApp.action.cancel": "Anuluj", "SyncApp.action.chooseGame": "Wybierz ARC Raiders", "SyncApp.action.prepareLauncher": "Przygotuj %{launcher}", "SyncApp.action.whatDoesThisDo": "Co to robi?", "SyncApp.action.closeLauncher": "Zamknij %{launcher}", + "SyncApp.action.viewStash": "Wyświetl swój ekwipunek", "SyncApp.action.hideToTray": "Ukryj w zasobniku", "SyncApp.action.getHelp": "Uzyskaj pomoc", "SyncApp.action.tryAgain": "Spróbuj ponownie", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Język", "SyncApp.settings.displayLanguage": "Język wyświetlania", "SyncApp.settings.matchesWindows": "Domyślnie dopasowuje się do Windows", + "SyncApp.settings.matchWindows": "Dopasuj do Windows", "SyncApp.settings.network": "Sieć", "SyncApp.settings.networkAdapter": "Adapter sieciowy", "SyncApp.settings.networkAdapterSub": "Auto-wybrany. Zmień tylko jeśli synchronizowanie się nie łączy.", "SyncApp.settings.refresh": "Odśwież", + "SyncApp.settings.captureMethod": "Metoda przechwytywania", + "SyncApp.settings.captureMethodSub": "Sposób, w jaki Sync odczytuje ruch gry z twojego komputera. Przełącz na Npcap, jeśli oprogramowanie antywirusowe blokuje wbudowaną metodę — może działać też lepiej, jeśli używasz VPN lub proxy.", + "SyncApp.settings.captureRawSockets": "Surowe gniazda Windows (wbudowane)", + "SyncApp.settings.captureNpcap": "Npcap (instalowany osobno)", + "SyncApp.settings.npcapMissing": "Npcap nie jest zainstalowany, więc synchronizowanie nie może przechwytywać ruchu. Zainstaluj go, a następnie użyj Spróbuj ponownie na głównym ekranie.", + "SyncApp.settings.npcapLink": "Pobierz Npcap (bezpłatnie) ze strony npcap.com", "SyncApp.settings.troubleshooting": "Rozwiązywanie problemów", "SyncApp.settings.activityLog": "Dziennik aktywności", "SyncApp.settings.activityLogSub": "Ostatnie zdarzenia dla wsparcia", "SyncApp.settings.view": "Wyświetl", + "SyncApp.settings.hide": "Ukryj", "SyncApp.settings.copyDiagnostics": "Skopiuj diagnostykę", "SyncApp.settings.copyDiagnosticsSub": "Udostępnij wsparciu ARCTracker", "SyncApp.settings.copy": "Skopiuj", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Sprawdź aktualizacje", + "SyncApp.settings.whatIsSync": "Co robi Synchronizacja?", + "SyncApp.settings.quit": "Wyjdź z ARCTracker Sync", "SyncApp.tray.open": "Otwórz ARCTracker Sync", "SyncApp.tray.pause": "Wstrzymaj synchronizowanie", "SyncApp.tray.resume": "Wznów synchronizowanie", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 5970c9c..66434c9 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Inicializador pronto", "SyncApp.progress.playing": "Jogando", "SyncApp.progress.synced": "Sincronizado", + "SyncApp.phase.account.label": "Conta", + "SyncApp.phase.account.sub": "Conectar ARCTracker", + "SyncApp.phase.launcher.label": "Inicializador", + "SyncApp.phase.launcher.sub": "Preparar %{launcher}", + "SyncApp.phase.play.label": "Jogar", + "SyncApp.phase.play.sub": "Inicie o ARC Raiders", + "SyncApp.phase.sync.label": "Sincronizar", + "SyncApp.phase.sync.sub": "Manter inventário atualizado", + "SyncApp.hero.step": "Passo %{n} de 4", "SyncApp.footer.signedInAs": "Conectado como %{account}", + "SyncApp.footer.synced": "Sincronizado · %{account}", + "SyncApp.footer.syncedLabel": "Sincronizado ·", "SyncApp.footer.notSignedIn": "Não conectado", "SyncApp.footer.settings": "Configurações", "SyncApp.state.needsAdmin.title": "Uma permissão rápida primeiro", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker reinicia %{launcher} para que possa ler seu inventário do ARC Raiders enquanto você joga. Seus jogos, downloads e logins do %{launcher} não serão alterados.", "SyncApp.state.preparingLauncher.title": "Preparando %{launcher}…", "SyncApp.state.preparingLauncher.body": "Fechando e reabrindo %{launcher}. Isso leva alguns segundos.", + "SyncApp.state.preparingLauncher.waiting": "Preparando %{launcher}…", "SyncApp.state.closeLauncher.title": "Feche %{launcher} para continuar", "SyncApp.state.closeLauncher.body": "%{launcher} precisa fechar para que ARCTracker possa prepará-lo. Salve qualquer progresso em andamento e feche-o.", "SyncApp.state.launcherReady.title": "%{launcher} está pronto. Bora jogar.", "SyncApp.state.launcherReady.body": "Inicie o ARC Raiders a partir de %{launcher} quando quiser. Seu inventário é sincronizado automaticamente enquanto você joga.", "SyncApp.state.connecting.title": "Procurando sua conta do ARC Raiders…", "SyncApp.state.connecting.body": "Mantenha o ARC Raiders aberto. Geralmente conecta alguns segundos depois que você alcança o menu principal.", + "SyncApp.state.connecting.waiting": "Aguardando ARC Raiders…", "SyncApp.state.updating.title": "Atualizando ARCTracker…", "SyncApp.state.updating.body": "Já está quase pronto. Salvando sua conta.", + "SyncApp.state.updating.waiting": "Salvando seu inventário…", "SyncApp.state.synced.title": "Você está sincronizado", "SyncApp.state.synced.body": "Jogando como %{account}. Seu inventário do ARC Raiders está atualizado no ARCTracker.", "SyncApp.state.synced.session": "Seu inventário fica sincronizado até %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} foi reiniciado sozinho, então ARCTracker não consegue ler seu inventário agora. Prepare-o novamente para continuar sincronizando.", "SyncApp.state.needsAttention.title": "Sincronização precisa de atenção", "SyncApp.state.needsAttention.body": "ARCTracker Sync encontrou um problema ao conectar. Abra a solução de problemas para obter detalhes.", + "SyncApp.state.needsAttention.npcapBody": "O método de captura está definido como Npcap, mas o Npcap não está instalado neste PC. Instale-o em npcap.com ou volte para os sockets brutos nas Configurações.", "SyncApp.action.signIn": "Conectar ao ARCTracker", "SyncApp.action.cancel": "Cancelar", "SyncApp.action.chooseGame": "Escolher ARC Raiders", "SyncApp.action.prepareLauncher": "Preparar %{launcher}", "SyncApp.action.whatDoesThisDo": "O que faz isso?", "SyncApp.action.closeLauncher": "Fechar %{launcher}", + "SyncApp.action.viewStash": "Ver seu inventário", "SyncApp.action.hideToTray": "Ocultar na bandeja", "SyncApp.action.getHelp": "Obter ajuda", "SyncApp.action.tryAgain": "Tentar novamente", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Idioma", "SyncApp.settings.displayLanguage": "Idioma de exibição", "SyncApp.settings.matchesWindows": "Corresponde ao Windows por padrão", + "SyncApp.settings.matchWindows": "Corresponder ao Windows", "SyncApp.settings.network": "Rede", "SyncApp.settings.networkAdapter": "Adaptador de rede", "SyncApp.settings.networkAdapterSub": "Auto-selecionado. Mude apenas se a sincronização não conectar.", "SyncApp.settings.refresh": "Atualizar", + "SyncApp.settings.captureMethod": "Método de captura", + "SyncApp.settings.captureMethodSub": "Como o Sync lê o tráfego de jogo do seu próprio PC. Mude para o Npcap se um antivírus bloquear o método integrado — também pode funcionar melhor se você usar um VPN ou proxy.", + "SyncApp.settings.captureRawSockets": "Sockets brutos do Windows (integrado)", + "SyncApp.settings.captureNpcap": "Npcap (instalado separadamente)", + "SyncApp.settings.npcapMissing": "O Npcap não está instalado, então a sincronização não consegue capturar o tráfego. Instale-o e depois use Tentar novamente na tela principal.", + "SyncApp.settings.npcapLink": "Baixe o Npcap (grátis) em npcap.com", "SyncApp.settings.troubleshooting": "Solução de problemas", "SyncApp.settings.activityLog": "Registro de atividades", "SyncApp.settings.activityLogSub": "Eventos recentes, para suporte", "SyncApp.settings.view": "Visualizar", + "SyncApp.settings.hide": "Ocultar", "SyncApp.settings.copyDiagnostics": "Copiar diagnóstico", "SyncApp.settings.copyDiagnosticsSub": "Compartilhe com o suporte ARCTracker", "SyncApp.settings.copy": "Copiar", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Verificar atualizações", + "SyncApp.settings.whatIsSync": "O que Sync faz?", + "SyncApp.settings.quit": "Sair do ARCTracker Sync", "SyncApp.tray.open": "Abrir ARCTracker Sync", "SyncApp.tray.pause": "Pausar sincronização", "SyncApp.tray.resume": "Retomar sincronização", diff --git a/locales/pt.json b/locales/pt.json index 76970d6..b06f1b0 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Iniciador pronto", "SyncApp.progress.playing": "A jogar", "SyncApp.progress.synced": "Sincronizado", + "SyncApp.phase.account.label": "Conta", + "SyncApp.phase.account.sub": "Conectar ARCTracker", + "SyncApp.phase.launcher.label": "Iniciador", + "SyncApp.phase.launcher.sub": "Preparar %{launcher}", + "SyncApp.phase.play.label": "Jogar", + "SyncApp.phase.play.sub": "Inicia ARC Raiders", + "SyncApp.phase.sync.label": "Sincronizar", + "SyncApp.phase.sync.sub": "Manter inventário atualizado", + "SyncApp.hero.step": "Passo %{n} de 4", "SyncApp.footer.signedInAs": "Sessão iniciada como %{account}", + "SyncApp.footer.synced": "Sincronizado · %{account}", + "SyncApp.footer.syncedLabel": "Sincronizado ·", "SyncApp.footer.notSignedIn": "Sem sessão iniciada", "SyncApp.footer.settings": "Definições", "SyncApp.state.needsAdmin.title": "Uma permissão rápida primeiro", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker reinicia %{launcher} para que possa ler o teu inventário de ARC Raiders enquanto jogas. Os teus jogos, transferências e sessões %{launcher} não são afetados.", "SyncApp.state.preparingLauncher.title": "A preparar %{launcher}…", "SyncApp.state.preparingLauncher.body": "A fechar e reabrir %{launcher}. Isto demora alguns segundos.", + "SyncApp.state.preparingLauncher.waiting": "A preparar %{launcher}…", "SyncApp.state.closeLauncher.title": "Fecha %{launcher} para continuar", "SyncApp.state.closeLauncher.body": "%{launcher} precisa de fechar para que ARCTracker o possa preparar. Guarda o teu progresso e fecha-o.", "SyncApp.state.launcherReady.title": "%{launcher} está pronto. Vai jogar.", "SyncApp.state.launcherReady.body": "Inicia ARC Raiders a partir de %{launcher} sempre que quiseres. O teu inventário sincroniza-se automaticamente enquanto jogas.", "SyncApp.state.connecting.title": "À procura da tua conta ARC Raiders…", "SyncApp.state.connecting.body": "Mantém ARC Raiders aberto. Isto normalmente conecta alguns segundos depois de atingires o menu principal.", + "SyncApp.state.connecting.waiting": "À espera de ARC Raiders…", "SyncApp.state.updating.title": "A atualizar ARCTracker…", "SyncApp.state.updating.body": "Quase lá. A guardar a tua conta.", + "SyncApp.state.updating.waiting": "A guardar o teu inventário…", "SyncApp.state.synced.title": "Estás sincronizado", "SyncApp.state.synced.body": "A jogar como %{account}. O teu inventário de ARC Raiders está atualizado no ARCTracker.", "SyncApp.state.synced.session": "O teu inventário mantém-se sincronizado até %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} foi reiniciado por conta própria, por isso ARCTracker não consegue ler o teu inventário neste momento. Prepara-o novamente para continuar a sincronizar.", "SyncApp.state.needsAttention.title": "A sincronização precisa de atenção", "SyncApp.state.needsAttention.body": "ARCTracker Sync encontrou um problema ao conectar. Abre a resolução de problemas para mais detalhes.", + "SyncApp.state.needsAttention.npcapBody": "O método de captura está definido como Npcap, mas o Npcap não está instalado neste PC. Instala-o a partir de npcap.com ou volta aos sockets em bruto nas Definições.", "SyncApp.action.signIn": "Inicia sessão no ARCTracker", "SyncApp.action.cancel": "Cancelar", "SyncApp.action.chooseGame": "Escolhe ARC Raiders", "SyncApp.action.prepareLauncher": "Prepara %{launcher}", "SyncApp.action.whatDoesThisDo": "O que é que isto faz?", "SyncApp.action.closeLauncher": "Fecha %{launcher}", + "SyncApp.action.viewStash": "Ver o teu inventário", "SyncApp.action.hideToTray": "Oculta para a bandeja", "SyncApp.action.getHelp": "Obter ajuda", "SyncApp.action.tryAgain": "Tenta novamente", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Idioma", "SyncApp.settings.displayLanguage": "Idioma de apresentação", "SyncApp.settings.matchesWindows": "Corresponde a Windows por predefinição", + "SyncApp.settings.matchWindows": "Correspondência Windows", "SyncApp.settings.network": "Rede", "SyncApp.settings.networkAdapter": "Adaptador de rede", "SyncApp.settings.networkAdapterSub": "Auto-selecionado. Altera apenas se sync não conectar.", "SyncApp.settings.refresh": "Atualizar", + "SyncApp.settings.captureMethod": "Método de captura", + "SyncApp.settings.captureMethodSub": "Como Sync lê o tráfego de jogo do teu próprio PC. Muda para Npcap se um antivírus bloquear o método integrado — também pode funcionar melhor se usares um VPN ou proxy.", + "SyncApp.settings.captureRawSockets": "Sockets em bruto do Windows (integrado)", + "SyncApp.settings.captureNpcap": "Npcap (instalado separadamente)", + "SyncApp.settings.npcapMissing": "Npcap não está instalado, por isso a sincronização não consegue capturar tráfego. Instala-o e depois usa Tenta novamente no ecrã principal.", + "SyncApp.settings.npcapLink": "Obtém Npcap (gratuito) em npcap.com", "SyncApp.settings.troubleshooting": "Resolução de problemas", "SyncApp.settings.activityLog": "Registo de atividade", "SyncApp.settings.activityLogSub": "Eventos recentes, para suporte", "SyncApp.settings.view": "Ver", + "SyncApp.settings.hide": "Ocultar", "SyncApp.settings.copyDiagnostics": "Copia diagnósticos", "SyncApp.settings.copyDiagnosticsSub": "Partilha com suporte ARCTracker", "SyncApp.settings.copy": "Copiar", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Verifica atualizações", + "SyncApp.settings.whatIsSync": "O que faz Sync?", + "SyncApp.settings.quit": "Sair do ARCTracker Sync", "SyncApp.tray.open": "Abre ARCTracker Sync", "SyncApp.tray.pause": "Pausa sincronização", "SyncApp.tray.resume": "Retoma sincronização", diff --git a/locales/ru.json b/locales/ru.json index f0bb69b..e0e3111 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Лаунчер готов", "SyncApp.progress.playing": "Играет", "SyncApp.progress.synced": "Синхронизировано", + "SyncApp.phase.account.label": "Учётная запись", + "SyncApp.phase.account.sub": "Подключить ARCTracker", + "SyncApp.phase.launcher.label": "Лаунчер", + "SyncApp.phase.launcher.sub": "Подготовить %{launcher}", + "SyncApp.phase.play.label": "Играть", + "SyncApp.phase.play.sub": "Запустить ARC Raiders", + "SyncApp.phase.sync.label": "Синхронизация", + "SyncApp.phase.sync.sub": "Поддерживать инвентарь в актуальном состоянии", + "SyncApp.hero.step": "Шаг %{n} из 4", "SyncApp.footer.signedInAs": "Вход как %{account}", + "SyncApp.footer.synced": "Синхронизировано · %{account}", + "SyncApp.footer.syncedLabel": "Синхронизировано ·", "SyncApp.footer.notSignedIn": "Не вошли", "SyncApp.footer.settings": "Настройки", "SyncApp.state.needsAdmin.title": "Сначала нужно разрешение", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker перезагружает %{launcher}, чтобы иметь возможность читать ваш инвентарь ARC Raiders во время игры. Ваши игры в %{launcher}, загрузки и входы не будут затронуты.", "SyncApp.state.preparingLauncher.title": "Подготовка %{launcher}…", "SyncApp.state.preparingLauncher.body": "Закрытие и перезагрузка %{launcher}. Это займёт несколько секунд.", + "SyncApp.state.preparingLauncher.waiting": "Подготовка %{launcher}…", "SyncApp.state.closeLauncher.title": "Закройте %{launcher}, чтобы продолжить", "SyncApp.state.closeLauncher.body": "%{launcher} нужно закрыть, чтобы ARCTracker мог его подготовить. Сохраните любые текущие работы, а затем закройте его.", "SyncApp.state.launcherReady.title": "%{launcher} готов. Идите играть.", "SyncApp.state.launcherReady.body": "Запустите ARC Raiders из %{launcher} в любое время. Ваш инвентарь синхронизируется автоматически во время игры.", "SyncApp.state.connecting.title": "Поиск вашей учётной записи ARC Raiders…", "SyncApp.state.connecting.body": "Держите ARC Raiders открытым. Обычно подключение происходит через несколько секунд после того, как вы достигнете главного меню.", + "SyncApp.state.connecting.waiting": "Ожидание ARC Raiders…", "SyncApp.state.updating.title": "Обновление ARCTracker…", "SyncApp.state.updating.body": "Почти готово. Сохранение вашей учётной записи.", + "SyncApp.state.updating.waiting": "Сохранение вашего инвентаря…", "SyncApp.state.synced.title": "Вы синхронизированы", "SyncApp.state.synced.body": "Играете как %{account}. Ваш инвентарь ARC Raiders актуален на ARCTracker.", "SyncApp.state.synced.session": "Ваш инвентарь остаётся синхронизированным до %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} был перезагружен самостоятельно, поэтому ARCTracker не может прочитать ваш инвентарь прямо сейчас. Подготовьте его снова, чтобы продолжить синхронизацию.", "SyncApp.state.needsAttention.title": "Синхронизация требует внимания", "SyncApp.state.needsAttention.body": "ARCTracker Sync столкнулся с проблемой при подключении. Откройте устранение неполадок для получения деталей.", + "SyncApp.state.needsAttention.npcapBody": "Метод захвата установлен на Npcap, но Npcap не установлен на этом ПК. Установите его с npcap.com или переключитесь обратно на сырые сокеты в настройках.", "SyncApp.action.signIn": "Войти в ARCTracker", "SyncApp.action.cancel": "Отмена", "SyncApp.action.chooseGame": "Выбрать ARC Raiders", "SyncApp.action.prepareLauncher": "Подготовить %{launcher}", "SyncApp.action.whatDoesThisDo": "Что это делает?", "SyncApp.action.closeLauncher": "Закрыть %{launcher}", + "SyncApp.action.viewStash": "Посмотреть ваш инвентарь", "SyncApp.action.hideToTray": "Скрыть в трей", "SyncApp.action.getHelp": "Получить помощь", "SyncApp.action.tryAgain": "Попробовать снова", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Язык", "SyncApp.settings.displayLanguage": "Язык интерфейса", "SyncApp.settings.matchesWindows": "По умолчанию совпадает с Windows", + "SyncApp.settings.matchWindows": "Совпадать с Windows", "SyncApp.settings.network": "Сеть", "SyncApp.settings.networkAdapter": "Сетевой адаптер", "SyncApp.settings.networkAdapterSub": "Выбран автоматически. Измените только если синхронизация не подключается.", "SyncApp.settings.refresh": "Обновить", + "SyncApp.settings.captureMethod": "Метод захвата", + "SyncApp.settings.captureMethodSub": "Как Sync читает собственный игровой трафик вашего ПК. Переключайтесь на Npcap, если антивирус блокирует встроенный метод — это также может работать лучше, если вы используете VPN или прокси.", + "SyncApp.settings.captureRawSockets": "Сырые сокеты Windows (встроено)", + "SyncApp.settings.captureNpcap": "Npcap (устанавливается отдельно)", + "SyncApp.settings.npcapMissing": "Npcap не установлен, поэтому синхронизация не может захватывать трафик. Установите его, а затем нажмите «Попробовать снова» на главном экране.", + "SyncApp.settings.npcapLink": "Получить Npcap (бесплатно) на npcap.com", "SyncApp.settings.troubleshooting": "Устранение неполадок", "SyncApp.settings.activityLog": "Журнал активности", "SyncApp.settings.activityLogSub": "Последние события для поддержки", "SyncApp.settings.view": "Просмотр", + "SyncApp.settings.hide": "Скрыть", "SyncApp.settings.copyDiagnostics": "Копировать диагностику", "SyncApp.settings.copyDiagnosticsSub": "Поделитесь с поддержкой ARCTracker", "SyncApp.settings.copy": "Копировать", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Проверить обновления", + "SyncApp.settings.whatIsSync": "Что делает синхронизация?", + "SyncApp.settings.quit": "Выход из ARCTracker Sync", "SyncApp.tray.open": "Открыть ARCTracker Sync", "SyncApp.tray.pause": "Паузировать синхронизацию", "SyncApp.tray.resume": "Возобновить синхронизацию", diff --git a/locales/sr.json b/locales/sr.json index 72f3c07..334dae2 100644 --- a/locales/sr.json +++ b/locales/sr.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Pokretač je spreman", "SyncApp.progress.playing": "Igranje", "SyncApp.progress.synced": "Sinhronizovano", + "SyncApp.phase.account.label": "Nalog", + "SyncApp.phase.account.sub": "Povežite ARCTracker", + "SyncApp.phase.launcher.label": "Pokretač", + "SyncApp.phase.launcher.sub": "Pripremite %{launcher}", + "SyncApp.phase.play.label": "Igraj", + "SyncApp.phase.play.sub": "Pokrenite ARC Raiders", + "SyncApp.phase.sync.label": "Sinhronizacija", + "SyncApp.phase.sync.sub": "Održavajte inventar ažurnim", + "SyncApp.hero.step": "Korak %{n} od 4", "SyncApp.footer.signedInAs": "Prijavljeni kao %{account}", + "SyncApp.footer.synced": "Sinhronizovano · %{account}", + "SyncApp.footer.syncedLabel": "Sinhronizovano ·", "SyncApp.footer.notSignedIn": "Niste prijavljeni", "SyncApp.footer.settings": "Postavke", "SyncApp.state.needsAdmin.title": "Prvo trebate dozvolu", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker restartuje %{launcher} kako bi mogao čitati vašu ARC Raiders inventaru dok igrate. Vaši %{launcher} igre, preuzimanja i prijave nisu dodirivane.", "SyncApp.state.preparingLauncher.title": "Pripremanje %{launcher}…", "SyncApp.state.preparingLauncher.body": "Zatvaranje i ponovno otvaranje %{launcher}. Ovo traje nekoliko sekundi.", + "SyncApp.state.preparingLauncher.waiting": "Pripremanje %{launcher}…", "SyncApp.state.closeLauncher.title": "Zatvorite %{launcher} da nastavite", "SyncApp.state.closeLauncher.body": "%{launcher} se mora zatvoriti kako bi ARCTracker mogao to da pripremi. Sačuvajte sve što je u toku, zatim ga zatvorite.", "SyncApp.state.launcherReady.title": "%{launcher} je spreman. Idite da igrate.", "SyncApp.state.launcherReady.body": "Pokrenite ARC Raiders iz %{launcher} kada god želite. Vaša inventara se automatski sinhronizuje dok igrate.", "SyncApp.state.connecting.title": "Tražim vaš ARC Raiders nalog…", "SyncApp.state.connecting.body": "Čuvajte ARC Raiders otvoren. Ovo se obično povezuje nekoliko sekundi nakon što dosegnete glavni meni.", + "SyncApp.state.connecting.waiting": "Čekam ARC Raiders…", "SyncApp.state.updating.title": "Ažuriranje ARCTrackera…", "SyncApp.state.updating.body": "Skoro gotovo. Čuvanje vašeg naloga.", + "SyncApp.state.updating.waiting": "Čuvanje vašeg inventara…", "SyncApp.state.synced.title": "Sinhronizovani ste", "SyncApp.state.synced.body": "Igranje kao %{account}. Vaša ARC Raiders inventara je ažurna na ARCTracker-u.", "SyncApp.state.synced.session": "Vaša inventara ostaje sinhronizovana do %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} je sâm restartuovan, tako da ARCTracker trenutno ne može čitati vašu inventaru. Pripremite ponovo da nastavite sa sinhronizacijom.", "SyncApp.state.needsAttention.title": "Sinhronizacija trebate pažnju", "SyncApp.state.needsAttention.body": "ARCTracker Sync je nailazio na problem pri povezivanju. Otvorite rešavanje problema za više detalja.", + "SyncApp.state.needsAttention.npcapBody": "Metoda hvatanja je postavljena na Npcap, ali Npcap nije instaliran na ovom PC-u. Instalirajte ga sa npcap.com ili se vratite na sirove sokete u Postavkama.", "SyncApp.action.signIn": "Prijavite se na ARCTracker", "SyncApp.action.cancel": "Otkaži", "SyncApp.action.chooseGame": "Odaberite ARC Raiders", "SyncApp.action.prepareLauncher": "Pripremite %{launcher}", "SyncApp.action.whatDoesThisDo": "Šta ovo radi?", "SyncApp.action.closeLauncher": "Zatvorite %{launcher}", + "SyncApp.action.viewStash": "Pogledajte svoj inventar", "SyncApp.action.hideToTray": "Sakrijte u paletu", "SyncApp.action.getHelp": "Zatražite pomoć", "SyncApp.action.tryAgain": "Pokušajte ponovo", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Jezik", "SyncApp.settings.displayLanguage": "Prikazi jezik", "SyncApp.settings.matchesWindows": "Odgovara Windows po zadanom", + "SyncApp.settings.matchWindows": "Uskladi sa Windows-om", "SyncApp.settings.network": "Mreža", "SyncApp.settings.networkAdapter": "Mrežni adapter", "SyncApp.settings.networkAdapterSub": "Automatski izabran. Samo promenite ako sinhronizacija se ne povezuje.", "SyncApp.settings.refresh": "Osvežite", + "SyncApp.settings.captureMethod": "Metoda hvatanja", + "SyncApp.settings.captureMethodSub": "Kako Sync čita sopstveni promet igre vašeg PC-a. Pređite na Npcap ako antivirusni softver blokira ugrađenu metodu — može i bolje raditi ako koristite VPN ili proxy.", + "SyncApp.settings.captureRawSockets": "Windows sirovi soketi (ugrađeno)", + "SyncApp.settings.captureNpcap": "Npcap (instalira se zasebno)", + "SyncApp.settings.npcapMissing": "Npcap nije instaliran, pa sinhronizacija ne može da hvata promet. Instalirajte ga, a zatim upotrebite Pokušajte ponovo na glavnom ekranu.", + "SyncApp.settings.npcapLink": "Preuzmite Npcap (besplatno) na npcap.com", "SyncApp.settings.troubleshooting": "Rešavanje problema", "SyncApp.settings.activityLog": "Zapisnik aktivnosti", "SyncApp.settings.activityLogSub": "Skorašnji događaji, za podršku", "SyncApp.settings.view": "Pogledajte", + "SyncApp.settings.hide": "Sakrijte", "SyncApp.settings.copyDiagnostics": "Kopirajte dijagnostiku", "SyncApp.settings.copyDiagnosticsSub": "Delite sa ARCTracker podrškom", "SyncApp.settings.copy": "Kopiraj", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Proverite ažuriranja", + "SyncApp.settings.whatIsSync": "Šta Sync radi?", + "SyncApp.settings.quit": "Izlaz iz ARCTracker Sync", "SyncApp.tray.open": "Otvorite ARCTracker Sync", "SyncApp.tray.pause": "Pauziranje sinhronizacije", "SyncApp.tray.resume": "Nastavite sinhronizaciju", diff --git a/locales/tr.json b/locales/tr.json index 3356505..d31d8d1 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -6,6 +6,17 @@ "SyncApp.progress.signedIn": "Oturum açıldı", "SyncApp.progress.launcherReady": "Başlatıcı hazır", "SyncApp.progress.playing": "Oynanıyor", + "SyncApp.phase.account.label": "Hesap", + "SyncApp.phase.account.sub": "ARCTracker'ı bağlayın", + "SyncApp.phase.launcher.label": "Başlatıcı", + "SyncApp.phase.launcher.sub": "%{launcher}'ı hazırlayın", + "SyncApp.phase.play.label": "Oyna", + "SyncApp.phase.play.sub": "ARC Raiders'ı başlatın", + "SyncApp.phase.sync.label": "Senkronizasyon", + "SyncApp.phase.sync.sub": "Envanteri canlı tutun", + "SyncApp.hero.step": "%{n}. adım / 4", + "SyncApp.footer.synced": "Senkronize edildi · %{account}", + "SyncApp.footer.syncedLabel": "Senkronize edildi ·", "SyncApp.progress.synced": "Senkronize edildi", "SyncApp.footer.signedInAs": "%{account} olarak oturum açıldı", "SyncApp.footer.notSignedIn": "Oturum açılmamış", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker, oynarken ARC Raiders envanterinizi okuyabilmesi için %{launcher}'ı yeniden başlatır. %{launcher} oyunlarınız, indirmeleriniz ve oturum açışlarınız etkilenmez.", "SyncApp.state.preparingLauncher.title": "%{launcher} hazırlanıyor…", "SyncApp.state.preparingLauncher.body": "%{launcher} kapatılıp yeniden açılıyor. Bu birkaç saniye sürer.", + "SyncApp.state.preparingLauncher.waiting": "%{launcher} hazırlanıyor…", "SyncApp.state.closeLauncher.title": "Devam etmek için %{launcher}'ı kapatın", "SyncApp.state.closeLauncher.body": "ARCTracker'ın %{launcher}'ı hazırlaması için kapatılması gerekiyor. Devam eden işleri kaydedin, ardından kapatın.", "SyncApp.state.launcherReady.title": "%{launcher} hazır. Oyna.", "SyncApp.state.launcherReady.body": "%{launcher}'dan ARC Raiders'ı her zaman olduğu gibi başlatın. Oynarken envanteriniz otomatik olarak senkronize olacak.", "SyncApp.state.connecting.title": "ARC Raiders hesabınız aranıyor…", "SyncApp.state.connecting.body": "ARC Raiders'ı açık tutun. Bu genellikle ana menüye ulaştıktan birkaç saniye sonra bağlanır.", + "SyncApp.state.connecting.waiting": "ARC Raiders bekleniyor…", "SyncApp.state.updating.title": "ARCTracker güncelleniyor…", "SyncApp.state.updating.body": "Neredeyse bitti. Hesabınız kaydediliyor.", + "SyncApp.state.updating.waiting": "Envanteriniz kaydediliyor…", "SyncApp.state.synced.title": "Senkronize oldunuz", "SyncApp.state.synced.body": "%{account} olarak oynanıyor. ARC Raiders envanteriniz ARCTracker'da güncel.", "SyncApp.state.synced.session": "Envanteriniz %{time} kadar senkronize kalacak.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} kendi başına yeniden başlatıldı, bu nedenle ARCTracker şu anda envanterinizi okuyamıyor. Senkronizasyonu devam ettirmek için yeniden hazırlayın.", "SyncApp.state.needsAttention.title": "Senkronizasyon dikkat gerektiriyor", "SyncApp.state.needsAttention.body": "ARCTracker Sync bağlanırken bir sorunla karşılaştı. Ayrıntılar için sorun gidermeyi açın.", + "SyncApp.state.needsAttention.npcapBody": "Yakalama yöntemi Npcap olarak ayarlanmış, ancak Npcap bu bilgisayarda yüklü değil. npcap.com'dan yükleyin veya Ayarlar'dan ham soketlere geri dönün.", "SyncApp.action.signIn": "ARCTracker'a oturum açın", "SyncApp.action.cancel": "İptal", "SyncApp.action.chooseGame": "ARC Raiders'ı seçin", "SyncApp.action.prepareLauncher": "%{launcher}'ı hazırlayın", "SyncApp.action.whatDoesThisDo": "Bu ne yapıyor?", "SyncApp.action.closeLauncher": "%{launcher}'ı kapatın", + "SyncApp.action.viewStash": "Envanterinizi görüntüleyin", "SyncApp.action.hideToTray": "Tray'e gizle", "SyncApp.action.getHelp": "Yardım alın", "SyncApp.action.tryAgain": "Tekrar deneyin", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Dil", "SyncApp.settings.displayLanguage": "Görüntüleme dili", "SyncApp.settings.matchesWindows": "Varsayılan olarak Windows ile eşleşir", + "SyncApp.settings.matchWindows": "Windows'la eşleştir", "SyncApp.settings.network": "Ağ", "SyncApp.settings.networkAdapter": "Ağ adaptörü", "SyncApp.settings.networkAdapterSub": "Otomatik seçilir. Senkronizasyon bağlanamıyorsa değiştirin.", "SyncApp.settings.refresh": "Yenile", + "SyncApp.settings.captureMethod": "Yakalama yöntemi", + "SyncApp.settings.captureMethodSub": "Sync'in bilgisayarınızın kendi oyun trafiğini nasıl okuduğu. Antivirüs yazılımı yerleşik yöntemi engelliyorsa Npcap'e geçin — VPN veya proxy kullanıyorsanız daha iyi çalışabilir.", + "SyncApp.settings.captureRawSockets": "Windows ham soketleri (yerleşik)", + "SyncApp.settings.captureNpcap": "Npcap (ayrı olarak yüklenir)", + "SyncApp.settings.npcapMissing": "Npcap yüklü değil, bu nedenle senkronizasyon trafiği yakalayamıyor. Yükleyin, ardından ana ekrandaki Tekrar deneyin düğmesini kullanın.", + "SyncApp.settings.npcapLink": "Npcap'i (ücretsiz) npcap.com adresinden edinin", "SyncApp.settings.troubleshooting": "Sorun giderme", "SyncApp.settings.activityLog": "Etkinlik günlüğü", "SyncApp.settings.activityLogSub": "Son olaylar, destek için", "SyncApp.settings.view": "Görüntüle", + "SyncApp.settings.hide": "Gizle", "SyncApp.settings.copyDiagnostics": "Tanılamaları kopyala", "SyncApp.settings.copyDiagnosticsSub": "ARCTracker desteği ile paylaş", "SyncApp.settings.copy": "Kopyala", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Güncellemeleri denetle", + "SyncApp.settings.whatIsSync": "Senkronizasyon ne yapar?", + "SyncApp.settings.quit": "ARCTracker Sync'ten çık", "SyncApp.tray.open": "ARCTracker Sync'i aç", "SyncApp.tray.pause": "Senkronizasyonu duraklat", "SyncApp.tray.resume": "Senkronizasyonu sürdür", diff --git a/locales/uk.json b/locales/uk.json index 060f00e..e514215 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "Засіб запуску готовий", "SyncApp.progress.playing": "Грає", "SyncApp.progress.synced": "Синхронізовано", + "SyncApp.phase.account.label": "Акаунт", + "SyncApp.phase.account.sub": "Підключити ARCTracker", + "SyncApp.phase.launcher.label": "Засіб запуску", + "SyncApp.phase.launcher.sub": "Підготувати %{launcher}", + "SyncApp.phase.play.label": "Гра", + "SyncApp.phase.play.sub": "Запустити ARC Raiders", + "SyncApp.phase.sync.label": "Синхронізація", + "SyncApp.phase.sync.sub": "Тримати інвентар актуальним", + "SyncApp.hero.step": "Крок %{n} з 4", "SyncApp.footer.signedInAs": "Вхід як %{account}", + "SyncApp.footer.synced": "Синхронізовано · %{account}", + "SyncApp.footer.syncedLabel": "Синхронізовано ·", "SyncApp.footer.notSignedIn": "Не увійшли", "SyncApp.footer.settings": "Параметри", "SyncApp.state.needsAdmin.title": "Один важливий дозвіл спочатку", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker перезапускає %{launcher}, щоб він міг читати ваш інвентар ARC Raiders під час гри. Ваші ігри %{launcher}, завантаження та входи залишаються незміненими.", "SyncApp.state.preparingLauncher.title": "Підготовка %{launcher}…", "SyncApp.state.preparingLauncher.body": "Закриття та повторне відкриття %{launcher}. Це займе кілька секунд.", + "SyncApp.state.preparingLauncher.waiting": "Підготовка %{launcher}…", "SyncApp.state.closeLauncher.title": "Закрийте %{launcher}, щоб продовжити", "SyncApp.state.closeLauncher.body": "%{launcher} потребує закриття, щоб ARCTracker міг його підготувати. Збережіть все, що у вас в процесі, а потім закрийте його.", "SyncApp.state.launcherReady.title": "%{launcher} готовий. Йдіть грати.", "SyncApp.state.launcherReady.body": "Запускайте ARC Raiders з %{launcher} коли завгодно. Ваш інвентар синхронізуватиметься автоматично під час гри.", "SyncApp.state.connecting.title": "Пошук вашого акаунта ARC Raiders…", "SyncApp.state.connecting.body": "Тримайте ARC Raiders відкритим. Зазвичай підключення відбувається кілька секунд після того, як ви дійдете до головного меню.", + "SyncApp.state.connecting.waiting": "Очікування ARC Raiders…", "SyncApp.state.updating.title": "Оновлення ARCTracker…", "SyncApp.state.updating.body": "Майже готово. Збереження вашого акаунта.", + "SyncApp.state.updating.waiting": "Збереження вашого інвентаря…", "SyncApp.state.synced.title": "Ви синхронізовані", "SyncApp.state.synced.body": "Грає як %{account}. Ваш інвентар ARC Raiders актуальний на ARCTracker.", "SyncApp.state.synced.session": "Ваш інвентар залишається синхронізованим до %{time}.", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} був перезапущений самостійно, тому ARCTracker не може прочитати ваш інвентар прямо зараз. Підготуйте його знову, щоб продовжити синхронізацію.", "SyncApp.state.needsAttention.title": "Синхронізація потребує уваги", "SyncApp.state.needsAttention.body": "ARCTracker Sync зіткнувся з проблемою підключення. Відкрийте усунення несправностей для отримання подробиць.", + "SyncApp.state.needsAttention.npcapBody": "Метод захоплення встановлено на Npcap, але Npcap не встановлений на цьому ПК. Встановіть його з npcap.com або поверніться до сирих сокетів у параметрах.", "SyncApp.action.signIn": "Увійти до ARCTracker", "SyncApp.action.cancel": "Скасувати", "SyncApp.action.chooseGame": "Виберіть ARC Raiders", "SyncApp.action.prepareLauncher": "Підготувати %{launcher}", "SyncApp.action.whatDoesThisDo": "Що це робить?", "SyncApp.action.closeLauncher": "Закрити %{launcher}", + "SyncApp.action.viewStash": "Переглянути ваш інвентар", "SyncApp.action.hideToTray": "Сховати в трей", "SyncApp.action.getHelp": "Отримати допомогу", "SyncApp.action.tryAgain": "Спробуйте ще раз", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "Мова", "SyncApp.settings.displayLanguage": "Мова інтерфейсу", "SyncApp.settings.matchesWindows": "За замовчуванням відповідає Windows", + "SyncApp.settings.matchWindows": "Відповідати Windows", "SyncApp.settings.network": "Мережа", "SyncApp.settings.networkAdapter": "Мережевий адаптер", "SyncApp.settings.networkAdapterSub": "Автоматично вибирається. Змініть, тільки якщо синхронізація не підключається.", "SyncApp.settings.refresh": "Оновити", + "SyncApp.settings.captureMethod": "Метод захоплення", + "SyncApp.settings.captureMethodSub": "Як Sync читає власний ігровий трафік вашого ПК. Переходьте на Npcap, якщо антивірус блокує вбудований метод — це також може працювати краще, якщо ви використовуєте VPN або проксі.", + "SyncApp.settings.captureRawSockets": "Сирі сокети Windows (вбудовано)", + "SyncApp.settings.captureNpcap": "Npcap (встановлюється окремо)", + "SyncApp.settings.npcapMissing": "Npcap не встановлений, тому синхронізація не може захоплювати трафік. Встановіть його, а потім натисніть «Спробуйте ще раз» на головному екрані.", + "SyncApp.settings.npcapLink": "Отримати Npcap (безкоштовно) на npcap.com", "SyncApp.settings.troubleshooting": "Усунення несправностей", "SyncApp.settings.activityLog": "Журнал активності", "SyncApp.settings.activityLogSub": "Недавні події для підтримки", "SyncApp.settings.view": "Переглянути", + "SyncApp.settings.hide": "Сховати", "SyncApp.settings.copyDiagnostics": "Скопіювати діагностику", "SyncApp.settings.copyDiagnosticsSub": "Поділіться з підтримкою ARCTracker", "SyncApp.settings.copy": "Скопіювати", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "Перевірити наявність оновлень", + "SyncApp.settings.whatIsSync": "Що робить синхронізація?", + "SyncApp.settings.quit": "Завершити ARCTracker Sync", "SyncApp.tray.open": "Відкрити ARCTracker Sync", "SyncApp.tray.pause": "Призупинити синхронізацію", "SyncApp.tray.resume": "Відновити синхронізацію", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 8db26e3..24ccf61 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "启动器已准备好", "SyncApp.progress.playing": "游戏中", "SyncApp.progress.synced": "已同步", + "SyncApp.phase.account.label": "账户", + "SyncApp.phase.account.sub": "连接 ARCTracker", + "SyncApp.phase.launcher.label": "启动器", + "SyncApp.phase.launcher.sub": "准备 %{launcher}", + "SyncApp.phase.play.label": "玩", + "SyncApp.phase.play.sub": "启动 ARC Raiders", + "SyncApp.phase.sync.label": "同步", + "SyncApp.phase.sync.sub": "保持库存最新", + "SyncApp.hero.step": "第 %{n} 步,共 4 步", "SyncApp.footer.signedInAs": "已登录为 %{account}", + "SyncApp.footer.synced": "已同步 · %{account}", + "SyncApp.footer.syncedLabel": "已同步 ·", "SyncApp.footer.notSignedIn": "未登录", "SyncApp.footer.settings": "设置", "SyncApp.state.needsAdmin.title": "需要权限", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker 会重启 %{launcher},这样它就可以在你游戏时读取你的 ARC Raiders 物品栏。你的 %{launcher} 游戏、下载和登录状态都不会受影响。", "SyncApp.state.preparingLauncher.title": "正在准备 %{launcher}…", "SyncApp.state.preparingLauncher.body": "正在关闭并重新打开 %{launcher}。这需要几秒钟。", + "SyncApp.state.preparingLauncher.waiting": "正在准备 %{launcher}…", "SyncApp.state.closeLauncher.title": "关闭 %{launcher} 继续", "SyncApp.state.closeLauncher.body": "%{launcher} 需要关闭,这样 ARCTracker 才能对其进行准备。保存正在进行的任何内容,然后关闭它。", "SyncApp.state.launcherReady.title": "%{launcher}已准备好。去玩吧。", "SyncApp.state.launcherReady.body": "随时从%{launcher}启动ARC Raiders。你的物品栏在游戏时会自动同步。", "SyncApp.state.connecting.title": "正在查找你的 ARC Raiders 账户…", "SyncApp.state.connecting.body": "保持 ARC Raiders 打开。通常在你进入主菜单后的几秒钟内会连接。", + "SyncApp.state.connecting.waiting": "正在等待 ARC Raiders…", "SyncApp.state.updating.title": "正在更新 ARCTracker…", "SyncApp.state.updating.body": "快完成了。正在保存你的账户。", + "SyncApp.state.updating.waiting": "正在保存你的库存…", "SyncApp.state.synced.title": "你已同步", "SyncApp.state.synced.body": "正在以 %{account} 的身份游玩。ARCTracker 正在保持你的 ARC Raiders 物品栏最新。", "SyncApp.state.synced.session": "你的物品栏将一直保持同步到 %{time}。", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} 被自动重启了,所以 ARCTracker 目前无法读取你的物品栏。再次准备它以继续同步。", "SyncApp.state.needsAttention.title": "同步需要关注", "SyncApp.state.needsAttention.body": "ARCTracker Sync 在连接时遇到问题。打开故障排除以查看详细信息。", + "SyncApp.state.needsAttention.npcapBody": "捕获方式已设置为 Npcap,但这台电脑上没有安装 Npcap。请从 npcap.com 安装,或在设置中切换回原始套接字。", "SyncApp.action.signIn": "登录 ARCTracker", "SyncApp.action.cancel": "取消", "SyncApp.action.chooseGame": "选择 ARC Raiders", "SyncApp.action.prepareLauncher": "准备 %{launcher}", "SyncApp.action.whatDoesThisDo": "这是做什么的?", "SyncApp.action.closeLauncher": "关闭 %{launcher}", + "SyncApp.action.viewStash": "查看你的库存", "SyncApp.action.hideToTray": "隐藏到系统托盘", "SyncApp.action.getHelp": "获取帮助", "SyncApp.action.tryAgain": "重试", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "语言", "SyncApp.settings.displayLanguage": "显示语言", "SyncApp.settings.matchesWindows": "默认与 Windows 匹配", + "SyncApp.settings.matchWindows": "与 Windows 匹配", "SyncApp.settings.network": "网络", "SyncApp.settings.networkAdapter": "网络适配器", "SyncApp.settings.networkAdapterSub": "自动选择。仅在同步无法连接时更改。", "SyncApp.settings.refresh": "刷新", + "SyncApp.settings.captureMethod": "捕获方式", + "SyncApp.settings.captureMethodSub": "Sync 读取你电脑自身游戏流量的方式。如果杀毒软件拦截内置方式,切换到 Npcap — 使用 VPN 或代理时可能也会工作得更好。", + "SyncApp.settings.captureRawSockets": "Windows 原始套接字(内置)", + "SyncApp.settings.captureNpcap": "Npcap(需单独安装)", + "SyncApp.settings.npcapMissing": "Npcap 未安装,同步无法捕获流量。安装后,在主界面使用“重试”。", + "SyncApp.settings.npcapLink": "前往 npcap.com 获取 Npcap(免费)", "SyncApp.settings.troubleshooting": "故障排除", "SyncApp.settings.activityLog": "活动日志", "SyncApp.settings.activityLogSub": "最近的事件,用于支持", "SyncApp.settings.view": "查看", + "SyncApp.settings.hide": "隐藏", "SyncApp.settings.copyDiagnostics": "复制诊断信息", "SyncApp.settings.copyDiagnosticsSub": "与 ARCTracker 支持分享", "SyncApp.settings.copy": "复制", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "检查更新", + "SyncApp.settings.whatIsSync": "同步是做什么的?", + "SyncApp.settings.quit": "退出 ARCTracker Sync", "SyncApp.tray.open": "打开 ARCTracker Sync", "SyncApp.tray.pause": "暂停同步", "SyncApp.tray.resume": "恢复同步", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 75bbc89..e1594dc 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -7,7 +7,18 @@ "SyncApp.progress.launcherReady": "啟動器已準備好", "SyncApp.progress.playing": "正在遊玩", "SyncApp.progress.synced": "已同步", + "SyncApp.phase.account.label": "帳號", + "SyncApp.phase.account.sub": "連接 ARCTracker", + "SyncApp.phase.launcher.label": "啟動器", + "SyncApp.phase.launcher.sub": "準備 %{launcher}", + "SyncApp.phase.play.label": "遊玩", + "SyncApp.phase.play.sub": "啟動 ARC Raiders", + "SyncApp.phase.sync.label": "同步", + "SyncApp.phase.sync.sub": "保持物品欄最新", + "SyncApp.hero.step": "第 %{n} 步,共 4 步", "SyncApp.footer.signedInAs": "登入帳號:%{account}", + "SyncApp.footer.synced": "已同步 · %{account}", + "SyncApp.footer.syncedLabel": "已同步 ·", "SyncApp.footer.notSignedIn": "未登入", "SyncApp.footer.settings": "設定", "SyncApp.state.needsAdmin.title": "請先授予權限", @@ -22,14 +33,17 @@ "SyncApp.state.prepareLauncher.body": "ARCTracker 將重新啟動 %{launcher},以便在遊玩時讀取你的 ARC Raiders 物品欄。你的 %{launcher} 遊戲、下載和登入不會受影響。", "SyncApp.state.preparingLauncher.title": "正在準備 %{launcher}…", "SyncApp.state.preparingLauncher.body": "正在關閉並重新開啟 %{launcher}。這將花費幾秒鐘。", + "SyncApp.state.preparingLauncher.waiting": "正在準備 %{launcher}…", "SyncApp.state.closeLauncher.title": "關閉 %{launcher} 以繼續", "SyncApp.state.closeLauncher.body": "ARCTracker 需要關閉 %{launcher} 來進行準備。請儲存進度中的任何內容,然後關閉它。", "SyncApp.state.launcherReady.title": "%{launcher} 已準備好。開始遊玩。", "SyncApp.state.launcherReady.body": "隨時從 %{launcher} 啟動 ARC Raiders。你的物品欄將在遊玩時自動同步。", "SyncApp.state.connecting.title": "正在尋找你的 ARC Raiders 帳號…", "SyncApp.state.connecting.body": "保持 ARC Raiders 開啟。通常在你到達主選單後幾秒鐘內會連接。", + "SyncApp.state.connecting.waiting": "正在等待 ARC Raiders…", "SyncApp.state.updating.title": "正在更新 ARCTracker…", "SyncApp.state.updating.body": "快完成了。正在儲存你的帳號。", + "SyncApp.state.updating.waiting": "正在儲存你的物品欄…", "SyncApp.state.synced.title": "已同步", "SyncApp.state.synced.body": "以 %{account} 身分遊玩中。你的 ARC Raiders 物品欄在 ARCTracker 上已是最新狀態。", "SyncApp.state.synced.session": "你的物品欄將保持同步至 %{time}。", @@ -39,12 +53,14 @@ "SyncApp.state.needsLauncher.body": "%{launcher} 已自行重新啟動,所以 ARCTracker 現在無法讀取你的物品欄。再次準備它以繼續同步。", "SyncApp.state.needsAttention.title": "同步需要關注", "SyncApp.state.needsAttention.body": "ARCTracker Sync 在連接時遇到問題。開啟疑難排解以查看詳細信息。", + "SyncApp.state.needsAttention.npcapBody": "擷取方式已設定為 Npcap,但這台電腦尚未安裝 Npcap。請從 npcap.com 安裝,或在設定中切換回原始通訊端。", "SyncApp.action.signIn": "登入 ARCTracker", "SyncApp.action.cancel": "取消", "SyncApp.action.chooseGame": "選擇 ARC Raiders", "SyncApp.action.prepareLauncher": "準備 %{launcher}", "SyncApp.action.whatDoesThisDo": "這有什麼作用?", "SyncApp.action.closeLauncher": "關閉 %{launcher}", + "SyncApp.action.viewStash": "檢視你的物品欄", "SyncApp.action.hideToTray": "隱藏到系統匣", "SyncApp.action.getHelp": "取得幫助", "SyncApp.action.tryAgain": "重試", @@ -67,19 +83,29 @@ "SyncApp.settings.language": "語言", "SyncApp.settings.displayLanguage": "顯示語言", "SyncApp.settings.matchesWindows": "預設與 Windows 相符", + "SyncApp.settings.matchWindows": "與 Windows 相符", "SyncApp.settings.network": "網路", "SyncApp.settings.networkAdapter": "網路介面卡", "SyncApp.settings.networkAdapterSub": "自動選擇。僅在同步無法連接時變更。", "SyncApp.settings.refresh": "重新整理", + "SyncApp.settings.captureMethod": "擷取方式", + "SyncApp.settings.captureMethodSub": "Sync 讀取你電腦自身遊戲流量的方式。如果防毒軟體封鎖內建方式,切換到 Npcap — 使用 VPN 或代理時也可能運作得更好。", + "SyncApp.settings.captureRawSockets": "Windows 原始通訊端(內建)", + "SyncApp.settings.captureNpcap": "Npcap(需另外安裝)", + "SyncApp.settings.npcapMissing": "Npcap 尚未安裝,因此同步無法擷取流量。安裝後,請在主畫面使用「重試」。", + "SyncApp.settings.npcapLink": "前往 npcap.com 取得 Npcap(免費)", "SyncApp.settings.troubleshooting": "疑難排解", "SyncApp.settings.activityLog": "活動日誌", "SyncApp.settings.activityLogSub": "最近的事件,用於支援", "SyncApp.settings.view": "檢視", + "SyncApp.settings.hide": "隱藏", "SyncApp.settings.copyDiagnostics": "複製診斷資訊", "SyncApp.settings.copyDiagnosticsSub": "與 ARCTracker 支援小組分享", "SyncApp.settings.copy": "複製", "SyncApp.settings.version": "ARCTracker Sync v%{version}", "SyncApp.settings.checkForUpdates": "檢查更新", + "SyncApp.settings.whatIsSync": "同步有什麼作用?", + "SyncApp.settings.quit": "結束 ARCTracker Sync", "SyncApp.tray.open": "開啟 ARCTracker Sync", "SyncApp.tray.pause": "暫停同步", "SyncApp.tray.resume": "繼續同步", diff --git a/src/app.rs b/src/app.rs index 6bbbcb8..573ef27 100644 --- a/src/app.rs +++ b/src/app.rs @@ -12,12 +12,14 @@ use eframe::egui::{ use crate::auth_bridge; use crate::capture::{self, CaptureEvent, CaptureHandle, CaptureStats, InterfaceInfo}; +use crate::capture_backend::CaptureMethod; use crate::config::{self, AppConfig}; use crate::credential_store; use crate::elevation; use crate::fonts; use crate::i18n; use crate::launch::{self, LauncherPlatform, LauncherStatus}; +use crate::npcap::Npcap; use crate::single_instance; use crate::sync_client::{self, SubmitError, SubmitResponse, BASE_URL}; use crate::theme::{ @@ -47,6 +49,8 @@ const GAME_PROCESS_NAMES: &[&str] = &["PioneerGame.exe", "PioneerGame-e.exe", "P const HELP_URL: &str = "https://arctracker.io/help/sync"; /// Where a synced user goes to view their inventory on the web app. const STASH_URL: &str = "https://arctracker.io/stash"; +/// Where the user installs Npcap from; never bundled or downloaded by the app. +const NPCAP_URL: &str = "https://npcap.com/#download"; /// Refresh the bridge token when fewer than this many days remain. const REFRESH_THRESHOLD_DAYS: i64 = 7; const REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60); @@ -213,6 +217,10 @@ extern "system" { pub struct ArcTrackerSyncApp { config: AppConfig, locale: String, + /// True once the heavier all-CJK font stack has been loaded so the language + /// picker renders every native name. Loaded lazily the first time that + /// dropdown opens, then kept for the session so reopening never flickers. + picker_fonts_loaded: bool, screen: Screen, show_activity_log: bool, show_explainer: bool, @@ -260,6 +268,10 @@ pub struct ArcTrackerSyncApp { capture: Option, capture_blocked: bool, + /// TTL-cached `Npcap::is_installed()`; `None` until first probed. Only + /// maintained while the Npcap capture method is selected. + npcap_available: Option, + last_npcap_check: Instant, stats: CaptureStats, latest_token: Option, auth_token: Option, @@ -381,6 +393,7 @@ impl ArcTrackerSyncApp { let mut app = Self { config, locale, + picker_fonts_loaded: false, screen: Screen::Hub, show_activity_log: false, show_explainer: false, @@ -416,6 +429,11 @@ impl ArcTrackerSyncApp { .unwrap_or_else(Instant::now), capture: None, capture_blocked: false, + npcap_available: None, + // Backdated so the first frame probes immediately. + last_npcap_check: Instant::now() + .checked_sub(Duration::from_secs(10)) + .unwrap_or_else(Instant::now), stats: CaptureStats::default(), latest_token: None, auth_token, @@ -603,11 +621,34 @@ impl ArcTrackerSyncApp { self.save_config(); self.locale = resolved; i18n::set_active_locale(&self.locale); - fonts::apply_locale(ctx, &self.locale); + self.apply_locale_fonts(ctx); self.tray_tooltip = String::new(); self.update_tray_tooltip(); } + /// Rebuild the font stack for the active locale, preserving the all-CJK set + /// if the language picker has already pulled it in this session. + fn apply_locale_fonts(&self, ctx: &egui::Context) { + if self.picker_fonts_loaded { + fonts::apply_locale_with_all_cjk(ctx, &self.locale); + } else { + fonts::apply_locale(ctx, &self.locale); + } + } + + /// Load the all-CJK font stack the first time the language picker opens, so + /// the non-active CJK native names stop rendering as tofu. + fn ensure_picker_fonts(&mut self, ctx: &egui::Context) { + if self.picker_fonts_loaded { + return; + } + self.picker_fonts_loaded = true; + fonts::apply_locale_with_all_cjk(ctx, &self.locale); + // The new fonts apply next frame; repaint so the just-opened picker + // shows the native names without waiting for the next input event. + ctx.request_repaint(); + } + // ----- silent refresh ---------------------------------------------------------- fn maybe_refresh_auth_on_launch(&mut self) { @@ -644,6 +685,7 @@ impl ArcTrackerSyncApp { self.maybe_retry_token_submission(); self.poll_refresh(); self.refresh_launcher_readiness_if_needed(); + self.refresh_npcap_available_if_needed(); self.refresh_game_running_if_needed(); self.maybe_refresh_auth_on_timer(); self.maybe_check_for_update(); @@ -952,7 +994,11 @@ impl ArcTrackerSyncApp { ), HubState::NeedsAttention => ( tr!("SyncApp.state.needsAttention.title"), - tr!("SyncApp.state.needsAttention.body"), + if self.npcap_known_missing() { + tr!("SyncApp.state.needsAttention.npcapBody") + } else { + tr!("SyncApp.state.needsAttention.body") + }, ), } } @@ -1139,6 +1185,29 @@ impl ArcTrackerSyncApp { } } + /// TTL-gated `Npcap::is_installed()` probe; only runs while the Npcap + /// capture method is selected. The 2s re-check means the "Npcap missing" + /// warnings clear on their own shortly after the user installs it. + fn refresh_npcap_available_if_needed(&mut self) { + if self.config.capture_method != CaptureMethod::Npcap { + return; + } + if self.npcap_available.is_some() + && self.last_npcap_check.elapsed() < Duration::from_secs(2) + { + return; + } + self.npcap_available = Some(Npcap::is_installed()); + self.last_npcap_check = Instant::now(); + } + + /// True when the user selected Npcap but it is not installed — the one + /// capture-blocked cause the app can name precisely (detected by state, + /// never by parsing error strings). + fn npcap_known_missing(&self) -> bool { + self.config.capture_method == CaptureMethod::Npcap && self.npcap_available == Some(false) + } + fn refresh_game_running(&mut self) { self.game_running = GAME_PROCESS_NAMES.iter().any(|name| { crate::process_env::find_processes(name) @@ -1399,7 +1468,11 @@ impl ArcTrackerSyncApp { self.stats = CaptureStats::default(); self.latest_token = None; - self.capture = Some(capture::start_capture(interface_name, sync_key_path)); + self.capture = Some(capture::start_capture( + self.config.capture_method, + interface_name, + sync_key_path, + )); } fn capture_settings_changed(&mut self) { @@ -1692,6 +1765,14 @@ impl ArcTrackerSyncApp { format!("Launcher detail: {}", self.launcher_readiness.detail), format!("Game running: {}", self.game_running), format!("Capture ready: {}", self.capture_ready()), + format!("Capture method: {}", self.config.capture_method.label()), + format!( + "Npcap: {}", + match Npcap::load() { + Ok(npcap) => npcap.lib_version(), + Err(_) => "not installed".to_string(), + } + ), format!("Account synced: {}", self.token_submitted), format!("Inventory sync enabled: {}", self.sync_enabled), format!("Connection active: {}", self.capture.is_some()), @@ -2447,8 +2528,14 @@ impl ArcTrackerSyncApp { } } HubState::NeedsAttention => { + if self.npcap_known_missing() { + ui.hyperlink_to(tr!("SyncApp.settings.npcapLink"), NPCAP_URL); + ui.add_space(4.0); + } if primary_button(ui, &tr!("SyncApp.action.tryAgain")) { self.capture_blocked = false; + // Re-probe for Npcap in case the user just installed it. + self.npcap_available = None; // Resume submission if a prior episode hard-stopped, then // re-attempt with the token we already have. self.reset_submit_retry_state(); @@ -2737,7 +2824,7 @@ impl ArcTrackerSyncApp { }; let mut chosen: Option> = None; - egui::ComboBox::from_id_salt("settings_language_combo") + let combo = egui::ComboBox::from_id_salt("settings_language_combo") .selected_text(current_label) .show_ui(ui, |ui| { if ui @@ -2758,10 +2845,17 @@ impl ArcTrackerSyncApp { chosen = Some(Some(locale.to_string())); } } - }) + }); + combo .response .on_hover_cursor(egui::CursorIcon::PointingHand); + // The popup is open this frame (the menu closure ran), so + // pull in the CJK fonts the native-name list needs. + if combo.inner.is_some() { + self.ensure_picker_fonts(ctx); + } + if let Some(language) = chosen { self.change_language(ctx, language); } @@ -2825,6 +2919,63 @@ impl ArcTrackerSyncApp { if refresh { self.refresh_interfaces(); self.refresh_sync_key_source(); + self.npcap_available = None; + } + + hairline(ui); + + let mut chosen_method: Option = None; + settings_row( + ui, + &tr!("SyncApp.settings.captureMethod"), + &tr!("SyncApp.settings.captureMethodSub"), + |ui| { + ui.spacing_mut().interact_size.y = 30.0; + let current_label = match self.config.capture_method { + CaptureMethod::RawSocket => tr!("SyncApp.settings.captureRawSockets"), + CaptureMethod::Npcap => tr!("SyncApp.settings.captureNpcap"), + }; + egui::ComboBox::from_id_salt("settings_capture_method_combo") + .selected_text(current_label) + .show_ui(ui, |ui| { + for method in [CaptureMethod::RawSocket, CaptureMethod::Npcap] { + let label = match method { + CaptureMethod::RawSocket => { + tr!("SyncApp.settings.captureRawSockets") + } + CaptureMethod::Npcap => tr!("SyncApp.settings.captureNpcap"), + }; + if ui + .selectable_label(self.config.capture_method == method, label) + .clicked() + && self.config.capture_method != method + { + chosen_method = Some(method); + } + } + }) + .response + .on_hover_cursor(egui::CursorIcon::PointingHand); + }, + ); + + if let Some(method) = chosen_method { + self.config.capture_method = method; + self.save_config(); + self.npcap_available = None; + self.capture_settings_changed(); + } + + if self.npcap_known_missing() { + ui.label( + RichText::new(tr!("SyncApp.settings.npcapMissing")) + .size(theme::TEXT_CAPTION) + .color(arc_warning()), + ); + ui.hyperlink_to( + RichText::new(tr!("SyncApp.settings.npcapLink")).size(theme::TEXT_CAPTION), + NPCAP_URL, + ); } }); } diff --git a/src/capture.rs b/src/capture.rs index 332770d..e12c02c 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -19,6 +19,8 @@ use pcapsql_core::stream::{ use pcapsql_core::tls::KeyLog as SyncKeyData; use sha2::{Digest, Sha256}; +use crate::capture_backend::{CaptureMethod, Packet}; +use crate::npcap::Npcap; use crate::packet::{datalink_name, parse_tcp_segment, CapturedSegment}; use crate::rawsock::RawSock; use crate::token::{self, RawTokenHit, TokenObservation}; @@ -147,13 +149,69 @@ pub fn list_interfaces() -> Result> { .collect()) } -pub fn start_capture(interface_name: String, sync_key_path: PathBuf) -> CaptureHandle { +/// The open capture handle for whichever backend the user selected. Both +/// backends expose the same pcap-shaped surface, so `capture_loop` is +/// backend-agnostic past this point. +enum LiveCapture { + Raw(crate::rawsock::Capture), + Npcap(crate::npcap::Capture), +} + +impl LiveCapture { + fn next_packet(&mut self) -> Result> { + match self { + LiveCapture::Raw(capture) => capture.next_packet(), + LiveCapture::Npcap(capture) => capture.next_packet(), + } + } + + fn datalink(&self) -> Result { + match self { + LiveCapture::Raw(capture) => capture.datalink(), + LiveCapture::Npcap(capture) => capture.datalink(), + } + } + + fn set_filter(&mut self, expression: &str) -> Result<()> { + match self { + LiveCapture::Raw(capture) => capture.set_filter(expression), + LiveCapture::Npcap(capture) => capture.set_filter(expression), + } + } +} + +fn open_capture(method: CaptureMethod, interface_name: &str) -> Result { + match method { + CaptureMethod::RawSocket => Ok(LiveCapture::Raw( + RawSock::load()? + .open_live(interface_name) + .with_context(|| format!("opening raw-socket capture on {interface_name}"))?, + )), + CaptureMethod::Npcap => Ok(LiveCapture::Npcap( + Npcap::load()? + .open_adapter(interface_name) + .with_context(|| format!("opening Npcap capture on {interface_name}"))?, + )), + } +} + +pub fn start_capture( + method: CaptureMethod, + interface_name: String, + sync_key_path: PathBuf, +) -> CaptureHandle { let (tx, rx) = unbounded(); let stop = Arc::new(AtomicBool::new(false)); let thread_stop = Arc::clone(&stop); let worker = thread::spawn(move || { - if let Err(error) = capture_loop(interface_name, sync_key_path, thread_stop, tx.clone()) { + if let Err(error) = capture_loop( + method, + interface_name, + sync_key_path, + thread_stop, + tx.clone(), + ) { let _ = tx.send(CaptureEvent::Error(format!("{error:#}"))); } let _ = tx.send(CaptureEvent::Stopped); @@ -167,6 +225,7 @@ pub fn start_capture(interface_name: String, sync_key_path: PathBuf) -> CaptureH } fn capture_loop( + method: CaptureMethod, interface_name: String, sync_key_path: PathBuf, stop: Arc, @@ -195,18 +254,20 @@ fn capture_loop( let mut manager = build_manager(sync_keys); let _ = tx.send(CaptureEvent::Status(format!( - "Opening capture interface {interface_name}" + "Opening capture interface {interface_name} via {}", + method.label() ))); // Raw-socket inbound capture is blocked by the Windows Firewall; add an - // inbound allow rule for our exe first (best-effort). - if let Err(error) = crate::firewall::ensure_capture_allowed() { - let _ = tx.send(CaptureEvent::Status(format!( - "Could not add firewall allowance (inbound may be blocked): {error}" - ))); + // inbound allow rule for our exe first (best-effort). Npcap captures at + // the NDIS layer below the firewall, so no rule is needed there. + if method == CaptureMethod::RawSocket { + if let Err(error) = crate::firewall::ensure_capture_allowed() { + let _ = tx.send(CaptureEvent::Status(format!( + "Could not add firewall allowance (inbound may be blocked): {error}" + ))); + } } - let mut capture: crate::rawsock::Capture = RawSock::load()? - .open_live(&interface_name) - .with_context(|| format!("opening raw-socket capture on {interface_name}"))?; + let mut capture = open_capture(method, &interface_name)?; capture .set_filter(LIVE_BPF_FILTER) .with_context(|| format!("installing capture filter {LIVE_BPF_FILTER:?}"))?; diff --git a/src/capture_backend.rs b/src/capture_backend.rs index 4615be6..e780843 100644 --- a/src/capture_backend.rs +++ b/src/capture_backend.rs @@ -1,4 +1,28 @@ -//! Types the [`crate::rawsock`] backend produces for `capture.rs` to consume. +//! Types shared by the capture backends ([`crate::rawsock`] and +//! [`crate::npcap`]) and consumed by `capture.rs`. + +use serde::{Deserialize, Serialize}; + +/// Which packet-capture backend drives the live capture. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +pub enum CaptureMethod { + /// Windows raw sockets (`SIO_RCVALL`). Built in, nothing extra to install. + #[default] + RawSocket, + /// Npcap (`wpcap.dll`), installed separately by the user from npcap.com. + Npcap, +} + +impl CaptureMethod { + /// English label for diagnostics and activity-log status lines (those are + /// intentionally not localized). + pub fn label(self) -> &'static str { + match self { + CaptureMethod::RawSocket => "raw sockets", + CaptureMethod::Npcap => "Npcap", + } + } +} /// A capture-able network interface, as shown in the adapter picker. #[derive(Debug, Clone, Default)] diff --git a/src/config.rs b/src/config.rs index e9d38b4..8c008bc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,7 @@ use anyhow::{Context, Result}; use directories::ProjectDirs; use serde::{Deserialize, Serialize}; +use crate::capture_backend::CaptureMethod; use crate::launch::LauncherPlatform; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -20,6 +21,9 @@ pub struct AppConfig { /// Whether closing the window hides to tray instead of quitting (default on). #[serde(default = "default_keep_in_tray")] pub keep_in_tray: bool, + /// Packet-capture backend. Raw sockets unless the user opts into Npcap. + #[serde(default)] + pub capture_method: CaptureMethod, } fn default_keep_in_tray() -> bool { @@ -34,6 +38,7 @@ impl Default for AppConfig { platform: LauncherPlatform::default(), language: None, keep_in_tray: default_keep_in_tray(), + capture_method: CaptureMethod::default(), } } } @@ -289,6 +294,7 @@ mod tests { platform: LauncherPlatform::Steam, language: Some("de".to_string()), keep_in_tray: false, + capture_method: CaptureMethod::Npcap, }; let json = serde_json::to_string(&config).expect("serialize config"); @@ -301,6 +307,7 @@ mod tests { assert_eq!(decoded.platform, LauncherPlatform::Steam); assert_eq!(decoded.language.as_deref(), Some("de")); assert!(!decoded.keep_in_tray); + assert_eq!(decoded.capture_method, CaptureMethod::Npcap); } #[test] @@ -311,5 +318,6 @@ mod tests { let legacy: AppConfig = serde_json::from_str("{}").expect("deserialize legacy config"); assert!(legacy.keep_in_tray); assert!(legacy.language.is_none()); + assert_eq!(legacy.capture_method, CaptureMethod::RawSocket); } } diff --git a/src/fonts.rs b/src/fonts.rs index 75406d1..9ef130f 100644 --- a/src/fonts.rs +++ b/src/fonts.rs @@ -5,6 +5,12 @@ //! Windows UI font (Malgun Gothic / Microsoft YaHei / Microsoft JhengHei / Yu //! Gothic) and chain it behind Segoe UI (Latin + Cyrillic + Hebrew coverage). //! The chain is rebuilt whenever the active language changes. +//! +//! By default only the active locale's CJK font loads, keeping the footprint +//! small. The language picker, though, lists every locale's native name +//! (한국어, 简体中文, 繁體中文, 日本語), which needs all the CJK fonts at once; +//! those tens of megabytes load lazily, only once that dropdown is opened, via +//! [`apply_locale_with_all_cjk`]. use std::sync::Arc; @@ -22,6 +28,28 @@ const SEGOE_UI: FontCandidate = FontCandidate { files: &["segoeui.ttf"], }; +/// The CJK locales with their own fallback font, in language-picker order. +const CJK_LOCALES: [&str; 4] = ["ko", "zh-CN", "zh-TW", "ja"]; + +/// The CJK locales to load fonts for. The active locale (if CJK) always comes +/// first so its regional Han glyphs take priority in body text. When +/// `include_all` is set — for the language picker, which lists every locale's +/// native name — the remaining CJK locales follow so those names also render. +fn cjk_load_order(active: &str, include_all: bool) -> Vec<&'static str> { + let mut order: Vec<&'static str> = Vec::new(); + if let Some(found) = CJK_LOCALES.iter().copied().find(|locale| *locale == active) { + order.push(found); + } + if include_all { + for locale in CJK_LOCALES { + if locale != active { + order.push(locale); + } + } + } + order +} + /// Locale-specific CJK fallback fonts, in the order egui should consult them. fn locale_fallbacks(locale: &str) -> &'static [FontCandidate] { match locale { @@ -51,18 +79,34 @@ fn locale_fallbacks(locale: &str) -> &'static [FontCandidate] { } } -/// Build a [`FontDefinitions`] that starts with the locale's preferred fonts -/// and falls back through Segoe UI to egui's built-in fonts, so missing system -/// fonts never produce blank text. +/// Build a [`FontDefinitions`] for the active locale: Segoe UI plus the +/// locale's own CJK font, falling back to egui's built-in fonts so missing +/// system fonts never produce blank text. pub fn definitions_for_locale(locale: &str) -> FontDefinitions { + build_definitions(locale, false) +} + +/// Like [`definitions_for_locale`], but also loads every other CJK locale's +/// font so the language picker can render all of the native names. Heavier +/// (tens of MB), so it is applied lazily — only once the picker is opened. +pub fn definitions_for_locale_with_all_cjk(locale: &str) -> FontDefinitions { + build_definitions(locale, true) +} + +fn build_definitions(locale: &str, include_all_cjk: bool) -> FontDefinitions { let mut fonts = FontDefinitions::default(); let mut chain: Vec = Vec::new(); // Latin / Cyrillic / Hebrew base first so western text stays crisp. register_candidate(&mut fonts, &SEGOE_UI, &mut chain); - for candidate in locale_fallbacks(locale) { - register_candidate(&mut fonts, candidate, &mut chain); + // The active locale's CJK font first (for correct regional glyphs in body + // text); for the picker, the remaining CJK fonts follow so every native + // name renders. `register_candidate` dedupes. + for cjk_locale in cjk_load_order(locale, include_all_cjk) { + for candidate in locale_fallbacks(cjk_locale) { + register_candidate(&mut fonts, candidate, &mut chain); + } } if !chain.is_empty() { @@ -122,3 +166,44 @@ fn windows_fonts_dir() -> Option { pub fn apply_locale(ctx: &egui::Context, locale: &str) { ctx.set_fonts(definitions_for_locale(locale)); } + +/// Apply the active locale's fonts plus every CJK fallback, so the language +/// picker renders all native names. Call once the picker is opened. +pub fn apply_locale_with_all_cjk(ctx: &egui::Context, locale: &str) { + ctx.set_fonts(definitions_for_locale_with_all_cjk(locale)); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_load_order_is_active_locale_only() { + // The lazy default keeps the footprint small: a non-CJK locale loads no + // CJK font, and a CJK locale loads only its own. + assert!(cjk_load_order("en", false).is_empty()); + assert_eq!(cjk_load_order("ja", false), ["ja"]); + assert_eq!(cjk_load_order("zh-CN", false), ["zh-CN"]); + } + + #[test] + fn picker_load_order_is_all_cjk_with_active_first() { + // When the language picker opens it lists 한국어, 简体中文, 繁體中文, + // and 日本語, so every CJK font must load. + assert_eq!(cjk_load_order("en", true), ["ko", "zh-CN", "zh-TW", "ja"]); + + // The active CJK locale leads so its regional Han glyphs win in body + // text; the rest follow so the other native names still render. + assert_eq!( + cjk_load_order("zh-TW", true), + ["zh-TW", "ko", "zh-CN", "ja"] + ); + assert_eq!(cjk_load_order("ja", true), ["ja", "ko", "zh-CN", "zh-TW"]); + + // Every CJK locale is present exactly once regardless of the active one. + let mut deduped = cjk_load_order("ko", true); + deduped.sort_unstable(); + deduped.dedup(); + assert_eq!(deduped, ["ja", "ko", "zh-CN", "zh-TW"]); + } +} diff --git a/src/i18n.rs b/src/i18n.rs index ca28704..81a9c3d 100644 --- a/src/i18n.rs +++ b/src/i18n.rs @@ -238,5 +238,14 @@ mod tests { ), "Signed in as Matt" ); + // The capture-method settings keys must resolve to real text. + assert_eq!( + __translate("SyncApp.settings.captureMethod", &[]), + "Capture method" + ); + assert_ne!( + __translate("SyncApp.state.needsAttention.npcapBody", &[]), + "SyncApp.state.needsAttention.npcapBody" + ); } } diff --git a/src/lib.rs b/src/lib.rs index 4aea6f7..4556e26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub mod firewall; pub mod fonts; pub mod i18n; pub mod launch; +pub mod npcap; pub mod packet; pub mod process_env; pub mod rawsock; diff --git a/src/npcap.rs b/src/npcap.rs new file mode 100644 index 0000000..849f312 --- /dev/null +++ b/src/npcap.rs @@ -0,0 +1,555 @@ +//! Optional packet-capture backend using Npcap's `wpcap.dll`, loaded +//! dynamically at runtime. Npcap is never bundled, downloaded, or installed by +//! this app — the user installs it themselves from . The +//! app must start and run normally when Npcap is absent, which is why there is +//! no import-table link against wpcap: every entry point is resolved with +//! `LoadLibraryExW`/`GetProcAddress` on first use. +//! +//! Exists as an alternative to [`crate::rawsock`] for users whose antivirus +//! interferes with raw-socket capture. Delivers link-layer frames (typically +//! `DLT_EN10MB` Ethernet), which `packet.rs` already parses. + +#[cfg(windows)] +pub use imp::{Capture, Npcap}; + +#[cfg(not(windows))] +pub use stub::{Capture, Npcap}; + +/// True when a pcap device name (e.g. `\Device\NPF_{GUID}`) refers to the +/// adapter with this GetAdaptersAddresses GUID string (e.g. `{GUID}`). +/// Case-insensitive; tolerates the GUID with or without braces. The Npcap +/// loopback device (`\Device\NPF_Loopback`) never matches because the match +/// requires the braced form. +pub fn npf_name_matches_adapter(pcap_name: &str, adapter_guid: &str) -> bool { + let bare = adapter_guid.trim_matches(|c| c == '{' || c == '}'); + if bare.is_empty() { + return false; + } + let needle = format!("{{{}}}", bare.to_ascii_lowercase()); + pcap_name.to_ascii_lowercase().contains(&needle) +} + +/// `pcap_pkthdr`'s timeval → unix microseconds. On 64-bit Windows `timeval` +/// is two 32-bit `long`s, hence the `i32` fields. +pub fn pkthdr_timestamp_us(ts_sec: i32, ts_usec: i32) -> i64 { + i64::from(ts_sec) * 1_000_000 + i64::from(ts_usec) +} + +#[cfg(windows)] +mod imp { + use std::ffi::CString; + use std::sync::OnceLock; + + use anyhow::{bail, Context, Result}; + + use crate::capture_backend::{Device, Packet}; + + const PCAP_ERRBUF_SIZE: usize = 256; + const PCAP_NETMASK_UNKNOWN: u32 = 0xffff_ffff; + // Matches rawsock's 256 KB RECV_BUF so LSO/GRO super-frames are not truncated. + const SNAPLEN: i32 = 262_144; + // Matches rawsock's 100 ms select() cadence; pcap_next_ex returns 0 on + // timeout, so the capture loop keeps honoring the stop flag. + const READ_TIMEOUT_MS: i32 = 100; + // Matches rawsock's 8 MB SO_RCVBUF to reduce drops (best-effort). + const KERNEL_BUFFER_BYTES: i32 = 8 * 1024 * 1024; + + const NOT_INSTALLED: &str = "Npcap is not installed (wpcap.dll could not be loaded). \ + Install Npcap from https://npcap.com, or switch the capture method back to \ + raw sockets in Settings."; + + // ---- LoadLibraryExW search flags ---- + // Lets wpcap.dll's dependency Packet.dll resolve from the directory the + // DLL itself was loaded from (System32\Npcap). + const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: u32 = 0x0000_0100; + const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: u32 = 0x0000_1000; + const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x0000_0800; + + /// Opaque `pcap_t`. + type PcapT = core::ffi::c_void; + + /// `struct pcap_pkthdr` on 64-bit Windows: `timeval` is two 32-bit longs. + #[repr(C)] + struct PcapPkthdr { + ts_sec: i32, + ts_usec: i32, + caplen: u32, + len: u32, + } + + /// Prefix of `pcap_if_t` up to the fields we read. + #[repr(C)] + struct PcapIf { + next: *mut PcapIf, + name: *mut u8, // C string: \Device\NPF_{GUID} + description: *mut u8, // C string, may be null + addresses: *mut core::ffi::c_void, + flags: u32, + } + + /// `struct bpf_program`. + #[repr(C)] + struct BpfProgram { + bf_len: u32, + bf_insns: *mut core::ffi::c_void, + } + + // The wpcap.dll function signatures, named so the GetProcAddress + // transmutes below state exactly what they produce. + type FindAllDevsFn = unsafe extern "C" fn(*mut *mut PcapIf, *mut u8) -> i32; + type FreeAllDevsFn = unsafe extern "C" fn(*mut PcapIf); + type OpenLiveFn = unsafe extern "C" fn(*const u8, i32, i32, i32, *mut u8) -> *mut PcapT; + type CompileFn = unsafe extern "C" fn(*mut PcapT, *mut BpfProgram, *const u8, i32, u32) -> i32; + type SetFilterFn = unsafe extern "C" fn(*mut PcapT, *mut BpfProgram) -> i32; + type FreeCodeFn = unsafe extern "C" fn(*mut BpfProgram); + type NextExFn = unsafe extern "C" fn(*mut PcapT, *mut *mut PcapPkthdr, *mut *const u8) -> i32; + type DatalinkFn = unsafe extern "C" fn(*mut PcapT) -> i32; + type CloseFn = unsafe extern "C" fn(*mut PcapT); + type GetErrFn = unsafe extern "C" fn(*mut PcapT) -> *mut u8; + type LibVersionFn = unsafe extern "C" fn() -> *const u8; + type SetBuffFn = unsafe extern "C" fn(*mut PcapT, i32) -> i32; + + /// wpcap.dll entry points resolved via GetProcAddress. Plain `extern "C"` + /// function pointers are `Send + Sync`, so the `OnceLock` cache below is + /// shareable across threads without any unsafe impls. + struct Api { + findalldevs: FindAllDevsFn, + freealldevs: FreeAllDevsFn, + open_live: OpenLiveFn, + compile: CompileFn, + setfilter: SetFilterFn, + freecode: FreeCodeFn, + next_ex: NextExFn, + datalink: DatalinkFn, + close: CloseFn, + geterr: GetErrFn, + lib_version: LibVersionFn, + /// WinPcap/Npcap-specific kernel buffer resize; absent in some builds. + setbuff: Option, + } + + /// Set on successful load only — a failed probe is retried on the next + /// call, so installing Npcap while the app is running works without a + /// restart. The module stays loaded for the process lifetime. + static API: OnceLock = OnceLock::new(); + + // ---- Public surface: Npcap (loader) + Capture (live handle) ---- + + pub struct Npcap { + api: &'static Api, + } + + impl Npcap { + pub fn load() -> Result { + Ok(Npcap { api: load_api()? }) + } + + /// Cheap when Npcap is present (cached); a failed probe is a couple of + /// LoadLibraryExW calls. + pub fn is_installed() -> bool { + Self::load().is_ok() + } + + /// The `pcap_lib_version` banner, for diagnostics. + pub fn lib_version(&self) -> String { + c_to_string(unsafe { (self.api.lib_version)() }) + } + + /// Open a live capture on the adapter with this GetAdaptersAddresses + /// GUID by finding the matching `\Device\NPF_{GUID}` pcap device. + pub fn open_adapter(&self, adapter_guid: &str) -> Result { + let device = self + .list_devices()? + .into_iter() + .find(|device| super::npf_name_matches_adapter(&device.name, adapter_guid)) + .ok_or_else(|| { + anyhow::anyhow!( + "Npcap did not report a capture device for the selected network \ + adapter ({adapter_guid}). Press Refresh in Settings, reinstall \ + Npcap, or switch back to raw sockets." + ) + })?; + + let name = CString::new(device.name.clone()) + .with_context(|| format!("capture device name {:?}", device.name))?; + let mut errbuf = [0u8; PCAP_ERRBUF_SIZE + 1]; + let handle = unsafe { + (self.api.open_live)( + name.as_ptr().cast(), + SNAPLEN, + 0, // promiscuous off — mirrors RCVALL_IPLEVEL; promiscuous + // mode drops the host's own inbound traffic on some Wi-Fi + // adapters (see rawsock.rs), breaking TLS key establishment. + READ_TIMEOUT_MS, + errbuf.as_mut_ptr(), + ) + }; + if handle.is_null() { + bail!( + "pcap_open_live on {} failed: {}", + device.name, + c_to_string(errbuf.as_ptr()) + ); + } + + // Enlarge the kernel buffer to reduce drops; best-effort, like + // rawsock's SO_RCVBUF. + if let Some(setbuff) = self.api.setbuff { + let _ = unsafe { setbuff(handle, KERNEL_BUFFER_BYTES) }; + } + + let datalink = unsafe { (self.api.datalink)(handle) }; + Ok(Capture { + api: self.api, + handle, + datalink, + }) + } + + fn list_devices(&self) -> Result> { + let mut list: *mut PcapIf = std::ptr::null_mut(); + let mut errbuf = [0u8; PCAP_ERRBUF_SIZE + 1]; + if unsafe { (self.api.findalldevs)(&mut list, errbuf.as_mut_ptr()) } != 0 { + bail!( + "listing Npcap capture devices failed: {}", + c_to_string(errbuf.as_ptr()) + ); + } + + let mut devices = Vec::new(); + let mut current = list; + while !current.is_null() { + let entry = unsafe { &*current }; + let name = c_to_string(entry.name); + if !name.is_empty() { + let description = Some(c_to_string(entry.description)) + .filter(|description| !description.is_empty()); + devices.push(Device { name, description }); + } + current = entry.next; + } + if !list.is_null() { + unsafe { (self.api.freealldevs)(list) }; + } + Ok(devices) + } + } + + pub struct Capture { + api: &'static Api, + handle: *mut PcapT, + datalink: i32, + } + + impl Capture { + pub fn next_packet(&mut self) -> Result> { + let mut header: *mut PcapPkthdr = std::ptr::null_mut(); + let mut data: *const u8 = std::ptr::null(); + let result = unsafe { (self.api.next_ex)(self.handle, &mut header, &mut data) }; + match result { + // The header and data buffers are only valid until the next + // pcap call, so copy out immediately. + 1 => { + if header.is_null() || data.is_null() { + return Ok(None); + } + let header = unsafe { &*header }; + let captured = header.caplen as usize; + let bytes = unsafe { std::slice::from_raw_parts(data, captured) }; + Ok(Some(Packet { + timestamp_us: super::pkthdr_timestamp_us(header.ts_sec, header.ts_usec), + captured_len: header.caplen, + original_len: header.len, + data: bytes.to_vec(), + })) + } + 0 => Ok(None), // read timeout — nothing captured this round + -2 => Ok(None), // pcap_breakloop, not used but harmless + _ => bail!("Npcap capture read failed: {}", self.last_error()), + } + } + + pub fn datalink(&self) -> Result { + Ok(self.datalink) + } + + pub fn set_filter(&mut self, expression: &str) -> Result<()> { + let filter = CString::new(expression) + .with_context(|| format!("capture filter {expression:?}"))?; + let mut program = BpfProgram { + bf_len: 0, + bf_insns: std::ptr::null_mut(), + }; + if unsafe { + (self.api.compile)( + self.handle, + &mut program, + filter.as_ptr().cast(), + 1, // optimize + PCAP_NETMASK_UNKNOWN, + ) + } != 0 + { + bail!( + "compiling capture filter {expression:?} failed: {}", + self.last_error() + ); + } + let result = unsafe { (self.api.setfilter)(self.handle, &mut program) }; + unsafe { (self.api.freecode)(&mut program) }; + if result != 0 { + bail!( + "installing capture filter {expression:?} failed: {}", + self.last_error() + ); + } + Ok(()) + } + + fn last_error(&self) -> String { + c_to_string(unsafe { (self.api.geterr)(self.handle) }) + } + } + + impl Drop for Capture { + fn drop(&mut self) { + unsafe { (self.api.close)(self.handle) }; + } + } + + // ---- wpcap.dll loading ---- + + fn load_api() -> Result<&'static Api> { + if let Some(api) = API.get() { + return Ok(api); + } + let api = probe_wpcap()?; + // A racing thread may have loaded it too; either Api is equivalent. + Ok(API.get_or_init(|| api)) + } + + fn probe_wpcap() -> Result { + // (1) The standard Npcap install: \Npcap\wpcap.dll. + // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR lets its dependency Packet.dll + // resolve from that same directory. + let mut module: isize = 0; + if let Some(system32) = system_directory() { + let path = wide(&format!("{system32}\\Npcap\\wpcap.dll")); + module = unsafe { + LoadLibraryExW( + path.as_ptr(), + std::ptr::null_mut(), + LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, + ) + }; + } + // (2) "WinPcap API-compatible mode" installs place wpcap.dll in + // System32 itself. Restricting the search to System32 avoids picking + // up a planted wpcap.dll next to our exe or on PATH. + if module == 0 { + let name = wide("wpcap.dll"); + module = unsafe { + LoadLibraryExW( + name.as_ptr(), + std::ptr::null_mut(), + LOAD_LIBRARY_SEARCH_SYSTEM32, + ) + }; + } + if module == 0 { + bail!("{NOT_INSTALLED}"); + } + + macro_rules! required { + ($name:literal, $ty:ty) => {{ + let address = unsafe { GetProcAddress(module, concat!($name, "\0").as_ptr()) }; + if address.is_null() { + bail!( + "wpcap.dll is missing the expected function {} — the Npcap install \ + may be damaged; reinstall it from https://npcap.com", + $name + ); + } + unsafe { std::mem::transmute::<*mut core::ffi::c_void, $ty>(address) } + }}; + } + + let setbuff_address = unsafe { GetProcAddress(module, c"pcap_setbuff".as_ptr().cast()) }; + Ok(Api { + findalldevs: required!("pcap_findalldevs", FindAllDevsFn), + freealldevs: required!("pcap_freealldevs", FreeAllDevsFn), + open_live: required!("pcap_open_live", OpenLiveFn), + compile: required!("pcap_compile", CompileFn), + setfilter: required!("pcap_setfilter", SetFilterFn), + freecode: required!("pcap_freecode", FreeCodeFn), + next_ex: required!("pcap_next_ex", NextExFn), + datalink: required!("pcap_datalink", DatalinkFn), + close: required!("pcap_close", CloseFn), + geterr: required!("pcap_geterr", GetErrFn), + lib_version: required!("pcap_lib_version", LibVersionFn), + setbuff: if setbuff_address.is_null() { + None + } else { + Some(unsafe { + std::mem::transmute::<*mut core::ffi::c_void, SetBuffFn>(setbuff_address) + }) + }, + }) + } + + fn system_directory() -> Option { + let mut buffer = [0u16; 260]; + let written = unsafe { GetSystemDirectoryW(buffer.as_mut_ptr(), buffer.len() as u32) }; + if written == 0 || written as usize >= buffer.len() { + return None; + } + Some(String::from_utf16_lossy(&buffer[..written as usize])) + } + + fn wide(text: &str) -> Vec { + text.encode_utf16().chain(Some(0)).collect() + } + + fn c_to_string(ptr: *const u8) -> String { + if ptr.is_null() { + return String::new(); + } + let mut len = 0usize; + while unsafe { *ptr.add(len) } != 0 { + len += 1; + } + let bytes = unsafe { std::slice::from_raw_parts(ptr, len) }; + String::from_utf8_lossy(bytes).into_owned() + } + + #[link(name = "Kernel32")] + extern "system" { + fn LoadLibraryExW(name: *const u16, file: *mut core::ffi::c_void, flags: u32) -> isize; + fn GetProcAddress(module: isize, name: *const u8) -> *mut core::ffi::c_void; + fn GetSystemDirectoryW(buffer: *mut u16, size: u32) -> u32; + } + + #[cfg(test)] + mod tests { + use super::PcapPkthdr; + + /// Layout guard: `pcap_pkthdr` on 64-bit Windows is `timeval` (two + /// 32-bit longs) + two `bpf_u_int32`s — 16 bytes, no padding. + #[test] + fn pcap_pkthdr_layout_matches_windows_abi() { + assert_eq!(std::mem::size_of::(), 16); + let header = PcapPkthdr { + ts_sec: 1, + ts_usec: 2, + caplen: 3, + len: 4, + }; + assert_eq!( + std::mem::offset_of!(PcapPkthdr, caplen), + 8, + "caplen must directly follow the 8-byte timeval" + ); + assert_eq!(header.len, 4); + } + } +} + +#[cfg(not(windows))] +mod stub { + use anyhow::{bail, Result}; + + use crate::capture_backend::{Device, Packet}; + + pub struct Npcap; + pub struct Capture; + + impl Npcap { + pub fn load() -> Result { + bail!("Npcap capture is only supported on Windows") + } + pub fn is_installed() -> bool { + false + } + pub fn lib_version(&self) -> String { + String::new() + } + #[allow(dead_code)] + pub fn list_devices(&self) -> Result> { + Ok(Vec::new()) + } + pub fn open_adapter(&self, _adapter_guid: &str) -> Result { + bail!("Npcap capture is only supported on Windows") + } + } + + impl Capture { + pub fn next_packet(&mut self) -> Result> { + Ok(None) + } + pub fn datalink(&self) -> Result { + Ok(crate::packet::DLT_EN10MB) + } + pub fn set_filter(&mut self, _expression: &str) -> Result<()> { + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn npf_name_matches_braced_guid_case_insensitively() { + let pcap_name = r"\Device\NPF_{F3A9B2C1-0D4E-4F56-9A78-1B2C3D4E5F60}"; + assert!(npf_name_matches_adapter( + pcap_name, + "{f3a9b2c1-0d4e-4f56-9a78-1b2c3d4e5f60}" + )); + assert!(npf_name_matches_adapter( + pcap_name, + "{F3A9B2C1-0D4E-4F56-9A78-1B2C3D4E5F60}" + )); + } + + #[test] + fn npf_name_matches_guid_without_braces() { + assert!(npf_name_matches_adapter( + r"\Device\NPF_{F3A9B2C1-0D4E-4F56-9A78-1B2C3D4E5F60}", + "F3A9B2C1-0D4E-4F56-9A78-1B2C3D4E5F60" + )); + } + + #[test] + fn npf_loopback_device_never_matches() { + assert!(!npf_name_matches_adapter( + r"\Device\NPF_Loopback", + "Loopback" + )); + } + + #[test] + fn npf_name_rejects_different_guid() { + assert!(!npf_name_matches_adapter( + r"\Device\NPF_{F3A9B2C1-0D4E-4F56-9A78-1B2C3D4E5F60}", + "{00000000-0000-0000-0000-000000000000}" + )); + } + + #[test] + fn npf_name_rejects_empty_guid() { + let pcap_name = r"\Device\NPF_{F3A9B2C1-0D4E-4F56-9A78-1B2C3D4E5F60}"; + assert!(!npf_name_matches_adapter(pcap_name, "")); + assert!(!npf_name_matches_adapter(pcap_name, "{}")); + } + + #[test] + fn pkthdr_timestamp_converts_to_unix_micros() { + assert_eq!( + pkthdr_timestamp_us(1_700_000_000, 250_000), + 1_700_000_000_250_000 + ); + assert_eq!(pkthdr_timestamp_us(0, 0), 0); + assert_eq!(pkthdr_timestamp_us(1, 999_999), 1_999_999); + } +} diff --git a/src/widgets.rs b/src/widgets.rs index 1fe626c..964ee04 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -140,13 +140,18 @@ pub fn settings_row( ) { ui.add_space(SPACE_XS); ui.horizontal(|ui| { - ui.vertical(|ui| { - ui.label(RichText::new(label).color(arc_foreground())); - if !sub.is_empty() { - ui.label(RichText::new(sub).size(TEXT_CAPTION).color(arc_fg_dim())); - } + // Lay the control out from the right edge first so the remaining width + // bounds the text column — a long sub-label (or translation) wraps + // instead of running underneath the control. + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + add_control(ui); + ui.with_layout(Layout::top_down(Align::Min), |ui| { + ui.label(RichText::new(label).color(arc_foreground())); + if !sub.is_empty() { + ui.label(RichText::new(sub).size(TEXT_CAPTION).color(arc_fg_dim())); + } + }); }); - ui.with_layout(Layout::right_to_left(Align::Center), add_control); }); ui.add_space(SPACE_XS); }