Add translation support for website
All checks were successful
Queue Release Build / prepare (push) Successful in 1m20s
Deploy Web Apps / deploy (push) Successful in 17m37s
Queue Release Build / build-windows (push) Successful in 39m8s
Queue Release Build / build-linux (push) Successful in 1h3m53s
Queue Release Build / finalize (push) Successful in 5m43s
All checks were successful
Queue Release Build / prepare (push) Successful in 1m20s
Deploy Web Apps / deploy (push) Successful in 17m37s
Queue Release Build / build-windows (push) Successful in 39m8s
Queue Release Build / build-linux (push) Successful in 1h3m53s
Queue Release Build / finalize (push) Successful in 5m43s
This commit is contained in:
@@ -16,19 +16,19 @@
|
||||
class="inline-flex items-center gap-2 px-4 py-1.5 rounded-full border border-purple-500/30 bg-purple-500/10 text-purple-400 text-sm font-medium mb-8 animate-fade-in"
|
||||
>
|
||||
<span class="w-2 h-2 rounded-full bg-purple-400 animate-pulse"></span>
|
||||
Currently in Beta - Free & Open Source
|
||||
{{ 'pages.home.hero.badge' | translate }}
|
||||
</div>
|
||||
|
||||
<h1 class="text-5xl md:text-7xl lg:text-8xl font-extrabold tracking-tight mb-6 animate-fade-in-up">
|
||||
<span class="text-foreground">Talk freely.</span><br />
|
||||
<span class="gradient-text">Own your voice.</span>
|
||||
<span class="text-foreground">{{ 'pages.home.hero.titleLine1' | translate }}</span><br />
|
||||
<span class="gradient-text">{{ 'pages.home.hero.titleLine2' | translate }}</span>
|
||||
</h1>
|
||||
|
||||
<p
|
||||
class="text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto mb-10 leading-relaxed animate-fade-in-up"
|
||||
style="animation-delay: 0.2s"
|
||||
>
|
||||
Crystal-clear voice calls, unlimited screen sharing, and file transfers with no size limits. Peer-to-peer. Private. Completely free.
|
||||
{{ 'pages.home.hero.description' | translate }}
|
||||
</p>
|
||||
|
||||
<!-- CTA Buttons -->
|
||||
@@ -54,7 +54,7 @@
|
||||
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||
/>
|
||||
</svg>
|
||||
Download for {{ detectedOS().name }}
|
||||
{{ 'common.actions.downloadFor' | translate:{ os: getDetectedOsLabel() } }}
|
||||
<span class="text-sm opacity-75">{{ detectedOS().icon }}</span>
|
||||
</a>
|
||||
} @else {
|
||||
@@ -75,7 +75,7 @@
|
||||
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||
/>
|
||||
</svg>
|
||||
Download Toju
|
||||
{{ 'common.actions.downloadBrand' | translate }}
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
rel="noopener"
|
||||
class="inline-flex items-center gap-2 px-8 py-4 rounded-xl border border-border/50 bg-card/50 text-foreground font-medium text-lg hover:bg-card hover:border-purple-500/30 transition-all backdrop-blur-sm"
|
||||
>
|
||||
Open in Browser
|
||||
{{ 'common.actions.openInBrowser' | translate }}
|
||||
<svg
|
||||
class="w-5 h-5 opacity-60"
|
||||
fill="none"
|
||||
@@ -107,11 +107,11 @@
|
||||
class="text-xs text-muted-foreground/60 animate-fade-in"
|
||||
style="animation-delay: 0.6s"
|
||||
>
|
||||
Version {{ latestVersion() }} ·
|
||||
{{ 'pages.home.hero.version' | translate:{ version: latestVersion() } }} ·
|
||||
<a
|
||||
routerLink="/downloads"
|
||||
class="underline hover:text-muted-foreground transition-colors"
|
||||
>All platforms</a
|
||||
>{{ 'pages.home.hero.allPlatforms' | translate }}</a
|
||||
>
|
||||
</p>
|
||||
}
|
||||
@@ -142,10 +142,10 @@
|
||||
<div class="container mx-auto px-6">
|
||||
<div class="text-center mb-20 section-fade">
|
||||
<h2 class="text-3xl md:text-5xl font-bold text-foreground mb-4">
|
||||
Everything you need,<br />
|
||||
<span class="gradient-text">nothing you don't.</span>
|
||||
{{ 'pages.home.features.titleLine1' | translate }}<br />
|
||||
<span class="gradient-text">{{ 'pages.home.features.titleLine2' | translate }}</span>
|
||||
</h2>
|
||||
<p class="text-muted-foreground text-lg max-w-xl mx-auto">No bloat. No paywalls. Just the tools to connect with the people who matter.</p>
|
||||
<p class="text-muted-foreground text-lg max-w-xl mx-auto">{{ 'pages.home.features.description' | translate }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@@ -168,9 +168,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">HD Voice Calls</h3>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">{{ 'pages.home.features.items.voiceCalls.title' | translate }}</h3>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
Crystal-clear audio with built-in noise reduction. Hear every word, not the background. No quality compromises, ever.
|
||||
{{ 'pages.home.features.items.voiceCalls.description' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -194,10 +194,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">Screen Sharing</h3>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">{{ 'pages.home.features.items.screenSharing.title' | translate }}</h3>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
Share your screen at full resolution. No time limits, no quality downgrades. Perfect for pair programming, presentations, or showing your
|
||||
epic gameplay.
|
||||
{{ 'pages.home.features.items.screenSharing.description' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -221,9 +220,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">Unlimited File Sharing</h3>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">{{ 'pages.home.features.items.fileSharing.title' | translate }}</h3>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
Send files of any size directly to your friends. No upload limits, no compression. Your files go straight from you to them.
|
||||
{{ 'pages.home.features.items.fileSharing.description' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -246,10 +245,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">True Privacy</h3>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">{{ 'pages.home.features.items.privacy.title' | translate }}</h3>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
Peer-to-peer means your data goes directly between you and your friends. No servers storing your conversations. Your business is your
|
||||
business.
|
||||
{{ 'pages.home.features.items.privacy.description' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -273,9 +271,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">Open Source</h3>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">{{ 'pages.home.features.items.openSource.title' | translate }}</h3>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
Every line of code is public. Audit it, modify it, host your own signal server. Full transparency - nothing is hidden.
|
||||
{{ 'pages.home.features.items.openSource.description' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -299,9 +297,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">Completely Free</h3>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-3">{{ 'pages.home.features.items.free.title' | translate }}</h3>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
No premium tiers. No paywalls. No "starter plans". Every feature is available to everyone, always. Made with love, not profit margins.
|
||||
{{ 'pages.home.features.items.free.description' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -338,15 +336,14 @@
|
||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
Built for Gamers
|
||||
{{ 'pages.home.gaming.badge' | translate }}
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-5xl font-bold text-foreground mb-6">
|
||||
Your perfect<br />
|
||||
<span class="gradient-text">gaming companion.</span>
|
||||
{{ 'pages.home.gaming.titleLine1' | translate }}<br />
|
||||
<span class="gradient-text">{{ 'pages.home.gaming.titleLine2' | translate }}</span>
|
||||
</h2>
|
||||
<p class="text-lg text-muted-foreground leading-relaxed mb-8">
|
||||
Ultra-low latency voice chat that doesn't eat your bandwidth. Share your screen without frame drops. Send clips and files instantly. All
|
||||
while keeping your CPU free for what matters - winning.
|
||||
{{ 'pages.home.gaming.description' | translate }}
|
||||
</p>
|
||||
<ul class="space-y-4">
|
||||
<li class="flex items-center gap-3 text-muted-foreground">
|
||||
@@ -363,7 +360,7 @@
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
<span>Low-latency peer-to-peer voice - no relay servers in the way</span>
|
||||
<span>{{ 'pages.home.gaming.bullets.lowLatency' | translate }}</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-muted-foreground">
|
||||
<svg
|
||||
@@ -379,7 +376,7 @@
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
<span>AI-powered noise suppression - keyboard clatter stays out</span>
|
||||
<span>{{ 'pages.home.gaming.bullets.noiseSuppression' | translate }}</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-muted-foreground">
|
||||
<svg
|
||||
@@ -395,7 +392,7 @@
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
<span>Full-resolution screen sharing at high FPS</span>
|
||||
<span>{{ 'pages.home.gaming.bullets.screenShare' | translate }}</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-muted-foreground">
|
||||
<svg
|
||||
@@ -411,7 +408,7 @@
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
<span>Send replays and screenshots with no file size limit</span>
|
||||
<span>{{ 'pages.home.gaming.bullets.fileTransfers' | translate }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -422,11 +419,11 @@
|
||||
ngSrc="/images/screenshots/screenshare_gaming.png"
|
||||
fill
|
||||
priority
|
||||
alt="Toju gaming screen sharing preview"
|
||||
[attr.alt]="'pages.home.gaming.imageAlt' | translate"
|
||||
class="object-cover"
|
||||
/>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-background/80 via-transparent to-transparent"></div>
|
||||
<div class="absolute bottom-4 left-4 text-sm text-muted-foreground/60">Game on. No limits.</div>
|
||||
<div class="absolute bottom-4 left-4 text-sm text-muted-foreground/60">{{ 'pages.home.gaming.caption' | translate }}</div>
|
||||
</div>
|
||||
<!-- Glow effect -->
|
||||
<div class="absolute -inset-4 bg-purple-600/5 rounded-3xl blur-2xl -z-10"></div>
|
||||
@@ -457,22 +454,21 @@
|
||||
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2"
|
||||
/>
|
||||
</svg>
|
||||
Self-Hostable
|
||||
{{ 'pages.home.selfHostable.badge' | translate }}
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-5xl font-bold text-foreground mb-6">
|
||||
Your infrastructure,<br />
|
||||
<span class="gradient-text">your rules.</span>
|
||||
{{ 'pages.home.selfHostable.titleLine1' | translate }}<br />
|
||||
<span class="gradient-text">{{ 'pages.home.selfHostable.titleLine2' | translate }}</span>
|
||||
</h2>
|
||||
<p class="text-lg text-muted-foreground leading-relaxed mb-8">
|
||||
Toju uses a lightweight coordination server to help peers find each other - that's it. Your actual conversations never touch a server. Want
|
||||
even more control? Run your own coordination server in minutes. Full independence, zero compromises.
|
||||
{{ 'pages.home.selfHostable.description' | translate }}
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<a
|
||||
routerLink="/what-is-toju"
|
||||
class="inline-flex items-center gap-2 px-6 py-3 rounded-xl border border-border/50 bg-card/50 text-foreground font-medium hover:border-purple-500/30 transition-all"
|
||||
>
|
||||
Learn how it works
|
||||
{{ 'common.actions.learnHowItWorks' | translate }}
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
fill="none"
|
||||
@@ -500,7 +496,7 @@
|
||||
height="16"
|
||||
class="w-4 h-4 object-contain"
|
||||
/>
|
||||
View source code
|
||||
{{ 'common.actions.viewSourceCode' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -511,22 +507,22 @@
|
||||
<section class="relative py-24">
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-purple-950/20 via-violet-950/30 to-purple-950/20"></div>
|
||||
<div class="relative container mx-auto px-6 text-center section-fade">
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-foreground mb-4">Ready to take back your conversations?</h2>
|
||||
<p class="text-muted-foreground text-lg mb-8 max-w-lg mx-auto">Join thousands choosing privacy, freedom, and real connection.</p>
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-foreground mb-4">{{ 'pages.home.cta.title' | translate }}</h2>
|
||||
<p class="text-muted-foreground text-lg mb-8 max-w-lg mx-auto">{{ 'pages.home.cta.description' | translate }}</p>
|
||||
<div class="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
@if (downloadUrl()) {
|
||||
<a
|
||||
[href]="downloadUrl()"
|
||||
class="inline-flex items-center gap-2 px-8 py-4 rounded-xl bg-gradient-to-r from-purple-600 to-violet-600 text-white font-semibold hover:from-purple-500 hover:to-violet-500 transition-all shadow-2xl shadow-purple-500/25"
|
||||
>
|
||||
Download for {{ detectedOS().name }}
|
||||
{{ 'common.actions.downloadFor' | translate:{ os: getDetectedOsLabel() } }}
|
||||
</a>
|
||||
} @else {
|
||||
<a
|
||||
routerLink="/downloads"
|
||||
class="inline-flex items-center gap-2 px-8 py-4 rounded-xl bg-gradient-to-r from-purple-600 to-violet-600 text-white font-semibold hover:from-purple-500 hover:to-violet-500 transition-all shadow-2xl shadow-purple-500/25"
|
||||
>
|
||||
Download Toju
|
||||
{{ 'common.actions.downloadBrand' | translate }}
|
||||
</a>
|
||||
}
|
||||
<a
|
||||
@@ -535,7 +531,7 @@
|
||||
rel="noopener"
|
||||
class="inline-flex items-center gap-2 px-8 py-4 rounded-xl border border-border/50 bg-card/50 text-foreground font-medium hover:border-purple-500/30 transition-all"
|
||||
>
|
||||
Try in Browser
|
||||
{{ 'common.actions.tryInBrowser' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { isPlatformBrowser, NgOptimizedImage } from '@angular/common';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { ReleaseService, DetectedOS } from '../../services/release.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
@@ -20,6 +21,7 @@ import { ParallaxDirective } from '../../directives/parallax.directive';
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgOptimizedImage,
|
||||
TranslateModule,
|
||||
RouterLink,
|
||||
AdSlotComponent,
|
||||
ParallaxDirective
|
||||
@@ -28,7 +30,7 @@ import { ParallaxDirective } from '../../directives/parallax.directive';
|
||||
})
|
||||
export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
readonly detectedOS = signal<DetectedOS>({
|
||||
name: 'Linux',
|
||||
key: 'linux',
|
||||
icon: '🐧',
|
||||
filePattern: /\.AppImage$/i,
|
||||
ymlFile: 'latest-linux.yml'
|
||||
@@ -40,13 +42,10 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private readonly seoService = inject(SeoService);
|
||||
private readonly scrollAnimation = inject(ScrollAnimationService);
|
||||
private readonly platformId = inject(PLATFORM_ID);
|
||||
private readonly translate = inject(TranslateService);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seoService.update({
|
||||
title: 'Free Peer-to-Peer Voice, Video & Chat',
|
||||
description:
|
||||
'Toju is a free, open-source, peer-to-peer communication app. Crystal-clear voice calls, unlimited screen sharing, '
|
||||
+ 'no file size limits, complete privacy.',
|
||||
this.seoService.updateFromTranslations('pages.home.seo', {
|
||||
url: 'https://toju.app/'
|
||||
});
|
||||
|
||||
@@ -74,4 +73,8 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
ngOnDestroy(): void {
|
||||
this.scrollAnimation.destroy();
|
||||
}
|
||||
|
||||
getDetectedOsLabel(): string {
|
||||
return this.translate.instant(`common.os.${this.detectedOS().key}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user