Skip to content
Snippets Groups Projects
Commit cdb11fd5 authored by Lisa Marie Fuhrmann's avatar Lisa Marie Fuhrmann :computer:
Browse files

feat: Added language input for Freitext and updated README for usage instructions

parent 67076cec
Branches main
No related tags found
No related merge requests found
# Gesundheitsportal - PWA (Progressive Web App)
Das **Gesundheitsportal** ist eine Progressive Web App (PWA), die Sprachbarrieren im Gesundheitswesen überwindet. Sie ermöglicht eine einfache Übersetzung von medizinischen Anamnesefragen und Freitexten zwischen verschiedenen Sprachen, insbesondere Deutsch und Englisch. Die App richtet sich an medizinisches Personal sowie Patienten und stellt sicher, dass wichtige Informationen ohne Sprachbarrieren geteilt werden können.
Das **Gesundheitsportal** ist eine Progressive Web App (PWA), die Sprachbarrieren im Gesundheitswesen abbaut. Nutzer können damit medizinische Anamnesefragen und Freitext in verschiedenen Sprachen (z. B. Deutsch und Englisch) übersetzen. Die App unterstützt sowohl medizinisches Personal als auch Patienten, damit wichtige Informationen verständlich ausgetauscht werden können.
> **Hinweis**: Teile des Quellcodes (inklusive des DeepL-Integrationscodes) wurden in Zusammenarbeit mit [ChatGPT](https://openai.com) erstellt, um den Entwicklungsprozess zu beschleunigen und Unterstützung bei bestimmten Codestellen zu erhalten.
> **Hinweis**: Teile des Codes (einschließlich DeepL-Integration) wurden gemeinsam mit [ChatGPT](https://openai.com) entwickelt, um den Entwicklungsprozess zu beschleunigen.
## Features
- **Anamnese**: Eine Schritt-für-Schritt-Übersetzung von medizinischen Anamnesefragen von Deutsch nach Englisch und umgekehrt.
- **Freitextübersetzung**: Übersetze Text zwischen Deutsch und Englisch in Echtzeit.
- **Offline-Verfügbarkeit**: Da es sich um eine PWA handelt, kannst du die App auch offline nutzen. Sie wird automatisch heruntergeladen und funktioniert auch ohne aktive Internetverbindung.
- **Multi-Language Support**: Die App unterstützt die Übersetzung von Texten in mehrere Sprachen (aktuell Deutsch und Englisch).
- **Einfache Benutzeroberfläche**: Eine benutzerfreundliche Oberfläche sorgt dafür, dass auch Nutzer ohne technische Vorkenntnisse die App problemlos verwenden können.
- **Anamnese**: Schrittweises Übersetzen von Fragen (Deutsch Englisch).
- **Freitextübersetzung**: Freitexte zwischen Deutsch und Englisch, Italienisch oder Spanisch in Echtzeit, inkl. Mikrofon-Unterstützung für Spracheingaben.
- **Benutzerfreundliche UI**: Intuitive Oberfläche, leicht bedienbar ohne Vorkenntnisse.
---
## Installation & Setup
### 1. Lokale Entwicklung (für Entwicklung und Tests)
### 1. Entwicklung
1. **Klonen des Repositories**:
Klone das Repository mit dem folgenden Befehl:
......@@ -58,7 +58,7 @@ Wenn du die App für den **Produktionsbetrieb** bereitstellen möchtest (zum Bei
- Die **Startseite** enthält zwei Hauptaktionen:
- **„Anamnese starten“**: Beginnt den Übersetzungsprozess für medizinische Anamnesefragen.
- **„Freitextübersetzung“**: Hier können Benutzer Text eingeben, der dann zwischen Deutsch und Englisch übersetzt wird.
- **„Freitextübersetzung“**: Hier können Benutzer Text eingeben, der dann zwischen Deutsch und einer anderen Sprache übersetzt wird.
### 2. **Anamnese**
......@@ -67,14 +67,13 @@ Wenn du die App für den **Produktionsbetrieb** bereitstellen möchtest (zum Bei
### 3. **Freitextübersetzung**
- **Deutsch → Englisch**: Das medizinische Personal gibt den Text auf Deutsch ein und erhält die englische Übersetzung.
- **Englisch → Deutsch**: Der Patient gibt den Text auf Englisch ein und erhält die deutsche Übersetzung.
- Das medizinische Personal gibt den Text auf Deutsch ein und erhält die Übersetzung.
- Der Patient gibt den Text h ein und erhält die deutsche Übersetzung.
### 4. **PWA-Funktionalität**
- **Offline-Funktionalität**: Nach dem ersten Laden der App wird sie für den Offline-Betrieb zwischengespeichert.
- **Offline-Funktionalität (zukünftig)**: Nach dem ersten Laden der App wird sie für den Offline-Betrieb zwischengespeichert.
- **Installierbar**: Benutzer können die PWA auf ihre Geräte installieren, indem sie auf das PWA-Symbol in der Adressleiste oder im Menü klicken.
- **Service Worker**: Für die Offline-Unterstützung sorgt der Service Worker, der die Ressourcen und Übersetzungsdaten speichert.
---
......@@ -84,7 +83,7 @@ Wenn du die App für den **Produktionsbetrieb** bereitstellen möchtest (zum Bei
- **React**: Die gesamte Anwendung ist mit React erstellt, was eine modulare und wartbare Struktur ermöglicht.
- **DeepL API**: Die Übersetzungen werden über die **DeepL API** durchgeführt. API-Schlüssel müssen für die Nutzung in einer `.env`-Datei hinterlegt werden.
- **PWA**: Um die App als Progressive Web App bereitzustellen, wird der Service Worker von `workbox` genutzt. Dies ermöglicht Offline-Funktionalität, Caching und mehr.
- **PWA**: Um die App als Progressive Web App bereitzustellen.
### 2. **Verzeichnisstruktur**
......@@ -92,7 +91,8 @@ Wenn du die App für den **Produktionsbetrieb** bereitstellen möchtest (zum Bei
- `pages/` - Die Hauptseiten (Startseite, Anamnese, Freitext).
- `components/` - Wiederverwendbare UI-Komponenten.
- `assets/` - Grafiken, Icons und andere statische Ressourcen.
- `public/` - Statische Ressourcen, die nicht durch React verarbeitet werden (z. B. das Manifest für PWA, Service Worker).
- `styles/`- CSS-Dateien
- `public/` - Statische Ressourcen, die nicht durch React verarbeitet werden (z. B. das Manifest für PWA).
- `.env` - Umgebungsvariablen wie API-Schlüssel.
---
......@@ -115,8 +115,7 @@ Wenn du die App für den **Produktionsbetrieb** bereitstellen möchtest (zum Bei
- **Responsive Layout**:
Momentan basiert das Layout auf festen Pixelwerten und ist primär für Desktop-Auflösungen ausgelegt. Zukünftig planen wir, mithilfe von flexiblen CSS-Techniken (z. B. Flexbox oder CSS Grid) die App für alle Bildschirmgrößen (Smartphones, Tablets, Desktops) zu optimieren, sodass eine durchgängige Responsive-Experience gewährleistet wird.
- **Mehrsprachige Unterstützung**: Derzeit sind nur wenige Sprachen unterstützt. Weitere Sprachen könnten hinzugefügt werden, basierend auf der API von DeepL.
- **Erweiterte Fehlerbehandlung**: Verbesserte Rückmeldungen bei API-Fehlern, z. B. durch Anzeige von Fehlermeldungen im UI.
- **Benutzerfeedback**: Hinzufügen einer Möglichkeit für Benutzer, Feedback zu den Übersetzungen zu geben und die Qualität zu bewerten.
- **Optimierung der App**: Performance-Verbesserungen für schnelle Ladezeiten auch bei langsamen Internetverbindungen.
- **Optimierung der App**: Performance-Verbesserungen für schnelle Ladezeiten auch bei langsamen Internetverbindungen sowie Offline-FUnktionalität.
import React, { useState } from "react";
import React, { useState, useRef, useEffect } from "react";
import { Link } from "react-router-dom";
import arrowBoldForvard from "../assets/icons/arrow-bold-forward.svg";
......@@ -17,41 +17,92 @@ export const Freitext = () => {
const [germanTranslation, setGermanTranslation] = useState("");
const [foreignTranslation, setForeignTranslation] = useState("");
const [isListeningDE, setIsListeningDE] = useState(false);
const recognitionRefDE = useRef(null);
const [isListeningForeign, setIsListeningForeign] = useState(false);
const recognitionRefForeign = useRef(null);
useEffect(() => {
const SpeechRecognition =
window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
// Für Deutsch
const recDE = new SpeechRecognition();
recDE.lang = "de-DE";
recDE.interimResults = false;
recDE.maxAlternatives = 1;
recognitionRefDE.current = recDE;
// Für Fremdsprache (z. B. Englisch)
const recForeign = new SpeechRecognition();
recForeign.lang = "en-US";
recForeign.interimResults = false;
recForeign.maxAlternatives = 1;
recognitionRefForeign.current = recForeign;
}
}, []);
const startListeningDE = () => {
if (recognitionRefDE.current && !isListeningDE) {
recognitionRefDE.current.start();
setIsListeningDE(true);
recognitionRefDE.current.onresult = (e) => {
const transcript = e.results[0][0].transcript;
setGermanInput((prev) => (prev ? `${prev} ${transcript}` : transcript));
};
recognitionRefDE.current.onend = () => setIsListeningDE(false);
}
};
const startListeningForeign = () => {
if (recognitionRefForeign.current && !isListeningForeign) {
recognitionRefForeign.current.start();
setIsListeningForeign(true);
recognitionRefForeign.current.onresult = (e) => {
const transcript = e.results[0][0].transcript;
setForeignInput((prev) => (prev ? `${prev} ${transcript}` : transcript));
};
recognitionRefForeign.current.onend = () => setIsListeningForeign(false);
}
};
const handleTranslateBoth = async () => {
try {
if (germanInput.trim()) {
const paramsDEtoForeign = new URLSearchParams();
paramsDEtoForeign.append("auth_key", DEEPL_API_KEY);
paramsDEtoForeign.append("text", germanInput);
paramsDEtoForeign.append("source_lang", "DE");
paramsDEtoForeign.append("target_lang", language.toUpperCase());
const response1 = await fetch(DEEPL_URL, {
const params1 = new URLSearchParams();
params1.append("auth_key", DEEPL_API_KEY);
params1.append("text", germanInput);
params1.append("source_lang", "DE");
params1.append("target_lang", language.toUpperCase());
const r1 = await fetch(DEEPL_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: paramsDEtoForeign,
body: params1,
});
const data1 = await response1.json();
if (data1?.translations?.[0]?.text) {
setForeignTranslation(data1.translations[0].text);
const d1 = await r1.json();
if (d1?.translations?.[0]?.text) {
setForeignTranslation(d1.translations[0].text);
}
}
if (foreignInput.trim()) {
const paramsForeignToDE = new URLSearchParams();
paramsForeignToDE.append("auth_key", DEEPL_API_KEY);
paramsForeignToDE.append("text", foreignInput);
paramsForeignToDE.append("source_lang", language.toUpperCase());
paramsForeignToDE.append("target_lang", "DE");
const response2 = await fetch(DEEPL_URL, {
const params2 = new URLSearchParams();
params2.append("auth_key", DEEPL_API_KEY);
params2.append("text", foreignInput);
params2.append("source_lang", language.toUpperCase());
params2.append("target_lang", "DE");
const r2 = await fetch(DEEPL_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: paramsForeignToDE,
body: params2,
});
const data2 = await response2.json();
if (data2?.translations?.[0]?.text) {
setGermanTranslation(data2.translations[0].text);
const d2 = await r2.json();
if (d2?.translations?.[0]?.text) {
setGermanTranslation(d2.translations[0].text);
}
}
} catch (error) {
......@@ -85,7 +136,6 @@ export const Freitext = () => {
</select>
</div>
</div>
<div className="header__title">
<Link to="/" style={{ color: "white", textDecoration: "none" }}>
Gesundheitsportal
......@@ -100,6 +150,13 @@ export const Freitext = () => {
Eingabe des medizinischen Personals (Deutsch)
</h2>
<div className="freitext-page__box-left-top">
<button
className="freitext-page__mic-button"
onClick={startListeningDE}
disabled={isListeningDE}
>
{isListeningDE ? "Aufnahme läuft..." : "🎤 Mikro"}
</button>
<textarea
className="freitext-page__input"
placeholder="Geben Sie Ihren Text auf Deutsch ein..."
......@@ -107,6 +164,7 @@ export const Freitext = () => {
onChange={(e) => setGermanInput(e.target.value)}
/>
</div>
<div className="freitext-page__box-right-top">
<div className="freitext-page__translation-output">
{foreignTranslation || "Übersetzung erscheint hier..."}
......@@ -117,6 +175,13 @@ export const Freitext = () => {
Eingabe des Patienten (Fremdsprache)
</h2>
<div className="freitext-page__box-left-bottom">
<button
className="freitext-page__mic-button"
onClick={startListeningForeign}
disabled={isListeningForeign}
>
{isListeningForeign ? "Aufnahme läuft..." : "🎤 Mikro"}
</button>
<textarea
className="freitext-page__input"
placeholder="Enter your text here..."
......@@ -124,6 +189,7 @@ export const Freitext = () => {
onChange={(e) => setForeignInput(e.target.value)}
/>
</div>
<div className="freitext-page__box-right-bottom">
<div className="freitext-page__translation-output">
{germanTranslation || "Translation appears here..."}
......
......@@ -527,3 +527,27 @@
height: 20px;
filter: invert(1);
}
.freitext-page__mic-button {
position: absolute;
top: 10px;
left: 10px;
z-index: 3;
width: 80px;
height: 32px;
background-color: var(--color-blue-mid);
color: var(--color-white);
border: none;
border-radius: var(--border-radius-button);
cursor: pointer;
font-size: var(--font-size-small);
}
.freitext-page__input {
position: absolute;
z-index: 2;
top: 10px;
left: 100px;
width: 530px;
height: 130px;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment