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
80 changes: 63 additions & 17 deletions src/components/Footer.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import { execSync } from 'child_process';
import { Mail } from '@lucide/astro';
import { Mail, ArrowUpRight } from '@lucide/astro';
import { version } from '../../package.json';
import { resolveLocale, localesForPath, localizedPath, t } from '../lib/i18n';

Expand All @@ -9,14 +9,38 @@ const ui = t(lang);
const localeHref = (path: string) =>
localesForPath(path).includes(lang) ? localizedPath(path, lang) : path;

// Sitemap links reuse the localized chrome strings — no new translations needed.
const links = [
{ href: localeHref('/voice/'), label: ui.navVoice },
{ href: localeHref('/voice/download/'), label: ui.subDownload },
{ href: localeHref('/voice/changelog/'), label: ui.subChangelog },
{ href: '/docs', label: ui.navDocs },
{ href: localeHref('/blog/'), label: ui.navBlog },
{ href: localeHref('/about/'), label: ui.footerAbout },
// Three columns: the WaveKat Voice desktop app, the hosted tools, then
// everything sitewide. Tools links straight to the live app rather than to a
// marketing page — someone reaching for it in the footer wants the tool, not a
// description of it — and is a separate group because it's a different product
// from the Voice desktop app.
const groups = [
{
heading: 'WaveKat Voice', // brand name — never translated
links: [
{ href: localeHref('/voice/'), label: ui.subOverview },
{ href: localeHref('/voice/alternatives/'), label: ui.subAlternatives },
{ href: localeHref('/voice/download/'), label: ui.subDownload },
{ href: localeHref('/voice/changelog/'), label: ui.subChangelog },
],
},
{
heading: ui.footerTools,
links: [
{
href: 'https://platform.wavekat.com/voice/prompts',
label: ui.footerPrompts,
external: true,
},
],
},
{
heading: ui.footerMore,
links: [
{ href: localeHref('/blog/'), label: ui.navBlog },
{ href: localeHref('/about/'), label: ui.footerAbout },
],
},
];

let siteVersion: string;
Expand Down Expand Up @@ -66,14 +90,36 @@ try {
</a>
</div>
</div>
<nav class="grid grid-cols-2 gap-x-10 gap-y-2.5 sm:text-right">
{links.map((link) => (
<a
href={link.href}
class="text-sm text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
>
{link.label}
</a>
<!-- Auto-width groups hugging the right edge, not stretched grid tracks —
equal columns spread a one-item group miles from its neighbours. -->
<nav class="flex flex-wrap gap-x-12 gap-y-8 sm:justify-end sm:text-right">
{groups.map((group) => (
<div class="flex flex-col gap-2.5">
<span
class="font-mono text-xs font-bold uppercase tracking-widest text-gray-400 dark:text-gray-600"
>{group.heading}</span
>
{group.links.map((link) =>
link.external ? (
<a
href={link.href}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-1 text-sm text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white sm:justify-end"
>
{link.label}
<ArrowUpRight class="h-3.5 w-3.5 shrink-0 opacity-60" aria-hidden="true" />
</a>
) : (
<a
href={link.href}
class="text-sm text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
>
{link.label}
</a>
)
)}
</div>
))}
</nav>
</div>
Expand Down
31 changes: 20 additions & 11 deletions src/lib/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ export interface UIStrings {
footerEmail: string;
footerSource: string;
footerAbout: string;
footerPrompts: string;
footerMore: string;
footerTools: string;
notFoundTitle: string;
notFoundBody: string;
notFoundHome: string;
Expand Down Expand Up @@ -242,22 +245,22 @@ const strings: Record<string, UIStrings> = {
fr: uiFr,
it: uiIt,
en: {
navVoice: 'voice',
navDocs: 'docs',
navBlog: 'blog',
navVoice: 'Voice',
navDocs: 'Docs',
navBlog: 'Blog',
switcherLabel: 'Change language',
bannerText: 'This page is available in English.',
bannerView: 'View',
bannerDismiss: 'Dismiss',
subOverview: 'overview',
subUseCases: 'use cases',
subAlternatives: 'alternatives',
subDownload: 'download',
subChangelog: "what's new",
subTalk: 'talk to us',
subOverview: 'Overview',
subUseCases: 'Use Cases',
subAlternatives: 'Alternatives',
subDownload: 'Download',
subChangelog: "What's New",
subTalk: 'Talk to Us',
dlMac: 'Download for Mac',
dlLinux: 'Download for Linux',
dlOther: 'other platforms',
dlOther: 'Other Platforms',
talkHeading: 'Talk to us',
talkBody:
'Questions, feedback, or a device you wish we supported? Email is the best way to reach us — we read every message and reply as soon as we can.',
Expand All @@ -267,10 +270,13 @@ const strings: Record<string, UIStrings> = {
footerEmail: 'Email us',
footerSource: 'View source on GitHub',
footerAbout: 'About',
footerPrompts: 'Prompt Generator',
footerMore: 'More',
footerTools: 'Tools',
notFoundTitle: "Page not found",
notFoundBody: "The page you're looking for doesn't exist or has moved.",
notFoundHome: "Go to the homepage",
postBack: '← back to blog',
postBack: '← Back to Blog',
postUpdated: 'updated',
},
'zh-Hans': {
Expand Down Expand Up @@ -299,6 +305,9 @@ const strings: Record<string, UIStrings> = {
footerEmail: '给我们发邮件',
footerSource: '在 GitHub 上查看源码',
footerAbout: '关于',
footerPrompts: '语音生成器',
footerMore: '更多',
footerTools: '工具',
notFoundTitle: "页面不存在",
notFoundBody: "您要找的页面不存在或已被移动。",
notFoundHome: "返回首页",
Expand Down
25 changes: 14 additions & 11 deletions src/lib/ui/de.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { UIStrings } from '../i18n';

export const ui: UIStrings = {
navVoice: 'stimme',
navDocs: 'docs',
navBlog: 'blog',
navVoice: 'Stimme',
navDocs: 'Docs',
navBlog: 'Blog',
switcherLabel: 'Sprache wechseln',
bannerText: 'Diese Seite ist auf Deutsch verfügbar.',
bannerView: 'Ansehen',
bannerDismiss: 'Schließen',
subOverview: 'überblick',
subUseCases: 'anwendungsfälle',
subAlternatives: 'alternativen',
subDownload: 'download',
subChangelog: 'neuigkeiten',
subTalk: 'kontakt',
subOverview: 'Überblick',
subUseCases: 'Anwendungsfälle',
subAlternatives: 'Alternativen',
subDownload: 'Download',
subChangelog: 'Neuigkeiten',
subTalk: 'Kontakt',
dlMac: 'Für Mac herunterladen',
dlLinux: 'Für Linux herunterladen',
dlOther: 'andere Plattformen',
dlOther: 'Andere Plattformen',
talkHeading: 'Kontakt',
talkBody:
'Fragen, Feedback oder ein Gerät, das Sie sich von uns wünschen? Am besten erreichen Sie uns per E-Mail — wir lesen jede Nachricht und antworten, so schnell wir können.',
Expand All @@ -25,10 +25,13 @@ export const ui: UIStrings = {
talkWritePost: '.',
footerEmail: 'Schreiben Sie uns',
footerAbout: 'Über uns',
footerPrompts: 'Sprachgenerator',
footerMore: 'Mehr',
footerTools: 'Tools',
notFoundTitle: "Seite nicht gefunden",
notFoundBody: "Die gesuchte Seite existiert nicht oder wurde verschoben.",
notFoundHome: "Zur Startseite",
footerSource: 'Quellcode auf GitHub ansehen',
postBack: '← zurück zum Blog',
postBack: '← Zurück zum Blog',
postUpdated: 'aktualisiert',
};
25 changes: 14 additions & 11 deletions src/lib/ui/es.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { UIStrings } from '../i18n';

export const ui: UIStrings = {
navVoice: 'voz',
navDocs: 'docs',
navBlog: 'blog',
navVoice: 'Voz',
navDocs: 'Docs',
navBlog: 'Blog',
switcherLabel: 'Cambiar idioma',
bannerText: 'Esta página está disponible en español.',
bannerView: 'Ver',
bannerDismiss: 'Descartar',
subOverview: 'visión general',
subUseCases: 'casos de uso',
subAlternatives: 'alternativas',
subDownload: 'descargar',
subChangelog: 'novedades',
subTalk: 'contacto',
subOverview: 'Visión general',
subUseCases: 'Casos de uso',
subAlternatives: 'Alternativas',
subDownload: 'Descargar',
subChangelog: 'Novedades',
subTalk: 'Contacto',
dlMac: 'Descargar para Mac',
dlLinux: 'Descargar para Linux',
dlOther: 'otras plataformas',
dlOther: 'Otras plataformas',
talkHeading: 'Contacto',
talkBody:
'¿Preguntas, comentarios o un dispositivo que le gustaría que admitiéramos? El correo electrónico es la mejor forma de contactarnos: leemos cada mensaje y respondemos en cuanto podemos.',
Expand All @@ -25,10 +25,13 @@ export const ui: UIStrings = {
talkWritePost: '.',
footerEmail: 'Escríbanos',
footerAbout: 'Acerca de',
footerPrompts: 'Generador de voz',
footerMore: 'Más',
footerTools: 'Herramientas',
notFoundTitle: "Página no encontrada",
notFoundBody: "La página que buscas no existe o se ha movido.",
notFoundHome: "Ir a la página de inicio",
footerSource: 'Ver el código en GitHub',
postBack: '← volver al blog',
postBack: '← Volver al blog',
postUpdated: 'actualizado',
};
25 changes: 14 additions & 11 deletions src/lib/ui/fr.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { UIStrings } from '../i18n';

export const ui: UIStrings = {
navVoice: 'voix',
navDocs: 'docs',
navBlog: 'blog',
navVoice: 'Voix',
navDocs: 'Docs',
navBlog: 'Blog',
switcherLabel: 'Changer de langue',
bannerText: 'Cette page est disponible en français.',
bannerView: 'Voir',
bannerDismiss: 'Fermer',
subOverview: 'aperçu',
subUseCases: "cas d'usage",
subAlternatives: 'alternatives',
subDownload: 'télécharger',
subChangelog: 'nouveautés',
subTalk: 'nous contacter',
subOverview: 'Aperçu',
subUseCases: "Cas d'usage",
subAlternatives: 'Alternatives',
subDownload: 'Télécharger',
subChangelog: 'Nouveautés',
subTalk: 'Nous contacter',
dlMac: 'Télécharger pour Mac',
dlLinux: 'Télécharger pour Linux',
dlOther: 'autres plateformes',
dlOther: 'Autres plateformes',
talkHeading: 'Nous contacter',
talkBody:
'Une question, un retour, ou un appareil que vous aimeriez nous voir prendre en charge ? L’e-mail est le meilleur moyen de nous joindre — nous lisons chaque message et répondons dès que possible.',
Expand All @@ -25,10 +25,13 @@ export const ui: UIStrings = {
talkWritePost: '.',
footerEmail: 'Écrivez-nous',
footerAbout: 'À propos',
footerPrompts: 'Générateur vocal',
footerMore: 'Plus',
footerTools: 'Outils',
notFoundTitle: "Page introuvable",
notFoundBody: "La page que vous cherchez n'existe pas ou a été déplacée.",
notFoundHome: "Aller à l'accueil",
footerSource: 'Voir le code source sur GitHub',
postBack: '← retour au blog',
postBack: '← Retour au blog',
postUpdated: 'mis à jour le',
};
25 changes: 14 additions & 11 deletions src/lib/ui/it.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { UIStrings } from '../i18n';

export const ui: UIStrings = {
navVoice: 'voce',
navDocs: 'documentazione',
navBlog: 'blog',
navVoice: 'Voce',
navDocs: 'Documentazione',
navBlog: 'Blog',
switcherLabel: 'Cambia lingua',
bannerText: 'Questa pagina è disponibile in italiano.',
bannerView: 'Visualizza',
bannerDismiss: 'Chiudi',
subOverview: 'panoramica',
subUseCases: 'casi d’uso',
subAlternatives: 'alternative',
subDownload: 'download',
subChangelog: 'novità',
subTalk: 'contattaci',
subOverview: 'Panoramica',
subUseCases: 'Casi d’uso',
subAlternatives: 'Alternative',
subDownload: 'Download',
subChangelog: 'Novità',
subTalk: 'Contattaci',
dlMac: 'Scarica per Mac',
dlLinux: 'Scarica per Linux',
dlOther: 'altre piattaforme',
dlOther: 'Altre piattaforme',
talkHeading: 'Contattaci',
talkBody:
'Domande, suggerimenti o un dispositivo che vorresti vedere supportato? L’email è il modo migliore per raggiungerci: leggiamo ogni messaggio e rispondiamo il prima possibile.',
Expand All @@ -25,10 +25,13 @@ export const ui: UIStrings = {
talkWritePost: '.',
footerEmail: 'Scrivici un’email',
footerAbout: 'Chi siamo',
footerPrompts: 'Generatore vocale',
footerMore: 'Altro',
footerTools: 'Strumenti',
notFoundTitle: "Pagina non trovata",
notFoundBody: "La pagina che cerchi non esiste o è stata spostata.",
notFoundHome: "Vai alla home",
footerSource: 'Vedi il codice sorgente su GitHub',
postBack: '← torna al blog',
postBack: '← Torna al blog',
postUpdated: 'aggiornato',
};
3 changes: 3 additions & 0 deletions src/lib/ui/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const ui: UIStrings = {
talkWritePost: ' まで直接ご連絡ください。',
footerEmail: 'メールを送る',
footerAbout: 'WaveKat について',
footerPrompts: '音声ジェネレーター',
footerMore: 'その他',
footerTools: 'ツール',
notFoundTitle: "ページが見つかりません",
notFoundBody: "お探しのページは存在しないか、移動しました。",
notFoundHome: "ホームへ戻る",
Expand Down
3 changes: 3 additions & 0 deletions src/lib/ui/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const ui: UIStrings = {
talkWritePost: '(으)로 직접 메일을 보내셔도 됩니다.',
footerEmail: '이메일 보내기',
footerAbout: '소개',
footerPrompts: '음성 생성기',
footerMore: '기타',
footerTools: '도구',
notFoundTitle: "페이지를 찾을 수 없습니다",
notFoundBody: "찾으시는 페이지가 존재하지 않거나 이동되었습니다.",
notFoundHome: "홈으로 돌아가기",
Expand Down
3 changes: 3 additions & 0 deletions src/lib/ui/zh-hant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const ui: UIStrings = {
talkWritePost: '。',
footerEmail: '寄信給我們',
footerAbout: '關於',
footerPrompts: '語音產生器',
footerMore: '更多',
footerTools: '工具',
notFoundTitle: "找不到頁面",
notFoundBody: "您要找的頁面不存在或已被移動。",
notFoundHome: "返回首頁",
Expand Down
Loading