Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions scripts/check-i18n.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const requiredChunks = [
"support.json",
"sponsors.json",
"open-source-accessibility.json",
"privacy.json",
"android.json",
"delete-account.json",
"docs/common.json",
"docs/mac.json",
];
Expand Down
9 changes: 8 additions & 1 deletion scripts/sitemap-filter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ import { existsSync, readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

const META_REFRESH_RE = /<meta[^>]+http-equiv=["']?refresh/i;
const NOINDEX_RE = /<meta[^>]+name=["']?robots["']?[^>]+content=["'][^"']*noindex/i;

/** A page that only forwards the visitor carries a meta refresh. */
export function isRedirectHtml(html) {
return META_REFRESH_RE.test(html);
}

/** A page that asks search engines not to index it, e.g. a page that is not live yet. */
export function isNoIndexHtml(html) {
return NOINDEX_RE.test(html);
}

/**
* Filter for @astrojs/sitemap. The sitemap is written after the pages, so the
* built HTML tells whether a URL is a redirect page: the unprefixed URLs that
Expand All @@ -18,6 +24,7 @@ export function createSitemapFilter(distDir) {
const pathname = decodeURIComponent(new URL(page).pathname);
const file = fileURLToPath(new URL(`.${pathname}index.html`, distDir));
if (!existsSync(file)) return true;
return !isRedirectHtml(readFileSync(file, "utf8"));
const html = readFileSync(file, "utf8");
return !isRedirectHtml(html) && !isNoIndexHtml(html);
};
}
7 changes: 7 additions & 0 deletions src/components/layout/footer.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { mulberry32 } from "@/lib/seeded-random";
import type { Locale } from "@/i18n/index";
import { t, localePath } from "@/i18n/index";
import { androidBeta } from "@/lib/android-beta";

interface Props {
locale?: Locale;
Expand Down Expand Up @@ -139,6 +140,11 @@ const community = [
data-tracking-placement="footer">{t(locale, "footer.ios")}</a
>
</li>
{androidBeta.listed && (
<li>
<a href={localePath(locale, "/android")}>{t(locale, "footer.android")}</a>
</li>
)}
</ul>
</div>

Expand Down Expand Up @@ -167,6 +173,7 @@ const community = [
<li><a href={localePath(locale, "/privacy")}>{t(locale, "footer.privacy")}</a></li>
<li><a href={localePath(locale, "/terms")}>{t(locale, "footer.terms")}</a></li>
<li><a href={localePath(locale, "/legal-notice")}>{t(locale, "footer.legalNotice")}</a></li>
<li><a href={localePath(locale, "/delete-account")}>{t(locale, "footer.deleteAccount")}</a></li>
</ul>
</div>
</div>
Expand Down
38 changes: 38 additions & 0 deletions src/i18n/locales/de/android.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"android.meta.title": "TypeWhisper für Android (Beta)",
"android.meta.description": "Mach mit bei der TypeWhisper-Beta für Android: private Spracherkennung mit KI auf dem Gerät, einer Diktier-Tastatur und Schreibprofilen.",
"android.label": "Beta",
"android.title": "TypeWhisper für Android",
"android.lede": "Private Spracherkennung für Android mit KI auf dem Gerät, einer Diktier-Tastatur und Schreibprofilen.",
"android.requirement": "Android 10 oder neuer",
"android.join.label": "Bei der Beta mitmachen",
"android.join.title": "So machst du bei der Beta mit",
"android.join.lede": "Die Beta läuft über Google Play. Zwei Schritte, danach kommen Updates wie bei jeder anderen App.",
"android.join.step1.title": "Tritt der Google-Gruppe bei",
"android.join.step1.text": "Jedes Google-Konto funktioniert. Die Gruppe dient nur als Testerliste; dort werden keine E-Mails verschickt.",
"android.join.step1.button": "Google-Gruppe beitreten",
"android.join.step2.title": "Melde dich an und installiere aus Google Play",
"android.join.step2.text": "Öffne die Testseite mit dem Google-Konto, mit dem du der Gruppe beigetreten bist, tippe auf Tester werden und installiere TypeWhisper dann aus Google Play. Nach dem Beitritt zur Gruppe kann es ein paar Minuten dauern, bis die Seite dich hereinlässt.",
"android.join.step2.button": "Bei Google Play anmelden",
"android.join.step2.soon": "Die Testseite bei Google Play öffnet, sobald die Beta startet. Tritt der Gruppe schon jetzt bei, dann kannst du sofort einsteigen.",
"android.try.label": "Was du ausprobieren kannst",
"android.try.title": "Was du ausprobieren kannst",
"android.try.lede": "Alles hier ist in der Beta enthalten. Probier aus, was du jeden Tag nutzen würdest.",
"android.try.item1": "Diktieren auf dem Gerät mit Parakeet (25 europäische Sprachen) oder Whisper (99 Sprachen).",
"android.try.item2": "Optionale Cloud-Spracherkennung mit deinem eigenen API-Schlüssel.",
"android.try.item3": "Die TypeWhisper-Tastatur in jeder App, mit QWERTZ- oder QWERTY-Layout.",
"android.try.item4": "Schreibprofile: Aufräumen, Nachricht, E-Mail, Notiz, Kalendertermin, Checkliste, Zusammenfassung, Übersetzung oder eigene Profile.",
"android.try.item5": "Die Inbox für schnelle Diktate über die Schnelleinstellungs-Kachel, das Widget, eine App-Verknüpfung oder die Assistenten-Geste.",
"android.try.item6": "Dateien transkribieren, mit Untertiteln.",
"android.try.item7": "Premium: die Profile für E-Mail und Kalender, Live-Transkription mit Nemotron und Sprechererkennung.",
"android.notice.label": "Hinweis zur Beta",
"android.notice.title": "Hinweis zur Beta",
"android.notice.p1": "Das ist eine Beta. Rechne mit Fehlern und unfertigen Stellen.",
"android.notice.p2": "Bitte bleib mindestens 14 Tage angemeldet. Google erlaubt die öffentliche Veröffentlichung erst, wenn 12 Tester 14 Tage lang in der Beta geblieben sind.",
"android.notice.p3": "Feedback kannst du in der App unter Einstellungen → Feedback senden, auf Discord oder per E-Mail schicken.",
"android.notice.discord": "Discord",
"android.notice.discordText": "Fragen, Fehler und Ideen mit anderen Testern.",
"android.notice.emailText": "Fehlerberichte und alles, was du nicht öffentlich posten möchtest.",
"android.requirements.title": "Voraussetzungen",
"android.requirements.text": "Android 10 oder neuer und ein Google-Konto für Google Play."
}
9 changes: 8 additions & 1 deletion src/i18n/locales/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,19 @@
"footer.changelog": "Changelog",
"footer.windows": "Windows",
"footer.ios": "iOS",
"footer.android": "Android-Beta",
"footer.discord": "Discord",
"footer.github": "GitHub",
"footer.kofi": "Ko-fi",
"footer.copyright": "TypeWhisper. Open Source unter GPLv3.",
"footer.privacy": "Datenschutz",
"footer.terms": "Nutzungsbedingungen",
"footer.legalNotice": "Impressum",
"footer.deleteAccount": "Konto löschen",
"madeInGermany.label": "Made in Germany",
"madeInGermany.craft": "Unabhängig in Deutschland entwickelt",
"releaseStage.stable": "Stabil",
"releaseStage.beta": "Vorschau",
"releaseStage.beta": "Beta",
"releaseStage.alpha": "Alpha",
"releaseStage.pending": "App-Store-Release in Vorbereitung",
"benchmark.title": "Benchmark - TypeWhisper",
Expand Down Expand Up @@ -88,6 +90,11 @@
"releaseStatus.ios.description": "Private Sprache-zu-Text mit lokalen Engines, Diktier-Tastatur, Profilen, Dateien, Kurzbefehlen, Wörterbuch, Textbausteine und Apple-Watch-Unterstützung. Version 1.1 bringt lokale Schreibmodelle, Wörterbuchimporte mit Vorschau und Aufnahmesteuerungen für das Kontrollzentrum und die Aktionstaste.",
"releaseStatus.ios.download": "Im App Store laden",
"releaseStatus.ios.docs": "iOS-Dokumentation lesen",
"releaseStatus.android.title": "Android",
"releaseStatus.android.intro": "Android ist in einer geschlossenen Beta bei Google Play.",
"releaseStatus.android.detail": "Tester bekommen Diktieren auf dem Gerät, die Diktier-Tastatur, Schreibprofile und die Inbox vor der öffentlichen Veröffentlichung. Rechne mit Fehlern.",
"releaseStatus.android.description": "Benötigt Android 10 oder neuer. Jeder mit einem Google-Konto kann bei der Beta mitmachen.",
"releaseStatus.android.join": "Bei der Android-Beta mitmachen",
"notFound.title": "404",
"notFound.heading": "Seite nicht gefunden",
"notFound.description": "Die Seite, die du suchst, existiert nicht oder wurde verschoben.",
Expand Down
29 changes: 29 additions & 0 deletions src/i18n/locales/de/delete-account.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"deleteAccount.meta.title": "TypeWhisper-Konto löschen - TypeWhisper",
"deleteAccount.meta.description": "So löschst du dein TypeWhisper-Premium-Konto auf Android, iPhone, iPad und Mac, und das wird dabei gelöscht.",
"deleteAccount.heading": "TypeWhisper-Konto löschen",
"deleteAccount.lede": "Du kannst dein TypeWhisper-Premium-Konto in der App oder im Web löschen.",
"deleteAccount.section.who.title": "Für wen diese Seite ist",
"deleteAccount.section.who.p1": "Diese Seite gilt für TypeWhisper-Premium-Konten auf Android, iPhone, iPad und Mac. Ein solches Konto hast du, wenn du dich für Premium mit Google oder Apple angemeldet hast. TypeWhisper funktioniert ohne Konto; hast du dich nie angemeldet, gibt es nichts zu löschen.",
"deleteAccount.section.inApp.title": "In der App löschen",
"deleteAccount.section.inApp.p1": "Das ist der schnellste Weg.",
"deleteAccount.section.inApp.item1": "Android: Einstellungen → Premium → Konto löschen.",
"deleteAccount.section.inApp.item2": "iPhone, iPad und Mac: in den Premium-Einstellungen der App.",
"deleteAccount.section.email.title": "Im Web löschen",
"deleteAccount.section.email.p1": "Ohne App öffnest du <a href=\"https://app.typewhisper.com/account/delete\" class=\"text-primary hover:underline\">app.typewhisper.com/account/delete</a>, meldest dich mit dem Google- oder Apple-Konto an, das du für TypeWhisper nutzt, und löschst das Konto dort. Wenn etwas nicht klappt, schreib an <a href=\"mailto:hello@typewhisper.com\" class=\"text-primary hover:underline\">hello@typewhisper.com</a>.",
"deleteAccount.section.deleted.title": "Was gelöscht wird",
"deleteAccount.section.deleted.p1": "Beim Löschen entfernt der TypeWhisper-Premium-Dienst:",
"deleteAccount.section.deleted.item1": "Dein TypeWhisper-Konto",
"deleteAccount.section.deleted.item2": "Die damit verbundenen Geräte",
"deleteAccount.section.deleted.item3": "Deine Premium-Berechtigungen, samt den gehashten Verweisen auf deine Käufe im App Store, bei Google Play und bei Polar",
"deleteAccount.section.deleted.item4": "Die Einträge zu deinen Käufen über Google Play",
"deleteAccount.section.deleted.item5": "Deine Anmeldesitzungen",
"deleteAccount.section.deleted.p2": "Ist eine Polar-Lizenz von typewhisper.com mit dem Konto verknüpft, werden ihre Aktivierungen für dieses Konto deaktiviert.",
"deleteAccount.section.kept.title": "Was nicht gelöscht wird",
"deleteAccount.section.kept.item1": "Daten auf deinen Geräten, etwa Verlauf, Wörterbuch, Profile und Aufnahmen, bleiben, bis du sie in der App löschst. Synchronisierte Dateien in iCloud oder einem von dir gewählten Ordner bleiben, bis du sie dort löschst.",
"deleteAccount.section.kept.item2": "Google Play, Apple und Polar behalten ihre eigenen Kaufbelege. Deine Käufe bleiben bei deinem Google- oder Apple-Konto, du kannst sie also später nach einer neuen Anmeldung wiederherstellen.",
"deleteAccount.section.kept.item3": "Das Löschen des Kontos kündigt kein Abo. Kündige es separat in Google Play oder im App Store, sonst wird es weiter abgerechnet.",
"deleteAccount.section.privacy.title": "Mehr Details",
"deleteAccount.section.privacy.beforeLink": "Was der Premium-Dienst speichert, solange dein Konto besteht, steht in der ",
"deleteAccount.section.privacy.linkText": "Datenschutzerklärung"
}
4 changes: 4 additions & 0 deletions src/i18n/locales/de/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import support from "./support.json";
import sponsors from "./sponsors.json";
import openSourceAccessibility from "./open-source-accessibility.json";
import privacy from "./privacy.json";
import android from "./android.json";
import deleteAccount from "./delete-account.json";
import docsCommon from "./docs/common.json";
import docsMac from "./docs/mac.json";
import platformReleases from "./platform-releases.json";
Expand All @@ -23,6 +25,8 @@ const translations = {
...sponsors,
...openSourceAccessibility,
...privacy,
...android,
...deleteAccount,
...docsCommon,
...docsMac,
...platformReleases,
Expand Down
Loading
Loading