Cross-platform speech donation application built with Tauri v2, React, and TypeScript. Supports iOS, Android, macOS, Windows, and Linux.
This is the Tauri implementation that replaces the .NET MAUI version. See TAURI_MIGRATION_PLAN.md for migration details.
- Node.js 18+ and pnpm (package manager)
- Rust (latest stable)
- Tauri CLI:
cargo install tauri-cli
Platform-specific:
- iOS/macOS: Xcode 15+
- Android: Android Studio, Android SDK 21+
- Windows: Visual Studio Build Tools
- Linux: Development packages (see Tauri prerequisites)
# Install dependencies
pnpm install
# Desktop development (hot reload)
pnpm tauri dev
# Build for desktop
pnpm tauri build
# Mobile development
pnpm tauri ios dev # iOS
pnpm tauri android dev # Android
# Mobile build
pnpm tauri ios build
pnpm tauri android buildThe app requires the recorder backend to be running for schedules, themes, and uploads.
From the backend repository:
cd ../Kielipankki-donatespeech-backend/recorder-backend
./setup-local.shThe backend runs on http://localhost:8000 by default.
Verify backend is reachable:
curl http://localhost:8000/v1/schedule
curl http://localhost:8000/v1/themeThe app uses Tauri build profiles to configure the API endpoint:
- Development (
tauri.conf.json): Useshttp://localhost:8000 - Release (
tauri.conf.release.json): Uses Azure dev endpoint
Build with different profiles:
# Development build (localhost:8000)
pnpm tauri:dev # Desktop
pnpm tauri:android:dev # Android
# Production build (Azure endpoint)
pnpm tauri:build # Desktop
pnpm tauri:android:build # AndroidTip (test on your own machine against release API):
pnpm tauri:dev:release-apiThis runs tauri dev with src-tauri/tauri.conf.release.json, so you get hot
reload while calling the release API endpoint.
Change API endpoint: Edit the plugins.recorder.apiBaseUrl in:
src-tauri/tauri.conf.json(development)src-tauri/tauri.conf.release.json(production)
Verify configuration: The console will log the API URL on startup:
Initializing API client with base URL: http://localhost:8000
Run pnpm tauri dev for hot-reload development mode.
Android Emulator:
# List available emulators
emulator -list-avds
# Start an emulator
emulator -avd <emulator_name>
# Run app
pnpm tauri android devAndroid Localhost: The app automatically remaps localhost and 127.0.0.1
to 10.0.2.2 (Android emulator's host address).
Physical Android Device:
- Find your computer's LAN IP:
ifconfig | grep "inet " | grep -v 127.0.0.1(macOS/Linux) - Start backend on all interfaces:
python -m uvicorn main:app --host 0.0.0.0 --port 8000 - Update API URL in
src-tauri/src/lib.rstohttp://YOUR_IP:8000 - Ensure firewall allows port 8000
pnpm tauri ios deviOS automatically handles localhost properly on simulators. For physical devices, use the same LAN IP approach as Android.
The app shows onboarding pages (welcome and terms) to first-time users. To reset during development:
// In browser DevTools console (F12 or Cmd+Option+I):
localStorage.removeItem("onboardingCompleted")Then refresh the app (F5 or Cmd+R).
The app supports 9 languages. Language preference is stored in localStorage and persists across sessions. See LOCALIZATION_COVERAGE.md for details.
You can export all locale strings from src/locales/*.ftl to a spreadsheet-like
table and import edited values back to the .ftl files.
NPM commands:
# Export to CSV
pnpm locales:table:export
# Export to TSV
pnpm locales:table:export:tsv
# Import from CSV
pnpm locales:table:importDirect script usage:
# Export
node scripts/ftl-table-sync.js export --out locales.csv
node scripts/ftl-table-sync.js export --out locales.tsv
# Import
node scripts/ftl-table-sync.js import --in locales.csv
node scripts/ftl-table-sync.js import --in locales.tsvNotes:
- The first table column must be
key. - Locale columns are inferred from the header names (for example
fi,nn,sv). - Export uses
fi.ftlas the source-of-truth key list (or the first locale iffiis missing). - Multiline Fluent values are preserved through export/import.
The web content-creator interface is deployed with GitHub Actions to Azure Static Web Apps.
Workflow file:
.github/workflows/deploy-azure-static-web-app.yml
One-time setup:
- Create (or reuse) an Azure Static Web App in the labs subscription.
- In the Azure portal, copy the deployment token for that Static Web App.
- Add a GitHub Actions repository secret named
AZURE_STATIC_WEB_APPS_API_TOKENwith that token. - Ensure backend CORS allows your Static Web App origin.
The workflow builds in web mode using:
VITE_PLATFORM_MODE=webVITE_BASE_PATH=/VITE_API_BASE_URL=https://ca-recorder-backend-dev.politedune-2911b299.northeurope.azurecontainerapps.io
Default frontend URL format:
https://<your-static-web-app-name>.azurestaticapps.net
-
Build release APK/AAB:
pnpm tauri android build --release
-
Signed build artifacts will be in
src-tauri/gen/android/app/build/outputs/ -
Upload to Google Play Console and publish to your desired track (internal, alpha, beta, or production)
Note: Ensure you have proper signing keys configured in Android Studio or via Tauri configuration.
-
Build release IPA:
pnpm tauri ios build --release
-
Submit using Apple Transporter or Xcode
-
Once processed by App Store Connect, publish to TestFlight or production
Note: Requires valid Apple Developer account and proper provisioning profiles.
macOS:
pnpm tauri build --target universal-apple-darwinProduces .dmg and .app in src-tauri/target/release/bundle/
Windows:
pnpm tauri buildProduces .msi installer in src-tauri/target/release/bundle/
Linux:
pnpm tauri buildProduces .deb, .AppImage, or other formats depending on configuration.
Trunk-based development: the latest development version is in the
feature/tauri-migration branch during migration, then will move to the default
main branch. Releases are tagged with semantic versioning.
App version is configured in:
package.json→ Frontend versionsrc-tauri/Cargo.toml→ Rust/Tauri versionsrc-tauri/tauri.conf.json→ App bundle version
Problem: App can't fetch themes/schedules
Solutions:
- Verify backend is running:
curl http://localhost:8000/v1/theme - Check if backend allows HTTP connections
- For physical devices, verify:
- Device is on same network as development machine
- Firewall allows port 8000
- API URL points to your LAN IP, not localhost
Problem: Build fails with SDK errors
Solutions:
- Ensure Android SDK 21+ is installed
- Set
ANDROID_HOMEandNDK_HOMEenvironment variables - Accept all Android SDK licenses:
sdkmanager --licenses
Problem: Build fails with Xcode errors
Solutions:
- Ensure Xcode 15+ is installed
- Install Xcode Command Line Tools:
xcode-select --install - Open project in Xcode and resolve signing issues
Problem: Recording fails or produces no audio
Solutions:
- Check microphone permissions are granted
- Verify no other app is using the microphone
- On mobile, ensure app has proper audio session configuration
- Check console logs for detailed error messages
- Frontend: React 19 + TypeScript + TailwindCSS
- Backend: Tauri v2 (Rust)
- Database: SQLite (via rusqlite)
- Audio: tauri-plugin-audio-recorder (WAV on desktop, M4A on mobile)
- Localization: Fluent (9 languages: Finnish, 2 Norwegian, Swedish, 5 Sámi variants)
- Navigation: React Router v7
Recording Format:
- Desktop (macOS/Windows/Linux): WAV (PCM) → Converted to FLAC for upload
- Mobile (iOS/Android): M4A (AAC) → Uploaded as-is
- Migration Plan: TAURI_MIGRATION_PLAN.md - Feature parity tracking
- Localization: LOCALIZATION_COVERAGE.md - Language support details
When adding features:
- Follow the migration plan priorities
- Commit per feature/bullet point (conventional commits format)
- Ensure all 9 language files are updated with new strings
- Test on at least 2 platforms (desktop + mobile)
- Update documentation as needed
See LICENSE file in repository root.