Translation pipeline: translate tool, YAML-driven language infrastructure, auto-dispatch

- Add tools/translate: MiniMax/GPT-oss draft → GLM 5 review pipeline via OpenRouter
  Supports 10 templates + YAML, 21 languages, provider routing (Fireworks/Groq)
- portal/main.go: render() auto-picks localized templates (page_xx.tmpl)
  Removes per-handler Lookup boilerplate from landing/pricing/faq handlers
- base.tmpl: dynamic language dropdown from {{.Languages}}, current lang first
- en.yaml: lang_* keys define all 22 languages (YAML-driven, no hardcoded lists)
- All existing YAMLs: added language_name key
- New: tr.yaml (Turkish), translated templates for de/es/nl/da/ja
- Makefile: translate build target

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
James 2026-03-11 23:34:09 -04:00
parent 155d24ec2e
commit 7cbca827b3
31 changed files with 7676 additions and 857 deletions

View File

@ -13,10 +13,10 @@ FIPS := GOFIPS140=v1.0.0
BINDIR := bin
DEPLOY_DIR := /tank/inou
.PHONY: all clean deploy deploy-prod sync linux lab tools help list fips-check check-db test test-rbac import-dicom import-genome import-lab nuke-imaging decrypt
.PHONY: all clean deploy deploy-prod sync linux lab tools help list fips-check check-db test test-rbac import-dicom import-renpho import-lab nuke-imaging decrypt
# Default: build everything
all: linux lab $(BINDIR)/import-genome $(BINDIR)/import-dicom $(BINDIR)/import-lab $(BINDIR)/nuke-imaging
all: linux lab $(BINDIR)/import-renpho $(BINDIR)/import-dicom $(BINDIR)/import-lab $(BINDIR)/nuke-imaging
# Linux binaries (native, FIPS)
linux: $(BINDIR)/viewer $(BINDIR)/portal $(BINDIR)/api
@ -39,11 +39,6 @@ $(BINDIR)/lab-scrape: ./scrape_mychart/main.go | $(BINDIR)
$(BINDIR)/lab-import: ./scrape_mychart/import.go | $(BINDIR)
$(FIPS) go build $(LDFLAGS) -o $@ ./scrape_mychart/import.go
# Genome import tool
import-genome: $(BINDIR)/import-genome
$(BINDIR)/import-genome: ./import-genome/*.go ./lib/*.go | $(BINDIR)
$(FIPS) go build $(LDFLAGS) -o $@ ./import-genome
# DICOM import tool
import-dicom: $(BINDIR)/import-dicom
$(BINDIR)/import-dicom: ./import-dicom/*.go ./lib/*.go | $(BINDIR)
@ -65,7 +60,7 @@ $(BINDIR)/import-lab: ./cmd/import-lab/*.go ./lib/*.go | $(BINDIR)
$(FIPS) go build $(LDFLAGS) -o $@ ./cmd/import-lab
# Debug tools (no FIPS needed)
tools: $(BINDIR)/decrypt $(BINDIR)/fips-check $(BINDIR)/dbquery $(BINDIR)/toolkit
tools: $(BINDIR)/decrypt $(BINDIR)/fips-check $(BINDIR)/dbquery $(BINDIR)/toolkit $(BINDIR)/translate
decrypt: $(BINDIR)/decrypt
$(BINDIR)/decrypt: ./tools/decrypt/*.go ./lib/*.go | $(BINDIR)
@ -79,6 +74,10 @@ toolkit: $(BINDIR)/toolkit
$(BINDIR)/toolkit: ./tools/toolkit/*.go ./lib/*.go | $(BINDIR)
go build -o $@ ./tools/toolkit
translate: $(BINDIR)/translate
$(BINDIR)/translate: ./tools/translate/*.go | $(BINDIR)
go build -o $@ ./tools/translate
fips-check: $(BINDIR)/fips-check
$(BINDIR)/fips-check: ./tools/fips-check/*.go | $(BINDIR)
go build -o $@ ./tools/fips-check
@ -120,7 +119,7 @@ deploy-prod: check-db all $(BINDIR)/decrypt $(BINDIR)/fips-check
ssh $(PROD_HOST) "$(DEPLOY_DIR)/stop.sh"
ssh $(PROD_HOST) "mkdir -p $(DEPLOY_DIR)/bin $(DEPLOY_DIR)/templates $(DEPLOY_DIR)/static $(DEPLOY_DIR)/lang"
scp $(BINDIR)/viewer $(BINDIR)/portal $(BINDIR)/api $(PROD_HOST):$(DEPLOY_DIR)/bin/
scp $(BINDIR)/import-genome $(BINDIR)/import-dicom $(BINDIR)/import-lab $(BINDIR)/nuke-imaging $(BINDIR)/fips-check $(PROD_HOST):$(DEPLOY_DIR)/bin/
scp $(BINDIR)/import-renpho $(BINDIR)/import-dicom $(BINDIR)/import-lab $(BINDIR)/nuke-imaging $(BINDIR)/fips-check $(PROD_HOST):$(DEPLOY_DIR)/bin/
scp $(BINDIR)/lab-* $(PROD_HOST):$(DEPLOY_DIR)/bin/ 2>/dev/null || true
rsync -avz --delete portal/templates/ $(PROD_HOST):$(DEPLOY_DIR)/templates/
rsync -avz portal/static/ $(PROD_HOST):$(DEPLOY_DIR)/static/
@ -171,7 +170,7 @@ help:
@echo " make check-db - Verify no direct DB access (runs auto on deploy)"
@echo " make test - Run integration tests (services must be running)"
@echo " make test-rbac - Run RBAC permission tests (API must be running)"
@echo " make import-genome - Build genome import tool"
@echo " make import-renpho - Build Renpho import tool"
@echo " make import-dicom - Build DICOM import tool"
@echo " make import-lab - Build lab import tool"
@echo " make tools - Build debug tools (decrypt)"

View File

@ -1,196 +1,271 @@
language_name: Dansk
# Landing
headline_1: "Dine sundhedsdata."
headline_2: "Din AI."
headline_3: "Dine svar."
intro: "Upload billeddiagnostik, laboratorieresultater og mere. Forbind din AI for at hjælpe dig med at forstå, hvad du ser på."
email: "E-mail"
get_started: "Kom i gang"
data_yours: "Dine data forbliver dine"
never_training: "Bruges aldrig til træning"
never_training_desc: "Dine billeder bruges aldrig til at træne AI-modeller."
never_shared: "Deles aldrig"
never_shared_desc: "Vi deler aldrig dine data med nogen."
encrypted: "Krypteret lagring"
encrypted_desc: "Alle data krypteret i hvile."
delete: "Slet når som helst"
delete_desc: "Dine data, din kontrol."
headline_1: Dine sundhedsdata.
headline_2: Din AI.
headline_3: Dine svar.
intro: Upload billeder, laboratorieprøver og mere. Forbind din AI til at hjælpe dig med at forstå, hvad du kigger på.
email: E-mail
get_started: Kom i gang
data_yours: Dine data forbliver dine
never_training: Aldrig brugt til træning
never_training_desc: Dine billeder bruges aldrig til at træne AI-modeller.
never_shared: Aldrig delt
never_shared_desc: Vi deler aldrig dine data med nogen.
encrypted: Militærgrads kryptering
encrypted_desc: Hvilende og under transport. Dine data rejser aldrig ubeskyttet.
delete: Slet når som helst
delete_desc: Dine data, din kontrol.
# Verify
check_email: "Tjek din e-mail"
code_sent_to: "Vi har sendt en 6-cifret kode til"
verification_code: "Bekræftelseskode"
verify: "Bekræft"
use_different_email: "Brug en anden e-mail"
invalid_code: "Ugyldig eller udløbet kode. Prøv igen."
check_email: Tjek din e-mail
code_sent_to: Vi sendte en 6-cifret kode til
verification_code: Verifikationskode
verify: Verificér
use_different_email: Brug en anden e-mail
invalid_code: Ugyldig eller udløbet kode. Prøv igen.
# Onboard
create_dossier: "Opret din dosje"
create_profile_intro: "Fortæl os om dig selv for at komme i gang."
name: "Navn"
name_placeholder: "Dit navn"
date_of_birth: "Fødselsdato"
sex_at_birth: "Køn ved fødslen"
female: "Kvinde"
male: "Mand"
create_my_dossier: "Opret min dosje"
create_dossier: Opret dit dossier
create_profile_intro: Fortæl os om dig selv for at komme i gang.
name: Navn
name_placeholder: Dit navn
date_of_birth: Fødselsdato
sex_at_birth: Køn ved fødslen
female: Kvinde
male: Mand
create_my_dossier: Opret mit dossier
# Minor error
must_be_18: "Du skal være 18 for at oprette en konto"
minor_explanation: "Hvis du opretter dette for en anden, start med din egen profil først. Dette sikrer, at kun du kan få adgang til deres sundhedsdata."
minor_next_steps: "Efter at have oprettet din dosje kan du tilføje andre."
use_different_dob: "Brug en anden fødselsdato"
must_be_18: Du skal være 18 for at oprette en konto
minor_explanation: Hvis du opsætter dette for en anden, start med din egen profil først. Dette sikrer, at kun du kan få adgang til deres sundhedsdata.
minor_next_steps: Efter at have oprettet dit dossier, kan du tilføje andre.
use_different_dob: Brug en anden fødselsdato
# Minor login block
minor_login_blocked: "Du skal være 18 for at logge ind"
minor_ask_guardian: "Bed %s om adgang til din dosje."
minor_ask_guardian_generic: "Bed en forælder eller værge om adgang til din dosje."
minor_login_blocked: "You must be 18 to log in"
minor_ask_guardian: "Spørg %s om adgang til dit dossier."
minor_ask_guardian_generic: Spørg en forælder eller værge om adgang til dit dossier.
# Dashboard
dossiers: "Dosjer"
dossiers_intro: "Administrer sundhedsdata for dig selv eller andre"
you: "dig"
view: "Vis"
save: "Gem"
cancel: "Annuller"
add_dossier: "Tilføj dosje"
edit_dossier: "Rediger dosje"
care: "pleje"
logout: "Log ud"
dossiers: Dossiers
dossiers_intro: Administrer sundhedsdata for dig selv eller andre
you: dig
view: Se
save: Gem
cancel: Annuller
add_dossier: Tilføj dossier
edit_dossier: Rediger dossier
care: pleje
logout: Log ud
# Profile detail
back_to_dossiers: "Tilbage til dosjer"
born: "Født"
no_access_yet: "Kun du har adgang."
people_with_access: "Personer med adgang"
share_access: "Del adgang"
can_edit: "kan tilføje data"
remove: "Fjern"
back_to_dossiers: Tilbage til dossiers
born: Født
no_access_yet: Kun du har adgang.
people_with_access: Personer med adgang
share_access: Del adgang
manage_permissions: Administrer tilladelser
can_edit: kan tilføje data
remove: Fjern
confirm_revoke: "Fjern adgang?"
# Dossier sections
section_imaging: "Billeddiagnostik"
section_labs: "Lab"
section_uploads: "Uploads"
section_vitals: "Vitale tegn"
section_medications: "Medicin"
section_records: "Journaler"
section_journal: "Dagbog"
section_genetics: "Genetik"
section_privacy: "Privatliv"
section_imaging: Billeddiagnostik
section_labs: Laboratorier
section_uploads: Uploads
section_vitals: Værdier
section_medications: Medicin
section_records: Journaler
section_journal: Journal
section_checkin: Daglig Check-in
section_procedures: Procedurer
section_assessments: Vurderinger
section_genetics: Genetik
section_supplements: Kosttilskud
section_symptoms: Symptomer
section_hospitalizations: Indlæggelser
section_therapies: Terapier
section_consultations: Konsultationer
section_diagnoses: Diagnoser
section_exercise: Motion
section_nutrition: Ernæring
section_fertility: Frugtbarhed
section_notes: Noter
section_history: Sygdomshistorie
section_family_history: Familiehistorik
section_birth: Fødsel
section_devices: Enheder
section_providers: Udbudere
section_questions: Spørgsmål
section_privacy: Privatliv
# Daily Check-in
checkin_summary: Spor vitale værdier, medicin, symptomer
checkin_build_profile: Tilføj det, du vil spore
btn_vitals: Værdier
btn_medications: Medicin
btn_supplements: Kosttilskud
btn_exercise: Motion
# Plural forms (use %d for count)
slice_one: "%d slice"
slice_other: "%d slices"
series_one: "%d serie"
series_other: "%d serier"
order_one: "%d ordre"
order_other: "%d ordrer"
result_one: "%d resultat"
result_other: "%d resultater"
# Section summaries
imaging_summary: "%d undersøgelser · %d snit"
no_imaging: "Ingen billeddata"
no_lab_data: "Ingen labdata"
no_genetics: "Ingen genetiske data"
no_files: "Ingen filer"
no_upload_access: "You don't have permission to upload"
imaging_summary: "%d undersøgelser · %d slices"
no_imaging: Ingen billeddata
no_lab_data: Ingen laboratoriedata
no_files: Ingen filer
no_upload_access: Du har ikke tilladelse til at uploade
files_summary: "%d filer (%s)"
series_count: "%d serier"
vitals_desc: "Blodtryk, puls, SpO₂, vægt, blodsukker"
medications_desc: "Recepter og kosttilskud"
records_desc: "Kliniske noter og journaler"
journal_desc: "Symptomer, smerte og observationer"
vitals_desc: Blodtryk, puls, SpO₂, vægt, glukose
medications_desc: Recepter og kosttilskud
records_desc: Kliniske noter og journaler
journal_desc: Symptomer, smerter og observationer
# Buttons and actions
open_viewer: "Åbn visning"
manage: "Administrer"
open: Åbn
open_viewer: Åbn viewer
manage: Administrer
show_all_studies: "Vis alle %d undersøgelser..."
coming_soon: "Kommer snart"
coming_soon: Kommer snart
# Upload page
upload_files: "Upload sundhedsdata"
upload_files_intro: "Upload medicinsk billeddiagnostik, laboratorieresultater, genomfiler eller sundhedsrelaterede dokumenter."
upload_hint_broad: "DICOM, PDF, CSV, VCF og mere"
uploading: "Uploader..."
files_uploaded: "filer uploadet"
upload_scans: "Upload scanninger"
upload_scans_intro: "Upload en mappe med DICOM-filer fra din billedundersøgelse."
upload_drop: "Klik eller træk en mappe hertil"
upload_hint: "Kun DICOM-mapper"
upload_files: Upload sundhedsdata
upload_files_intro: Upload medicinsk billeddiagnostik, laboratorieresultater, genomfiler eller andre sundhedsrelaterede dokumenter.
upload_hint_broad: DICOM, PDF, CSV, VCF og mere
uploading: Uploader...
files_uploaded: filer uploadet
upload_scans: Upload scans
upload_scans_intro: Upload en mappe med DICOM-filer fra din billedundersøgelse.
upload_drop: Klik eller træk en mappe her
upload_hint: Kun DICOM-mapper
# Add profile
add_dossier_intro: "Tilføj nogen, hvis sundhedsdata du vil administrere."
email_optional: "E-mail (valgfrit)"
email_optional_hint: "Hvis de er 18, kan de logge ind selv"
your_relation: "Dit forhold til dem"
select_relation: "Vælg..."
i_provide_care: "Jeg yder pleje til denne person"
i_am_their: "Jeg er deres..."
add_dossier_intro: Tilføj nogen, hvis sundhedsdata du vil administrere.
email_optional: E-mail (valgfrit)
email_optional_hint: Hvis de er 18+, kan de logge ind selv
your_relation: Din relation til dem
select_relation: Vælg relation...
i_provide_care: Jeg giver pleje til denne person
# Share access
share_access_intro: "Inviter nogen til at få adgang"
their_relation: "Deres forhold til denne person"
can_add_data: "Kan tilføje data (kosttilskud, noter, osv.)"
send_invitation: "Send invitation"
back_to_dossier: "Tilbage til dosje"
share_access_intro: Inviter nogen til at få adgang
their_relation: Deres relation til denne person
can_add_data: Kan tilføje data
send_invitation: Send invitation
back_to_dossier: Tilbage til dossier
# Relations
my_role: "min rolle"
role: "role"
# Invitation email
invite_email_subject: "%s tilføjede dig til inou"
invite_email_body: "%s tilføjede din sundhedsdosje til inou, så du kan se og administrere dine medicinske data."
invite_email_cta: "Log ind for at se"
continue: "Fortsæt"
invite_email_subject: "%s har tilføjet dig til inou"
invite_email_body: "%s har tilføjet dit sundhedsdossier til inou, så du kan se og administrere dine medicinske data."
invite_email_cta: Log ind for at se
continue: Fortsæt
i_am_their: Jeg er deres...
# Access management
people_with_access_count: "personer med adgang"
view_audit_log: "Vis aktivitetslog"
export_data: "Download my data"
relation_with: "Forhold til"
audit_log: "Aktivitetslog"
audit_log_intro: "Aktivitetshistorik for"
audit_log_desc: "Spor hvem der har haft adgang til eller ændret denne dosje"
# Simple relation names (for display)
my_role: min rolle
role: rolle
section_privacy: Privatliv
people_with_access_count: personer med adgang
view_audit_log: Se revisionslog
export_data: Download mine data
relation_with: Relation med
audit_log: Revisionslog
audit_log_intro: Aktivitetshistorik for
audit_log_desc: Track hvem der fik adgang til eller redigerede dette dossier
# Install / Connect
install_title: "Forbind til Claude"
install_intro: "Opsæt inou-broen for at lade Claude analysere dine sundhedsdata"
# Permissions (RBAC)
permissions_title: Tilladelser
permissions_subtitle: Kontrollér hvem der kan få adgang til dette dossier og hvad de kan gøre
current_access: Nuværende adgang
grant_access: Giv adgang
no_grantees: Ingen andre har adgang til dette dossier.
person_email: E-mailadresse
person_email_hint: Hvis de ikke har en konto, vil de blive inviteret til at oprette en.
person_name: Navn
select_role: Vælg en rolle...
custom_role: Brugerdefinerede tilladelser
permissions: Tilladelser
op_read: Læs
op_write: Skriv
op_delete: Slet
op_manage: Administrer
grant: Giv adgang
revoke: Tilbagekald
role_descriptions: Rollebeskrivelser
ops_legend: Tilladelsesforklaring
op_read_desc: Se data
op_write_desc: Tilføj/rediger data
op_delete_desc: Fjern data
op_manage_desc: Administrer hvem der har adgang
permissions_updated: Tilladelser opdateret succesfuldt.
back: Tilbage
can_add_data: Kan tilføje data
install_title: Forbind til Claude
install_intro: Sæt inou bridge op for at lade Claude analysere dine sundhedsdata
install_step1: "Trin 1: Download"
install_step1_desc: "Hent broen til din platform"
install_download_intro: "Download inou-broen til dit operativsystem:"
install_step1_desc: Hent bridge til din platform
install_download_intro: "Download inou bridge til dit operativsystem:"
install_step2: "Trin 2: Konfigurer"
install_step2_desc: "Tilføj til Claude Desktop-konfigurationen"
install_config_intro: "Tilføj dette til din Claude Desktop-konfigurationsfil:"
install_step2_desc: Tilføj til Claude Desktop config
install_config_intro: "Tilføj dette til din Claude Desktop konfigurationsfil:"
install_step3: "Trin 3: Test"
install_step3_desc: "Bekræft forbindelsen"
install_test_intro: "Genstart Claude Desktop og spørg: 'Vis mig mine inou-profiler'"
nav_install: "Forbind til Claude"
nav_home: "Hjem"
install_step3_desc: Verificer forbindelsen
install_test_intro: "Genstart Claude Desktop og spørg: 'Vis mig mine inou profiler'"
nav_install: Forbind til Claude
nav_home: Hjem
pending: afventer
rate_limit_exceeded: For mange tilmeldingsforsøg fra din placering. Prøv igen i morgen.
section_genetics: Genetik
no_genetics: Ingen genetiske data
# Status
pending: "afventer"
rate_limit_exceeded: "For mange tilmeldingsforsøg fra din placering. Prøv igen i morgen."
# Sex display
sex_0: "ukendt"
sex_1: "mand"
sex_2: "kvinde"
sex_9: "andet"
sex_0: ukendt
sex_1: mand
sex_2: kvinde
sex_9: andet
# Friend invite email
friend_invite_subject: "Tjek dette ud — %s"
friend_invite_p1: "Jeg bruger <strong>inou</strong>, den sikre måde at opbevare sundhedsdata og udforske dem med AI. Det holder al min families sundhedsinformation ét sted — billedstudier, laboratorieresultater, journaler — og jeg tænkte, det måske også kunne være nyttigt for dig."
friend_invite_p2: "Den virkelige styrke ligger i at kunne bruge AI til at forstå det hele: forstå hvad en rapport faktisk betyder, opdage tendenser over tid, eller bare stille spørgsmål på almindeligt dansk og få klare svar."
friend_invite_btn: "Opdag inou"
friend_invite_dear: "Hej %s,"
rel_0: "du"
rel_1: "Forælder"
rel_2: "Barn"
rel_3: "Ægtefælle"
rel_4: "Søskende"
rel_5: "Værge"
rel_6: "Omsorgsgiver"
rel_7: "Coach"
rel_8: "Læge"
rel_9: "Ven"
rel_10: "Andet"
rel_98: "Andet"
rel_99: "Demo"
select_relation: "Vælg relation..."
friend_invite_p1: "Jeg har brugt <strong>inou</strong>, den sikre måde at opbevare sundhedsdata og udforske dem med AI. Det holder al min families sundhedsoplysninger ét sted — billedundersøgelser, laboratorieresultater, journaler — og jeg tænkte, du måske også ville finde det nyttigt."
friend_invite_p2: "Den virkelige styrke er at kunne bruge AI til at give det hele mening: forstå, hvad en rapport faktisk betyder, spotte tendenser over tid, eller bare stille spørgsmål i almindeligt sprog og få klare svar."
friend_invite_btn: Tjek inou ud
friend_invite_dear: "Kære %s,"
rel_0: dig
rel_1: Forælder
rel_2: Barn
rel_3: Ægtefælle
rel_4: Søskende
rel_5: Værge
rel_6: Omsorgsperson
rel_7: Coach
rel_8: Læge
rel_9: Ven
rel_10: Andet
rel_98: Andet
rel_99: Demo
select_relation: Vælg relation...
audit_dossier_added: "Nyt dossier for %s oprettet af %s"
audit_dossier_edited: "Dossier %s redigeret af %s"
audit_access_granted: "Adgang til %s givet til %s"
audit_dossier_created: "Konto oprettet af %s"
audit_access_revoked: "Adgang for %s til %s tilbagekaldt"
audit_file_upload: "Fil %s uploadet af %s"
audit_file_delete: "Fil %s slettet af %s"
audit_file_category_change: "Fil %s kategori ændret af %s"
audit_genome_import: "%s genetiske varianter importeret"
# Kategorier
# Categories (category000 = imaging, etc.)
category000: Billeddiagnostik
category001: Dokument
category002: Laboratorieresultat
@ -198,44 +273,68 @@ category003: Genom
category004: Upload
category005: Konsultation
category006: Diagnose
category007: Billedresultat
category008: EEG-resultat
category009: Vitalværdi
category007: Billeddiagnostisk fund
category008: EEG-fund
category009: Vitalparameter
category010: Motion
category011: Medicin
category012: Tilskud
category012: Kosttilskud
category013: Ernæring
category014: Fertilitet
category014: Frugtbarhed
category015: Symptom
category016: Note
category017: Sygehistorie
category018: Familiehistorie
category017: Sygdomshistorie
category018: Familiehistorik
category019: Kirurgi
category020: Hospitalsindlæggelse
category021: Fødselsdata
category020: Indlæggelse
category021: Fødselsjournal
category022: Medicinsk udstyr
category023: Terapi
category024: Vurdering
category025: Sundhedsudbyder
category025: Udbuder
category026: Spørgsmål
# Genome
genome_english_only: "Al genetisk information er på engelsk. Brug Claude til at diskutere det på dansk."
genome_variants: "varianter"
genome_hidden: "skjulte"
genome_english_only: ""
genome_variants: varianter
genome_hidden: skjult
genome_show_all_categories: "Vis alle %d kategorier"
# API
api_token: "API Token"
api_token_use: "[EN] Use this token to authenticate API requests:"
api_token_warning: "[EN] Keep this private. Anyone with this token can access your health data."
api_token_none: "[EN] Generate a token to access the API programmatically or connect AI assistants."
api_token_generate: "Generate Token"
api_token_regenerate: "Regenerate Token"
api_token_regenerate_confirm: "[EN] This will invalidate your current token. Any connected apps will need to be updated."
api_authentication: "Authentication"
api_auth_instructions: "[EN] Include your API token in the Authorization header:"
copy: "Copy"
relation: "Relation"
relation_to: "Til"
me: "Mig"
api_token: API-token
api_token_use: "Brug denne token til at godkende API-anmodninger:"
api_token_warning: Hold dette privat. Alle med denne token kan få adgang til dine sundhedsdata.
api_token_none: "Generer en token for at få adgang til API'et programmatisk eller forbinde AI-assistenter."
api_token_generate: Generer token
api_token_regenerate: Regenerer token
api_token_regenerate_confirm: Dette ugyldiggør din nuværende token. Alle forbundne apps skal opdateres.
api_authentication: Godkendelse
api_auth_instructions: "Inkluder din API-token i Authorization-headeren:"
copy: Kopiér
relation: Relation
relation_to: Til
me: Mig
# Languages (native names — do not translate)
lang_da: Dansk
lang_de: Deutsch
lang_en: English
lang_es: Español
lang_fi: Suomi
lang_fr: Français
lang_hi: हिन्दी
lang_id: Bahasa Indonesia
lang_it: Italiano
lang_ja: 日本語
lang_ko: 한국어
lang_nl: Nederlands
lang_no: Norsk
lang_pl: Polski
lang_pt: Português
lang_ru: Русский
lang_sv: Svenska
lang_th: ไทย
lang_tr: Türkçe
lang_uk: Українська
lang_vi: Tiếng Việt
lang_zh: 中文

View File

@ -1,196 +1,271 @@
language_name: Deutsch
# Landing
headline_1: "Ihre Gesundheitsdaten."
headline_2: "Ihre KI."
headline_3: "Ihre Antworten."
intro: "Laden Sie Bildgebung, Laborergebnisse und mehr hoch. Verbinden Sie Ihre KI, um zu verstehen, was Sie sehen."
email: "E-Mail"
get_started: "Loslegen"
data_yours: "Ihre Daten bleiben Ihre"
never_training: "Nie für Training verwendet"
never_training_desc: "Ihre Bilder werden nie zum Trainieren von KI-Modellen verwendet."
never_shared: "Nie geteilt"
never_shared_desc: "Wir teilen Ihre Daten mit niemandem."
encrypted: "Verschlüsselte Speicherung"
encrypted_desc: "Alle Daten werden verschlüsselt gespeichert."
delete: "Jederzeit löschen"
delete_desc: "Ihre Daten, Ihre Kontrolle."
headline_1: Deine Gesundheitsdaten.
headline_2: Deine KI.
headline_3: Deine Antworten.
intro: Lade Bildgebung, Laborwerte und mehr hoch. Verbinde deine KI, um dir zu helfen zu verstehen, was du anschaust.
email: E-Mail
get_started: Loslegen
data_yours: Deine Daten bleiben deine
never_training: Nie für Training verwendet
never_training_desc: Deine Bilder werden niemals zum Trainieren von KI-Modellen verwendet.
never_shared: Nie geteilt
never_shared_desc: Wir teilen deine Daten nie mit irgendjemandem.
encrypted: Militärische Verschlüsselung
encrypted_desc: Ruhend und unterwegs. Deine Daten reisen nie ungeschützt.
delete: Jederzeit löschen
delete_desc: Deine Daten, deine Kontrolle.
# Verify
check_email: "Überprüfen Sie Ihre E-Mail"
code_sent_to: "Wir haben einen 6-stelligen Code gesendet an"
verification_code: "Bestätigungscode"
verify: "Bestätigen"
use_different_email: "Andere E-Mail verwenden"
invalid_code: "Ungültiger oder abgelaufener Code. Bitte versuchen Sie es erneut."
check_email: Überprüfe deine E-Mail
code_sent_to: Wir haben einen 6-stelligen Code gesendet an
verification_code: Verifizierungscode
verify: Verifizieren
use_different_email: Andere E-Mail verwenden
invalid_code: Ungültiger oder abgelaufener Code. Bitte versuche es erneut.
# Onboard
create_dossier: "Erstellen Sie Ihr Dossier"
create_profile_intro: "Erzählen Sie uns von sich, um loszulegen."
name: "Name"
name_placeholder: "Ihr Name"
date_of_birth: "Geburtsdatum"
sex_at_birth: "Geschlecht bei Geburt"
female: "Weiblich"
male: "Männlich"
create_my_dossier: "Mein Dossier erstellen"
create_dossier: Erstelle dein Dossier
create_profile_intro: Erzähl uns etwas über dich, um loszulegen.
name: Name
name_placeholder: Dein Name
date_of_birth: Geburtsdatum
sex_at_birth: Geschlecht bei Geburt
female: Weiblich
male: Männlich
create_my_dossier: Mein Dossier erstellen
# Minor error
must_be_18: "Sie müssen 18 sein, um ein Konto zu erstellen"
minor_explanation: "Wenn Sie dies für jemand anderen einrichten, beginnen Sie zuerst mit Ihrem eigenen Profil. So stellen Sie sicher, dass nur Sie auf deren Gesundheitsdaten zugreifen können."
minor_next_steps: "Nach der Erstellung Ihres Dossiers können Sie weitere hinzufügen."
use_different_dob: "Anderes Geburtsdatum verwenden"
must_be_18: Du musst 18 sein, um ein Konto zu erstellen
minor_explanation: Wenn du dies für jemand anderen einrichtest, beginne zuerst mit deinem eigenen Profil. Dies stellt sicher, dass nur du auf ihre Gesundheitsdaten zugreifen kannst.
minor_next_steps: Nach dem Erstellen deines Dossiers kannst du andere hinzufügen.
use_different_dob: Anderes Geburtsdatum verwenden
# Minor login block
minor_login_blocked: "Sie müssen 18 sein, um sich anzumelden"
minor_ask_guardian: "Bitten Sie %s, auf Ihr Dossier zuzugreifen."
minor_ask_guardian_generic: "Bitten Sie einen Elternteil oder Vormund, auf Ihr Dossier zuzugreifen."
minor_login_blocked: Du musst 18 sein, um dich anzumelden
minor_ask_guardian: "Frage %s, um auf dein Dossier zuzugreifen."
minor_ask_guardian_generic: Frage einen Elternteil oder Erziehungsberechtigten, um auf dein Dossier zuzugreifen.
# Dashboard
dossiers: "Dossiers"
dossiers_intro: "Verwalten Sie Gesundheitsdaten für sich selbst oder andere"
you: "Sie"
view: "Ansehen"
save: "Speichern"
cancel: "Abbrechen"
add_dossier: "Dossier hinzufügen"
edit_dossier: "Dossier bearbeiten"
care: "Pflege"
logout: "Abmelden"
dossiers: Dossiers
dossiers_intro: Verwalte Gesundheitsdaten für dich oder andere
you: du
view: Ansehen
save: Speichern
cancel: Abbrechen
add_dossier: Dossier hinzufügen
edit_dossier: Dossier bearbeiten
care: Pflege
logout: Abmelden
# Profile detail
back_to_dossiers: "Zurück zu Dossiers"
born: "Geboren"
no_access_yet: "Nur Sie haben Zugriff."
people_with_access: "Personen mit Zugriff"
share_access: "Zugriff teilen"
can_edit: "kann Daten hinzufügen"
remove: "Entfernen"
confirm_revoke: "Zugriff entfernen?"
back_to_dossiers: Zurück zu Dossiers
born: Geboren
no_access_yet: Nur du hast Zugriff.
people_with_access: Personen mit Zugriff
share_access: Zugriff teilen
manage_permissions: Berechtigungen verwalten
can_edit: kann Daten hinzufügen
remove: Entfernen
confirm_revoke: "Zugriff entziehen?"
# Dossier sections
section_imaging: "Bildgebung"
section_labs: "Labor"
section_uploads: "Uploads"
section_vitals: "Vitalwerte"
section_medications: "Medikamente"
section_records: "Unterlagen"
section_journal: "Tagebuch"
section_genetics: "Genetik"
section_privacy: "Datenschutz"
section_imaging: Bildgebung
section_labs: Laborwerte
section_uploads: Uploads
section_vitals: Vitalwerte
section_medications: Medikamente
section_records: Aufzeichnungen
section_journal: Tagebuch
section_checkin: Tägliches Check-in
section_procedures: Eingriffe
section_assessments: Bewertungen
section_genetics: Genetik
section_supplements: Nahrungsergänzungsmittel
section_symptoms: Symptome
section_hospitalizations: Krankenhausaufenthalte
section_therapies: Therapien
section_consultations: Konsultationen
section_diagnoses: Diagnosen
section_exercise: Sport
section_nutrition: Ernährung
section_fertility: Fruchtbarkeit
section_notes: Notizen
section_history: Krankengeschichte
section_family_history: Familienanamnese
section_birth: Geburt
section_devices: Geräte
section_providers: Anbieter
section_questions: Fragen
section_privacy: Datenschutz
# Daily Check-in
checkin_summary: Tracke Vitalwerte, Medikamente, Symptome
checkin_build_profile: Füge hinzu, was du tracken möchtest
btn_vitals: Vitalwerte
btn_medications: Medikamente
btn_supplements: Nahrungsergänzungsmittel
btn_exercise: Sport
# Plural forms (use %d for count)
slice_one: "%d Slice"
slice_other: "%d Slices"
series_one: "%d Serie"
series_other: "%d Serien"
order_one: "%d Bestellung"
order_other: "%d Bestellungen"
result_one: "%d Ergebnis"
result_other: "%d Ergebnisse"
# Section summaries
imaging_summary: "%d Studien · %d Schichten"
no_imaging: "Keine Bildgebungsdaten"
no_lab_data: "Keine Labordaten"
no_genetics: "Keine genetischen Daten"
no_files: "Keine Dateien"
no_upload_access: "You don't have permission to upload"
imaging_summary: "%d Studien · %d Slices"
no_imaging: Keine Bildgebungsdaten
no_lab_data: Keine Laborwerte
no_files: Keine Dateien
no_upload_access: Du hast keine Berechtigung zum Hochladen
files_summary: "%d Dateien (%s)"
series_count: "%d Serien"
vitals_desc: "Blutdruck, Herzfrequenz, SpO₂, Gewicht, Glukose"
medications_desc: "Rezepte und Nahrungsergänzungsmittel"
records_desc: "Klinische Notizen und Krankenakten"
journal_desc: "Symptome, Schmerzen und Beobachtungen"
vitals_desc: Blutdruck, Herzfrequenz, SpO₂, Gewicht, Glukose
medications_desc: Rezepte und Nahrungsergänzungsmittel
records_desc: Klinische Notizen und medizinische Aufzeichnungen
journal_desc: Symptome, Schmerzen und Beobachtungen
# Buttons and actions
open_viewer: "Viewer öffnen"
manage: "Verwalten"
open: Öffnen
open_viewer: Viewer öffnen
manage: Verwalten
show_all_studies: "Alle %d Studien anzeigen..."
coming_soon: "Demnächst"
coming_soon: Demnächst
# Upload page
upload_files: "Gesundheitsdaten hochladen"
upload_files_intro: "Laden Sie medizinische Bildgebung, Laborergebnisse, Genomdateien oder andere gesundheitsbezogene Dokumente hoch."
upload_hint_broad: "DICOM, PDF, CSV, VCF und mehr"
uploading: "Wird hochgeladen..."
files_uploaded: "Dateien hochgeladen"
upload_scans: "Scans hochladen"
upload_scans_intro: "Laden Sie einen Ordner mit DICOM-Dateien aus Ihrer Bildgebungsstudie hoch."
upload_drop: "Klicken oder Ordner hierher ziehen"
upload_hint: "Nur DICOM-Ordner"
upload_files: Gesundheitsdaten hochladen
upload_files_intro: Lade medizinische Bildgebung, Laborergebnisse, Genomdateien oder beliebige gesundheitsbezogene Dokumente hoch.
upload_hint_broad: DICOM, PDF, CSV, VCF und mehr
uploading: Wird hochgeladen...
files_uploaded: Dateien hochgeladen
upload_scans: Scans hochladen
upload_scans_intro: Lade einen Ordner mit DICOM-Dateien von deiner Bildgebungsstudie hoch.
upload_drop: Klicke oder ziehe einen Ordner hierher
upload_hint: Nur DICOM-Ordner
# Add profile
add_dossier_intro: "Fügen Sie jemanden hinzu, dessen Gesundheitsdaten Sie verwalten möchten."
email_optional: "E-Mail (optional)"
email_optional_hint: "Wenn sie 18 sind, können sie sich selbst anmelden"
your_relation: "Ihre Beziehung zu dieser Person"
select_relation: "Auswählen..."
i_provide_care: "Ich pflege diese Person"
i_am_their: "Ich bin deren..."
add_dossier_intro: Füge jemanden hinzu, dessen Gesundheitsdaten du verwalten möchtest.
email_optional: E-Mail (optional)
email_optional_hint: Wenn sie 18+ sind, können sie sich selbst einloggen
your_relation: Deine Beziehung zu ihnen
select_relation: Beziehung auswählen...
i_provide_care: Ich pflege diese Person
# Share access
share_access_intro: "Jemanden zum Zugriff einladen"
their_relation: "Deren Beziehung zu dieser Person"
can_add_data: "Kann Daten hinzufügen (Nahrungsergänzungsmittel, Notizen, usw.)"
send_invitation: "Einladung senden"
back_to_dossier: "Zurück zum Dossier"
share_access_intro: Jemanden einladen, zuzugreifen
their_relation: Ihre Beziehung zu dieser Person
can_add_data: Kann Daten hinzufügen
send_invitation: Einladung senden
back_to_dossier: Zurück zum Dossier
# Relations
my_role: "meine Rolle"
role: "role"
# Invitation email
invite_email_subject: "%s hat Sie zu inou hinzugefügt"
invite_email_body: "%s hat Ihr Gesundheitsdossier zu inou hinzugefügt, damit Sie Ihre medizinischen Daten einsehen und verwalten können."
invite_email_cta: "Anmelden zum Ansehen"
continue: "Weiter"
invite_email_subject: "%s hat dich zu inou hinzugefügt"
invite_email_body: "%s hat dein Gesundheitsdossier zu inou hinzugefügt, damit du deine medizinischen Daten ansehen und verwalten kannst."
invite_email_cta: Anmelden zum Ansehen
continue: Weiter
i_am_their: Ich bin ihr/e...
# Access management
people_with_access_count: "Personen mit Zugriff"
view_audit_log: "Aktivitätsprotokoll ansehen"
export_data: "Download my data"
relation_with: "Beziehung zu"
audit_log: "Aktivitätsprotokoll"
audit_log_intro: "Aktivitätsverlauf für"
audit_log_desc: "Verfolgen Sie, wer auf dieses Dossier zugegriffen oder es geändert hat"
# Simple relation names (for display)
my_role: meine Rolle
role: Rolle
section_privacy: Datenschutz
people_with_access_count: Personen mit Zugriff
view_audit_log: Audit-Protokoll ansehen
export_data: Meine Daten herunterladen
relation_with: Beziehung mit
audit_log: Audit-Protokoll
audit_log_intro: Aktivitätsverlauf für
audit_log_desc: Verfolge, wer auf dieses Dossier zugegriffen oder es geändert hat
# Install / Connect
install_title: "Mit Claude verbinden"
install_intro: "Richten Sie die inou-Bridge ein, damit Claude Ihre Gesundheitsdaten analysieren kann"
# Permissions (RBAC)
permissions_title: Berechtigungen
permissions_subtitle: Kontrolliere, wer auf dieses Dossier zugreifen kann und was sie tun können
current_access: Aktueller Zugriff
grant_access: Zugriff gewähren
no_grantees: Niemand anderes hat Zugriff auf dieses Dossier.
person_email: E-Mail-Adresse
person_email_hint: Wenn sie kein Konto haben, werden sie eingeladen, eines zu erstellen.
person_name: Name
select_role: Rolle auswählen...
custom_role: Benutzerdefinierte Berechtigungen
permissions: Berechtigungen
op_read: Lesen
op_write: Schreiben
op_delete: Löschen
op_manage: Verwalten
grant: Zugriff gewähren
revoke: Widerrufen
role_descriptions: Rollenbeschreibungen
ops_legend: Berechtigungslegende
op_read_desc: Daten ansehen
op_write_desc: Daten hinzufügen/bearbeiten
op_delete_desc: Daten entfernen
op_manage_desc: Verwalte, wer Zugriff hat
permissions_updated: Berechtigungen erfolgreich aktualisiert.
back: Zurück
can_add_data: Kann Daten hinzufügen
install_title: Mit Claude verbinden
install_intro: Richte die inou Bridge ein, damit Claude deine Gesundheitsdaten analysieren kann
install_step1: "Schritt 1: Herunterladen"
install_step1_desc: "Laden Sie die Bridge für Ihre Plattform herunter"
install_download_intro: "Laden Sie die inou-Bridge für Ihr Betriebssystem herunter:"
install_step1_desc: Hole dir die Bridge für deine Plattform
install_download_intro: "Lade die inou Bridge für dein Betriebssystem herunter:"
install_step2: "Schritt 2: Konfigurieren"
install_step2_desc: "Zur Claude Desktop-Konfiguration hinzufügen"
install_config_intro: "Fügen Sie dies zu Ihrer Claude Desktop-Konfigurationsdatei hinzu:"
install_step2_desc: Zur Claude Desktop Konfiguration hinzufügen
install_config_intro: "Füge dies zu deiner Claude Desktop Konfigurationsdatei hinzu:"
install_step3: "Schritt 3: Testen"
install_step3_desc: "Verbindung überprüfen"
install_test_intro: "Starten Sie Claude Desktop neu und fragen Sie: 'Zeige mir meine inou-Profile'"
nav_install: "Mit Claude verbinden"
nav_home: "Startseite"
install_step3_desc: Verifiziere die Verbindung
install_test_intro: "Starte Claude Desktop neu und frage: 'Zeig mir meine inou Profile'"
nav_install: Mit Claude verbinden
nav_home: Startseite
pending: ausstehend
rate_limit_exceeded: Zu viele Anmeldeversuche von deinem Standort. Bitte versuche es morgen erneut.
section_genetics: Genetik
no_genetics: Keine genetischen Daten
# Status
pending: "ausstehend"
rate_limit_exceeded: "Zu viele Anmeldeversuche von Ihrem Standort. Bitte versuchen Sie es morgen erneut."
# Sex display
sex_0: "unbekannt"
sex_1: "männlich"
sex_2: "weiblich"
sex_9: "andere"
sex_0: unbekannt
sex_1: männlich
sex_2: weiblich
sex_9: andere
# Friend invite email
friend_invite_subject: "Schau dir das an — %s"
friend_invite_p1: "Ich nutze <strong>inou</strong>, die sichere Art, Gesundheitsdaten zu speichern und mit KI zu erkunden. Es hält alle Gesundheitsinformationen meiner Familie an einem Ort — Bildgebung, Laborergebnisse, Krankenakten — und ich dachte, es könnte auch für dich nützlich sein."
friend_invite_p2: "Die wahre Stärke liegt darin, KI nutzen zu können, um alles zu verstehen: zu verstehen, was ein Bericht wirklich bedeutet, Trends über die Zeit zu erkennen, oder einfach Fragen in normaler Sprache zu stellen und klare Antworten zu bekommen."
friend_invite_btn: "Entdecke inou"
friend_invite_p1: "Ich nutze <strong>inou</strong>, die sichere Methode, um Gesundheitsdaten zu speichern und mit KI zu analysieren. Es speichert alle Gesundheitsinformationen meiner Familie an einem Ort Bildgebungsstudien, Laborergebnisse, medizinische Aufzeichnungen und ich dachte, du könntest es auch nützlich finden."
friend_invite_p2: "Die wahre Kraft liegt darin, KI nutzen zu können, um alles zu verstehen: verstehe, was ein Bericht wirklich bedeutet, erkenne Trends über Zeit, oder stell einfach Fragen in einfacher Sprache und erhalte klare Antworten."
friend_invite_btn: Schau dir inou an
friend_invite_dear: "Liebe/r %s,"
rel_0: "du"
rel_1: "Elternteil"
rel_2: "Kind"
rel_3: "Ehepartner"
rel_4: "Geschwister"
rel_5: "Vormund"
rel_6: "Betreuer"
rel_7: "Coach"
rel_8: "Arzt"
rel_9: "Freund"
rel_10: "Andere"
rel_98: "Andere"
rel_99: "Demo"
select_relation: "Beziehung auswählen..."
rel_0: du
rel_1: Elternteil
rel_2: Kind
rel_3: Ehepartner/in
rel_4: Geschwister
rel_5: Erziehungsberechtigte/r
rel_6: Pflegeperson
rel_7: Coach
rel_8: Arzt/Ärztin
rel_9: Freund/in
rel_10: Andere/r
rel_98: Andere/r
rel_99: Demo
select_relation: Beziehung auswählen...
audit_dossier_added: "Neues Dossier für %s erstellt von %s"
audit_dossier_edited: "Dossier %s bearbeitet von %s"
audit_access_granted: "Zugriff auf %s gewährt für %s"
audit_dossier_created: "Konto erstellt von %s"
audit_access_revoked: "Zugriff für %s auf %s widerrufen"
audit_file_upload: "Datei %s hochgeladen von %s"
audit_file_delete: "Datei %s gelöscht von %s"
audit_file_category_change: "Datei %s Kategorie geändert von %s"
audit_genome_import: "%s genetische Varianten importiert"
# Kategorien
# Categories (category000 = imaging, etc.)
category000: Bildgebung
category001: Dokument
category002: Laborergebnis
@ -198,12 +273,12 @@ category003: Genom
category004: Upload
category005: Konsultation
category006: Diagnose
category007: Bildgebungsergebnis
category008: EEG-Ergebnis
category009: Vitalwert
category010: Bewegung
category007: Bildgebungsbefund
category008: EEG-Befund
category009: Vitalzeichen
category010: Sport
category011: Medikament
category012: Nahrungsergänzung
category012: Nahrungsergänzungsmittel
category013: Ernährung
category014: Fruchtbarkeit
category015: Symptom
@ -212,30 +287,54 @@ category017: Krankengeschichte
category018: Familienanamnese
category019: Operation
category020: Krankenhausaufenthalt
category021: Geburtsdaten
category021: Geburtsurkunde
category022: Medizinisches Gerät
category023: Therapie
category024: Bewertung
category025: Gesundheitsdienstleister
category025: Anbieter
category026: Frage
# Genome
genome_english_only: "Alle genetischen Informationen sind auf Englisch. Verwenden Sie Claude, um sie auf Deutsch zu besprechen."
genome_variants: "Varianten"
genome_hidden: "verborgen"
genome_english_only: ""
genome_variants: Varianten
genome_hidden: versteckt
genome_show_all_categories: "Alle %d Kategorien anzeigen"
# API
api_token: "API Token"
api_token_use: "[EN] Use this token to authenticate API requests:"
api_token_warning: "[EN] Keep this private. Anyone with this token can access your health data."
api_token_none: "[EN] Generate a token to access the API programmatically or connect AI assistants."
api_token_generate: "Generate Token"
api_token_regenerate: "Regenerate Token"
api_token_regenerate_confirm: "[EN] This will invalidate your current token. Any connected apps will need to be updated."
api_authentication: "Authentication"
api_auth_instructions: "[EN] Include your API token in the Authorization header:"
copy: "Copy"
relation: "Beziehung"
relation_to: "Zu"
me: "Ich"
api_token: API-Token
api_token_use: "Verwende diesen Token für die Authentifizierung von API-Anfragen:"
api_token_warning: Bewahre dies privat auf. Jeder mit diesem Token kann auf deine Gesundheitsdaten zugreifen.
api_token_none: Generiere einen Token für den programmatischen API-Zugriff oder um KI-Assistenten zu verbinden.
api_token_generate: Token generieren
api_token_regenerate: Token neu generieren
api_token_regenerate_confirm: Dies macht deinen aktuellen Token ungültig. Alle verbundenen Apps müssen aktualisiert werden.
api_authentication: Authentifizierung
api_auth_instructions: "Füge deinen API-Token in den Authorization-Header ein:"
copy: Kopieren
relation: Beziehung
relation_to: Zu
me: Ich
# Languages (native names — do not translate)
lang_da: Dansk
lang_de: Deutsch
lang_en: English
lang_es: Español
lang_fi: Suomi
lang_fr: Français
lang_hi: हिन्दी
lang_id: Bahasa Indonesia
lang_it: Italiano
lang_ja: 日本語
lang_ko: 한국어
lang_nl: Nederlands
lang_no: Norsk
lang_pl: Polski
lang_pt: Português
lang_ru: Русский
lang_sv: Svenska
lang_th: ไทย
lang_tr: Türkçe
lang_uk: Українська
lang_vi: Tiếng Việt
lang_zh: 中文

View File

@ -1,3 +1,5 @@
language_name: English
# Landing
headline_1: "Your health data."
headline_2: "Your AI."
@ -312,3 +314,27 @@ copy: "Copy"
relation: "Relation"
relation_to: "To"
me: "Me"
# Languages (native names — do not translate)
lang_da: Dansk
lang_de: Deutsch
lang_en: English
lang_es: Español
lang_fi: Suomi
lang_fr: Français
lang_hi: हिन्दी
lang_id: Bahasa Indonesia
lang_it: Italiano
lang_ja: 日本語
lang_ko: 한국어
lang_nl: Nederlands
lang_no: Norsk
lang_pl: Polski
lang_pt: Português
lang_ru: Русский
lang_sv: Svenska
lang_th: ไทย
lang_tr: Türkçe
lang_uk: Українська
lang_vi: Tiếng Việt
lang_zh: 中文

View File

@ -1,205 +1,280 @@
language_name: Español
# Landing
headline_1: "Tus datos de salud."
headline_2: "Tu IA."
headline_3: "Tus respuestas."
intro: "Sube imágenes médicas, análisis y más. Conecta tu IA para ayudarte a entender lo que estás viendo."
email: "Correo electrónico"
get_started: "Comenzar"
data_yours: "Tus datos son tuyos"
never_training: "Nunca usados para entrenamiento"
never_training_desc: "Tus imágenes nunca se usan para entrenar modelos de IA."
never_shared: "Nunca compartidos"
never_shared_desc: "Nunca compartimos tus datos con nadie."
encrypted: "Almacenamiento cifrado"
encrypted_desc: "Todos los datos cifrados en reposo."
delete: "Eliminar en cualquier momento"
delete_desc: "Tus datos, tu control."
headline_1: Tus datos de salud.
headline_2: Tu IA.
headline_3: Tus respuestas.
intro: Sube imagenología, laboratorios y más. Conecta tu IA para ayudarte a entender lo que estás viendo.
email: Correo electrónico
get_started: Comenzar
data_yours: Tus datos siguen siendo tuyos
never_training: Nunca usado para entrenamiento
never_training_desc: Tus imágenes nunca se usan para entrenar modelos de IA.
never_shared: Nunca compartido
never_shared_desc: Nunca compartimos tus datos con nadie.
encrypted: Cifrado de grado militar
encrypted_desc: En reposo y en tránsito. Tus datos nunca viajan sin protección.
delete: Eliminar en cualquier momento
delete_desc: Tus datos, tu control.
# Verify
check_email: "Revisa tu correo"
code_sent_to: "Enviamos un código de 6 dígitos a"
verification_code: "Código de verificación"
verify: "Verificar"
use_different_email: "Usar otro correo"
invalid_code: "Código inválido o expirado. Por favor, inténtalo de nuevo."
check_email: Revisa tu correo electrónico
code_sent_to: Enviamos un código de 6 dígitos a
verification_code: Código de verificación
verify: Verificar
use_different_email: Usar un correo electrónico diferente
invalid_code: Código inválido o expirado. Por favor intenta de nuevo.
# Onboard
create_dossier: "Crea tu expediente"
create_profile_intro: "Cuéntanos sobre ti para comenzar."
name: "Nombre"
name_placeholder: "Tu nombre"
date_of_birth: "Fecha de nacimiento"
sex_at_birth: "Sexo al nacer"
female: "Femenino"
male: "Masculino"
create_my_dossier: "Crear mi expediente"
create_dossier: Crea tu expediente
create_profile_intro: Cuéntanos sobre ti para comenzar.
name: Nombre
name_placeholder: Tu nombre
date_of_birth: Fecha de nacimiento
sex_at_birth: Sexo al nacer
female: Femenino
male: Masculino
create_my_dossier: Crear mi expediente
# Minor error
must_be_18: "Debes tener 18 años para crear una cuenta"
minor_explanation: "Si estás configurando esto para otra persona, comienza primero con tu propio perfil. Esto asegura que solo tú puedas acceder a sus datos de salud."
minor_next_steps: "Después de crear tu expediente, puedes agregar otros."
use_different_dob: "Usar otra fecha de nacimiento"
must_be_18: Debes tener 18 años para crear una cuenta
minor_explanation: Si estás configurando esto para alguien más, comienza con tu propio perfil primero. Esto asegura que solo tú puedas acceder a sus datos de salud.
minor_next_steps: Después de crear tu expediente, puedes agregar a otros.
use_different_dob: Usar una fecha de nacimiento diferente
# Minor login block
minor_login_blocked: "Debes tener 18 años para iniciar sesión"
minor_login_blocked: "You must be 18 to log in"
minor_ask_guardian: "Pide a %s que acceda a tu expediente."
minor_ask_guardian_generic: "Pide a un padre o tutor que acceda a tu expediente."
minor_ask_guardian_generic: Pide a un padre o tutor que acceda a tu expediente.
# Dashboard
dossiers: "Expedientes"
dossiers_intro: "Gestiona datos de salud para ti o para otros"
you: "tú"
view: "Ver"
save: "Guardar"
cancel: "Cancelar"
add_dossier: "Agregar expediente"
edit_dossier: "Editar expediente"
care: "cuidado"
logout: "Cerrar sesión"
dossiers: Expedientes
dossiers_intro: Gestiona datos de salud para ti o para otros
you:
view: Ver
save: Guardar
cancel: Cancelar
add_dossier: Agregar expediente
edit_dossier: Editar expediente
care: cuidado
logout: Cerrar sesión
# Profile detail
back_to_dossiers: "Volver a expedientes"
born: "Nacido/a"
no_access_yet: "Solo tú tienes acceso."
people_with_access: "Personas con acceso"
share_access: "Compartir acceso"
can_edit: "puede agregar datos"
remove: "Eliminar"
back_to_dossiers: Volver a expedientes
born: Nacido/a
no_access_yet: Solo tú tienes acceso.
people_with_access: Personas con acceso
share_access: Compartir acceso
manage_permissions: Gestionar permisos
can_edit: puede agregar datos
remove: Eliminar
confirm_revoke: "¿Eliminar acceso?"
# Dossier sections
section_imaging: "Imágenes"
section_labs: "Laboratorio"
section_uploads: "Archivos"
section_vitals: "Signos vitales"
section_medications: "Medicamentos"
section_records: "Registros"
section_journal: "Diario"
section_genetics: "Genética"
section_privacy: "Privacidad"
section_imaging: Imagenología
section_labs: Laboratorios
section_uploads: Subidas
section_vitals: Signos vitales
section_medications: Medicamentos
section_records: Registros
section_journal: Diario
section_checkin: Registro diario
section_procedures: Procedimientos
section_assessments: Evaluaciones
section_genetics: Genética
section_supplements: Suplementos
section_symptoms: Síntomas
section_hospitalizations: Hospitalizaciones
section_therapies: Terapias
section_consultations: Consultas
section_diagnoses: Diagnósticos
section_exercise: Ejercicio
section_nutrition: Nutrición
section_fertility: Fertilidad
section_notes: Notas
section_history: Historial médico
section_family_history: Historial familiar
section_birth: Nacimiento
section_devices: Dispositivos
section_providers: Proveedores
section_questions: Preguntas
section_privacy: Privacidad
# Daily Check-in
checkin_summary: Registra signos vitales, medicamentos, síntomas
checkin_build_profile: Agrega lo que quieres seguir
btn_vitals: Signos vitales
btn_medications: Medicamentos
btn_supplements: Suplementos
btn_exercise: Ejercicio
# Plural forms (use %d for count)
slice_one: "%d corte"
slice_other: "%d cortes"
series_one: "%d serie"
series_other: "%d series"
order_one: "%d orden"
order_other: "%d órdenes"
result_one: "%d resultado"
result_other: "%d resultados"
# Section summaries
imaging_summary: "%d estudios · %d cortes"
no_imaging: "Sin datos de imágenes"
no_lab_data: "Sin datos de laboratorio"
no_genetics: "Sin datos genéticos"
no_files: "Sin archivos"
no_upload_access: "You don't have permission to upload"
no_imaging: Sin datos de imagenología
no_lab_data: Sin datos de laboratorio
no_files: Sin archivos
no_upload_access: No tienes permiso para subir
files_summary: "%d archivos (%s)"
series_count: "%d series"
vitals_desc: "Presión arterial, frecuencia cardíaca, SpO₂, peso, glucosa"
medications_desc: "Recetas y suplementos"
records_desc: "Notas clínicas e historiales médicos"
journal_desc: "Síntomas, dolor y observaciones"
vitals_desc: Presión arterial, frecuencia cardíaca, SpO₂, peso, glucosa
medications_desc: Recetas y suplementos
records_desc: Notas clínicas y registros médicos
journal_desc: Síntomas, dolor y observaciones
# Buttons and actions
open_viewer: "Abrir visor"
manage: "Gestionar"
open: Abrir
open_viewer: Abrir visor
manage: Gestionar
show_all_studies: "Mostrar los %d estudios..."
coming_soon: "Próximamente"
coming_soon: Próximamente
# Upload page
upload_files: "Subir datos de salud"
upload_files_intro: "Sube imágenes médicas, resultados de laboratorio, archivos genómicos o cualquier documento relacionado con la salud."
upload_hint_broad: "DICOM, PDF, CSV, VCF y más"
uploading: "Subiendo..."
files_uploaded: "archivos subidos"
upload_scans: "Subir estudios"
upload_scans_intro: "Sube una carpeta con archivos DICOM de tu estudio de imágenes."
upload_drop: "Haz clic o arrastra una carpeta aquí"
upload_hint: "Solo carpetas DICOM"
upload_files: Subir datos de salud
upload_files_intro: Sube imagenología médica, resultados de laboratorio, archivos de genoma o cualquier documento relacionado con la salud.
upload_hint_broad: DICOM, PDF, CSV, VCF y más
uploading: Subiendo...
files_uploaded: archivos subidos
upload_scans: Subir escaneos
upload_scans_intro: Sube una carpeta que contenga archivos DICOM de tu estudio de imagenología.
upload_drop: Haz clic o arrastra una carpeta aquí
upload_hint: Solo carpetas DICOM
# Add profile
add_dossier_intro: "Agrega a alguien cuyos datos de salud quieras gestionar."
email_optional: "Correo (opcional)"
email_optional_hint: "Si tienen 18, pueden iniciar sesión ellos mismos"
your_relation: "Tu relación con esta persona"
select_relation: "Seleccionar..."
i_provide_care: "Proporciono cuidado a esta persona"
i_am_their: "Soy su..."
add_dossier_intro: Agrega a alguien cuyos datos de salud quieres gestionar.
email_optional: Correo electrónico (opcional)
email_optional_hint: Si tiene más de 18 años, puede iniciar sesión por sí mismo
your_relation: Tu relación con ellos
select_relation: Selecciona relación...
i_provide_care: Brindo cuidado a esta persona
# Share access
share_access_intro: "Invitar a alguien a acceder"
their_relation: "Su relación con esta persona"
can_add_data: "Puede agregar datos (suplementos, notas, etc.)"
send_invitation: "Enviar invitación"
back_to_dossier: "Volver al expediente"
share_access_intro: Invita a alguien a acceder
their_relation: Su relación con esta persona
can_add_data: Puede agregar datos
send_invitation: Enviar invitación
back_to_dossier: Volver al expediente
# Relations
my_role: "mi rol"
role: "role"
# Invitation email
invite_email_subject: "%s te agregó a inou"
invite_email_body: "%s agregó tu expediente de salud a inou para que puedas ver y gestionar tus datos médicos."
invite_email_cta: "Iniciar sesión para ver"
continue: "Continuar"
invite_email_cta: Iniciar sesión para ver
continue: Continuar
i_am_their: Soy su...
# Access management
people_with_access_count: "personas con acceso"
view_audit_log: "Ver registro de actividad"
export_data: "Download my data"
relation_with: "Relación con"
audit_log: "Registro de actividad"
audit_log_intro: "Historial de actividad para"
audit_log_desc: "Rastrea quién accedió o modificó este expediente"
# Simple relation names (for display)
my_role: mi rol
role: rol
section_privacy: Privacidad
people_with_access_count: personas con acceso
view_audit_log: Ver registro de auditoría
export_data: Descargar mis datos
relation_with: Relación con
audit_log: Registro de auditoría
audit_log_intro: Historial de actividad para
audit_log_desc: Rastrea quién accedió o modificó este expediente
# Install / Connect
install_title: "Conectar con Claude"
install_intro: "Configura el puente inou para que Claude analice tus datos de salud"
# Permissions (RBAC)
permissions_title: Permisos
permissions_subtitle: Controla quién puede acceder a este expediente y qué puede hacer
current_access: Acceso actual
grant_access: Conceder acceso
no_grantees: Nadie más tiene acceso a este expediente.
person_email: Dirección de correo electrónico
person_email_hint: Si no tiene una cuenta, será invitado a crear una.
person_name: Nombre
select_role: Selecciona un rol...
custom_role: Permisos personalizados
permissions: Permisos
op_read: Leer
op_write: Escribir
op_delete: Eliminar
op_manage: Gestionar
grant: Conceder acceso
revoke: Revocar
role_descriptions: Descripciones de roles
ops_legend: Leyenda de permisos
op_read_desc: Ver datos
op_write_desc: Agregar/editar datos
op_delete_desc: Eliminar datos
op_manage_desc: Gestionar quién tiene acceso
permissions_updated: Permisos actualizados exitosamente.
back: Volver
can_add_data: Puede agregar datos
install_title: Conectar a Claude
install_intro: Configura el puente de inou para que Claude analice tus datos de salud
install_step1: "Paso 1: Descargar"
install_step1_desc: "Obtén el puente para tu plataforma"
install_download_intro: "Descarga el puente inou para tu sistema operativo:"
install_step1_desc: Obtén el puente para tu plataforma
install_download_intro: "Descarga el puente de inou para tu sistema operativo:"
install_step2: "Paso 2: Configurar"
install_step2_desc: "Agregar a la configuración de Claude Desktop"
install_step2_desc: Agregar a la configuración de Claude Desktop
install_config_intro: "Agrega esto a tu archivo de configuración de Claude Desktop:"
install_step3: "Paso 3: Probar"
install_step3_desc: "Verificar la conexión"
install_step3_desc: Verifica la conexión
install_test_intro: "Reinicia Claude Desktop y pregunta: 'Muéstrame mis perfiles de inou'"
nav_install: "Conectar con Claude"
nav_home: "Inicio"
nav_install: Conectar a Claude
nav_home: Inicio
pending: pendiente
rate_limit_exceeded: Demasiados intentos de registro desde tu ubicación. Por favor intenta de nuevo mañana.
section_genetics: Genética
no_genetics: Sin datos genéticos
# Status
pending: "pendiente"
rate_limit_exceeded: "Demasiados intentos de registro desde tu ubicación. Por favor, inténtalo mañana."
# Sex display
sex_0: "desconocido"
sex_1: "masculino"
sex_2: "femenino"
sex_9: "otro"
sex_0: desconocido
sex_1: masculino
sex_2: femenino
sex_9: otro
# Friend invite email
friend_invite_subject: "Mira esto — %s"
friend_invite_p1: "Estoy usando <strong>inou</strong>, la forma segura de guardar datos de salud y explorarlos con IA. Mantiene toda la información de salud de mi familia en un solo lugar — estudios de imagen, resultados de laboratorio, historiales médicos — y pensé que también te podría ser útil."
friend_invite_p2: "El verdadero poder está en poder usar IA para entenderlo todo: comprender qué significa realmente un informe, detectar tendencias a lo largo del tiempo, o simplemente hacer preguntas en lenguaje sencillo y obtener respuestas claras."
friend_invite_btn: "Descubre inou"
friend_invite_dear: "Querido/a %s,"
rel_0: "tú"
rel_1: "Padre/Madre"
rel_2: "Hijo/a"
rel_3: "Cónyuge"
rel_4: "Hermano/a"
rel_5: "Tutor"
rel_6: "Cuidador"
rel_7: "Coach"
rel_8: "Médico"
rel_9: "Amigo"
rel_10: "Otro"
rel_98: "Otro"
rel_99: "Demo"
select_relation: "Seleccionar relación..."
friend_invite_subject: "Echa un vistazo a esto — %s"
friend_invite_p1: "He estado usando <strong>inou</strong>, la forma segura de almacenar datos de salud y explorarlos con IA. Mantiene toda la información de salud de mi familia en un solo lugar — estudios de imagen, resultados de laboratorio, registros médicos — y pensé que podría serte útil."
friend_invite_p2: "El verdadero poder está en poder usar IA para darle sentido a todo: entender lo que realmente significa un informe, detectar tendencias a lo largo del tiempo, o simplemente hacer preguntas en lenguaje cotidiano y obtener respuestas claras."
friend_invite_btn: Ver inou
friend_invite_dear: "Estimado/a %s,"
rel_0:
rel_1: Padre/Madre
rel_2: Hijo/a
rel_3: Cónyuge
rel_4: Hermano/a
rel_5: Tutor
rel_6: Cuidador
rel_7: Entrenador
rel_8: Doctor/a
rel_9: Amigo/a
rel_10: Otro
rel_98: Otro
rel_99: Demo
select_relation: Selecciona relación...
audit_dossier_added: "Nuevo expediente para %s creado por %s"
audit_dossier_edited: "Expediente %s editado por %s"
audit_access_granted: "Acceso a %s concedido a %s"
audit_dossier_created: "Cuenta creada por %s"
audit_access_revoked: "Acceso de %s a %s revocado"
audit_file_upload: "Archivo %s subido por %s"
audit_file_delete: "Archivo %s eliminado por %s"
audit_file_category_change: "Categoría del archivo %s cambiada por %s"
audit_genome_import: "%s variantes genéticas importadas"
# Categorías
category000: Imagen médica
# Categories (category000 = imaging, etc.)
category000: Imagenología
category001: Documento
category002: Resultado de laboratorio
category003: Genoma
category004: Carga
category004: Subida
category005: Consulta
category006: Diagnóstico
category007: Resultado de imagen
category008: Resultado de EEG
category007: Hallazgo de imagenología
category008: Hallazgo de EEG
category009: Signo vital
category010: Ejercicio
category011: Medicamento
@ -209,33 +284,57 @@ category014: Fertilidad
category015: Síntoma
category016: Nota
category017: Historial médico
category018: Antecedentes familiares
category018: Historial familiar
category019: Cirugía
category020: Hospitalización
category021: Datos de nacimiento
category021: Registro de nacimiento
category022: Dispositivo médico
category023: Terapia
category024: Evaluación
category025: Proveedor de salud
category025: Proveedor
category026: Pregunta
# Genome
genome_english_only: "Toda la información genética está en inglés. Usa Claude para discutirla en español."
genome_variants: "variantes"
genome_hidden: "ocultas"
genome_english_only: ""
genome_variants: variantes
genome_hidden: oculto
genome_show_all_categories: "Mostrar las %d categorías"
# API
api_token: "API Token"
api_token_use: "[EN] Use this token to authenticate API requests:"
api_token_warning: "[EN] Keep this private. Anyone with this token can access your health data."
api_token_none: "[EN] Generate a token to access the API programmatically or connect AI assistants."
api_token_generate: "Generate Token"
api_token_regenerate: "Regenerate Token"
api_token_regenerate_confirm: "[EN] This will invalidate your current token. Any connected apps will need to be updated."
api_authentication: "Authentication"
api_auth_instructions: "[EN] Include your API token in the Authorization header:"
copy: "Copy"
relation: "Relación"
relation_to: "A"
me: "Yo"
api_token: Token de API
api_token_use: "Usa este token para autenticar solicitudes de API:"
api_token_warning: Mantén esto en privado. Cualquier persona con este token puede acceder a tus datos de salud.
api_token_none: Genera un token para acceder a la API programáticamente o conectar asistentes de IA.
api_token_generate: Generar token
api_token_regenerate: Regenerar token
api_token_regenerate_confirm: Esto invalidará tu token actual. Cualquier aplicación conectada deberá actualizarse.
api_authentication: Autenticación
api_auth_instructions: "Incluye tu token de API en el encabezado de Authorization:"
copy: Copiar
relation: Relación
relation_to: Con
me: Yo
# Languages (native names — do not translate)
lang_da: Dansk
lang_de: Deutsch
lang_en: English
lang_es: Español
lang_fi: Suomi
lang_fr: Français
lang_hi: हिन्दी
lang_id: Bahasa Indonesia
lang_it: Italiano
lang_ja: 日本語
lang_ko: 한국어
lang_nl: Nederlands
lang_no: Norsk
lang_pl: Polski
lang_pt: Português
lang_ru: Русский
lang_sv: Svenska
lang_th: ไทย
lang_tr: Türkçe
lang_uk: Українська
lang_vi: Tiếng Việt
lang_zh: 中文

View File

@ -1,3 +1,5 @@
language_name: Suomi
# Landing
headline_1: "Sinun terveystietosi."
headline_2: "Sinun tekoälysi."

View File

@ -1,3 +1,5 @@
language_name: Français
# Landing
headline_1: "Vos données de santé."
headline_2: "Votre IA."

View File

@ -1,3 +1,5 @@
language_name: Italiano
# Landing
headline_1: "I tuoi dati sanitari."
headline_2: "La tua IA."

View File

@ -1,3 +1,5 @@
language_name: 日本語
# Landing
headline_1: "あなたの健康データ。"
headline_2: "あなたのAI。"

View File

@ -1,3 +1,5 @@
language_name: 한국어
# Landing
headline_1: "당신의 건강 데이터."
headline_2: "당신의 AI."

View File

@ -1,27 +1,29 @@
language_name: Nederlands
# Landing
headline_1: "Jouw gezondheidsdata."
headline_2: "Jouw AI."
headline_3: "Jouw antwoorden."
intro: "Upload beeldvorming, labresultaten en meer. Verbind je AI om te begrijpen wat je ziet."
intro: "Upload beeldvorming, labresultaten en meer. Verbind je AI om te begrijpen waar je naar kijkt."
email: "E-mail"
get_started: "Aan de slag"
data_yours: "Jouw data blijft van jou"
data_yours: "Je data blijft van jou"
never_training: "Nooit gebruikt voor training"
never_training_desc: "Je beelden worden nooit gebruikt om AI-modellen te trainen."
never_shared: "Nooit gedeeld"
never_shared_desc: "We delen je data nooit met anderen."
encrypted: "Versleutelde opslag"
encrypted_desc: "Alle data versleuteld opgeslagen."
delete: "Altijd verwijderen"
never_shared_desc: "We delen je data nooit met wie dan ook."
encrypted: "Militaire encryptie"
encrypted_desc: "In rust en onderweg. Je data reist nooit onbeschermd."
delete: "Verwijder wanneer je wilt"
delete_desc: "Jouw data, jouw controle."
# Verify
check_email: "Controleer je e-mail"
check_email: "Check je e-mail"
code_sent_to: "We hebben een 6-cijferige code gestuurd naar"
verification_code: "Verificatiecode"
verify: "Verifiëren"
use_different_email: "Ander e-mailadres gebruiken"
invalid_code: "Ongeldige of verlopen code. Probeer opnieuw."
use_different_email: "Gebruik een ander e-mailadres"
invalid_code: "Ongeldige of verlopen code. Probeer het opnieuw."
# Onboard
create_dossier: "Maak je dossier aan"
@ -32,13 +34,13 @@ date_of_birth: "Geboortedatum"
sex_at_birth: "Geslacht bij geboorte"
female: "Vrouw"
male: "Man"
create_my_dossier: "Mijn dossier aanmaken"
create_my_dossier: "Maak mijn dossier aan"
# Minor error
must_be_18: "Je moet 18 zijn om een account aan te maken"
minor_explanation: "Als je dit voor iemand anders instelt, begin dan eerst met je eigen profiel. Zo heb alleen jij toegang tot hun gezondheidsgegevens."
minor_explanation: "Als je dit voor iemand anders instelt, begin dan met je eigen profiel. Zo heb alleen jij toegang tot hun gezondheidsdata."
minor_next_steps: "Na het aanmaken van je dossier kun je anderen toevoegen."
use_different_dob: "Andere geboortedatum gebruiken"
use_different_dob: "Gebruik een andere geboortedatum"
# Minor login block
minor_login_blocked: "Je moet 18 zijn om in te loggen"
@ -47,7 +49,7 @@ minor_ask_guardian_generic: "Vraag een ouder of voogd om toegang tot je dossier.
# Dashboard
dossiers: "Dossiers"
dossiers_intro: "Beheer de gezondheidsgegevens van jezelf of voor anderen"
dossiers_intro: "Beheer gezondheidsdata voor jezelf of anderen"
you: "jij"
view: "Bekijken"
save: "Opslaan"
@ -61,25 +63,54 @@ logout: "Uitloggen"
back_to_dossiers: "Terug naar dossiers"
born: "Geboren"
no_access_yet: "Alleen jij hebt toegang."
people_with_access: "Personen met toegang"
people_with_access: "Mensen met toegang"
share_access: "Toegang delen"
can_edit: "kan gegevens toevoegen"
manage_permissions: "Rechten beheren"
can_edit: "kan data toevoegen"
remove: "Verwijderen"
confirm_revoke: "Toegang intrekken?"
# Dossier sections
section_imaging: "Radiologie"
section_imaging: "Beeldvorming"
section_labs: "Labresultaten"
section_uploads: "Uploads"
section_vitals: "Vitale functies"
section_vitals: "Vitale waarden"
section_medications: "Medicatie"
section_records: "Dossiers"
section_journal: "Dagboek"
section_checkin: "Dagelijkse check-in"
section_procedures: "Ingrepen"
section_assessments: "Beoordelingen"
section_genetics: "Genetica"
section_supplements: "Supplementen"
section_symptoms: "Symptomen"
section_hospitalizations: "Ziekenhuisopnames"
section_therapies: "Therapieën"
section_consultations: "Consulten"
section_diagnoses: "Diagnoses"
section_exercise: "Beweging"
section_nutrition: "Voeding"
section_fertility: "Vruchtbaarheid"
section_notes: "Notities"
section_history: "Medische geschiedenis"
section_family_history: "Familiegeschiedenis"
section_birth: "Geboorte"
section_devices: "Apparaten"
section_providers: "Zorgverleners"
section_questions: "Vragen"
section_privacy: "Privacy"
# Section summaries
# Plural forms
slice_one: "%d beeld"
slice_other: "%d beelden"
# Daily Check-in
checkin_summary: "Houd vitale waarden, medicatie en symptomen bij"
checkin_build_profile: "Voeg toe wat je wilt bijhouden"
btn_vitals: "Vitale waarden"
btn_medications: "Medicatie"
btn_supplements: "Supplementen"
btn_exercise: "Beweging"
# Plural forms (use %d for count)
slice_one: "%d slice"
slice_other: "%d slices"
series_one: "%d serie"
series_other: "%d series"
order_one: "%d aanvraag"
@ -87,11 +118,12 @@ order_other: "%d aanvragen"
result_one: "%d resultaat"
result_other: "%d resultaten"
imaging_summary: "%d onderzoeken · %d beelden"
# Section summaries
imaging_summary: "%d onderzoeken · %d slices"
no_imaging: "Geen beeldvorming"
no_lab_data: "Geen labresultaten"
no_files: "Geen bestanden"
no_upload_access: "You don't have permission to upload"
no_upload_access: "Je hebt geen rechten om te uploaden"
files_summary: "%d bestanden (%s)"
series_count: "%d series"
vitals_desc: "Bloeddruk, hartslag, SpO₂, gewicht, glucose"
@ -100,13 +132,14 @@ records_desc: "Klinische notities en medische dossiers"
journal_desc: "Symptomen, pijn en observaties"
# Buttons and actions
open: "Openen"
open_viewer: "Viewer openen"
manage: "Beheren"
show_all_studies: "Toon alle %d onderzoeken..."
coming_soon: "Binnenkort beschikbaar"
# Upload page
upload_files: "Gezondheidsgegevens uploaden"
upload_files: "Gezondheidsdata uploaden"
upload_files_intro: "Upload medische beeldvorming, labresultaten, genoombestanden of andere gezondheidsgerelateerde documenten."
upload_hint_broad: "DICOM, PDF, CSV, VCF en meer"
uploading: "Uploaden..."
@ -117,17 +150,17 @@ upload_drop: "Klik of sleep een map hierheen"
upload_hint: "Alleen DICOM-mappen"
# Add profile
add_dossier_intro: "Voeg iemand toe wiens gezondheidsgegevens je wilt beheren."
add_dossier_intro: "Voeg iemand toe van wie je de gezondheidsdata wilt beheren."
email_optional: "E-mail (optioneel)"
email_optional_hint: "Als ze 18+ zijn, kunnen ze zelf inloggen"
your_relation: "Jouw relatie met hen"
select_relation: "Selecteer..."
your_relation: "Jouw relatie tot deze persoon"
select_relation: "Selecteer relatie..."
i_provide_care: "Ik zorg voor deze persoon"
# Share access
share_access_intro: "Nodig iemand uit voor toegang tot"
their_relation: "Hun relatie met deze persoon"
can_add_data: "Kan gegevens toevoegen (supplementen, notities, etc.)"
their_relation: "Hun relatie tot deze persoon"
can_add_data: "Kan data toevoegen"
send_invitation: "Uitnodiging versturen"
back_to_dossier: "Terug naar dossier"
@ -136,57 +169,85 @@ back_to_dossier: "Terug naar dossier"
# Invitation email
invite_email_subject: "%s heeft je toegevoegd aan inou"
invite_email_body: "%s heeft je gezondheidsdossier toegevoegd aan inou zodat je je medische gegevens kunt bekijken en beheren."
invite_email_cta: "Inloggen om te bekijken"
invite_email_cta: "Log in om te bekijken"
continue: "Doorgaan"
i_am_their: "Ik ben hun..."
# Simple relation names (for display)
my_role: "mijn rol"
role: "role"
role: "rol"
section_privacy: "Privacy"
people_with_access_count: "personen met toegang"
view_audit_log: "Bekijk auditlog"
export_data: "Download my data"
people_with_access_count: "mensen met toegang"
view_audit_log: "Activiteitenlog bekijken"
export_data: "Mijn data downloaden"
relation_with: "Relatie met"
audit_log: "Auditlog"
audit_log: "Activiteitenlog"
audit_log_intro: "Activiteitengeschiedenis voor"
audit_log_desc: "Bekijk wie dit dossier heeft bekeken of gewijzigd"
install_title: "Verbind met Claude"
install_intro: "Stel de inou-bridge in zodat Claude je gezondheidsgegevens kan analyseren"
install_step1: "Stap 1: Download"
audit_log_desc: "Bekijk wie dit dossier heeft geopend of gewijzigd"
# Permissions (RBAC)
permissions_title: "Rechten"
permissions_subtitle: "Bepaal wie toegang heeft tot dit dossier en wat ze kunnen doen"
current_access: "Huidige toegang"
grant_access: "Toegang verlenen"
no_grantees: "Niemand anders heeft toegang tot dit dossier."
person_email: "E-mailadres"
person_email_hint: "Als ze nog geen account hebben, worden ze uitgenodigd er een aan te maken."
person_name: "Naam"
select_role: "Selecteer een rol..."
custom_role: "Aangepaste rechten"
permissions: "Rechten"
op_read: "Lezen"
op_write: "Schrijven"
op_delete: "Verwijderen"
op_manage: "Beheren"
grant: "Toegang verlenen"
revoke: "Intrekken"
role_descriptions: "Rolbeschrijvingen"
ops_legend: "Rechtenlegenda"
op_read_desc: "Data bekijken"
op_write_desc: "Data toevoegen/bewerken"
op_delete_desc: "Data verwijderen"
op_manage_desc: "Beheren wie toegang heeft"
permissions_updated: "Rechten succesvol bijgewerkt."
back: "Terug"
can_add_data: "Kan data toevoegen"
install_title: "Verbinden met Claude"
install_intro: "Stel de inou bridge in zodat Claude je gezondheidsdata kan analyseren"
install_step1: "Stap 1: Downloaden"
install_step1_desc: "Download de bridge voor jouw platform"
install_download_intro: "Download de inou-bridge voor jouw besturingssysteem:"
install_step2: "Stap 2: Configureer"
install_step2_desc: "Voeg toe aan Claude Desktop configuratie"
install_config_intro: "Voeg dit toe aan je Claude Desktop configuratiebestand:"
install_step3: "Stap 3: Test"
install_download_intro: "Download de inou bridge voor jouw besturingssysteem:"
install_step2: "Stap 2: Configureren"
install_step2_desc: "Toevoegen aan Claude Desktop-configuratie"
install_config_intro: "Voeg dit toe aan je Claude Desktop-configuratiebestand:"
install_step3: "Stap 3: Testen"
install_step3_desc: "Controleer de verbinding"
install_test_intro: "Herstart Claude Desktop en vraag: 'Toon mijn inou profielen'"
nav_install: "Verbind met Claude"
nav_install: "Verbinden met Claude"
nav_home: "Home"
pending: "in afwachting"
rate_limit_exceeded: "Te veel aanmeldpogingen vanaf uw locatie. Probeer het morgen opnieuw."
section_genetics: Genetica
no_genetics: Geen genetische gegevens
rate_limit_exceeded: "Te veel aanmeldpogingen vanaf jouw locatie. Probeer het morgen opnieuw."
section_genetics: "Genetica"
no_genetics: "Geen genetische data"
sex_0: "onbekend"
sex_1: "mannelijk"
sex_2: "vrouwelijk"
sex_1: "man"
sex_2: "vrouw"
sex_9: "anders"
# Friend invite email
friend_invite_subject: "Kijk hier eens naar — %s"
friend_invite_p1: "Ik gebruik <strong>inou</strong>, de veilige manier om gezondheidsgegevens op te slaan en te verkennen met AI. Het houdt alle gezondheidsinformatie van mijn familie op één plek — beeldvorming, labresultaten, medische dossiers — en ik dacht dat jij het misschien ook handig zou vinden."
friend_invite_p2: "De echte kracht is dat je AI kunt gebruiken om alles te begrijpen: begrijpen wat een rapport echt betekent, trends in de tijd ontdekken, of gewoon vragen stellen in gewone taal en duidelijke antwoorden krijgen."
friend_invite_p1: "Ik gebruik <strong>inou</strong>, de veilige manier om gezondheidsdata op te slaan en te verkennen met AI. Het houdt alle gezondheidsinformatie van mijn gezin op één plek — beeldvorming, labresultaten, medische dossiers — en ik dacht dat jij er ook iets aan zou kunnen hebben."
friend_invite_p2: "De echte kracht zit in het gebruik van AI om er wijs uit te worden: begrijpen wat een rapport écht betekent, trends over tijd herkennen, of gewoon vragen stellen in normale taal en duidelijke antwoorden krijgen."
friend_invite_btn: "Bekijk inou"
friend_invite_dear: "Beste %s,"
rel_0: "jij"
rel_1: "Ouder"
rel_2: "Kind"
rel_3: "Partner"
rel_4: "Broer/Zus"
rel_4: "Broer/zus"
rel_5: "Voogd"
rel_6: "Verzorger"
rel_6: "Mantelzorger"
rel_7: "Coach"
rel_8: "Arts"
rel_9: "Vriend"
@ -194,62 +255,62 @@ rel_10: "Anders"
rel_98: "Anders"
rel_99: "Demo"
select_relation: "Selecteer relatie..."
audit_dossier_added: "Nieuw dossier voor %s aangemaakt door %s"
audit_dossier_added: "Een nieuw dossier voor %s aangemaakt door %s"
audit_dossier_edited: "Dossier %s bewerkt door %s"
audit_access_granted: "Toegang tot %s verleend aan %s"
audit_dossier_created: Account aangemaakt door %s
audit_access_revoked: Toegang voor %s tot %s ingetrokken
audit_file_upload: Bestand %s geüpload door %s
audit_file_delete: Bestand %s verwijderd door %s
audit_file_category_change: Bestandscategorie %s gewijzigd door %s
audit_genome_import: %s genetische varianten geïmporteerd
audit_dossier_created: "Account aangemaakt door %s"
audit_access_revoked: "Toegang van %s tot %s ingetrokken"
audit_file_upload: "Bestand %s geüpload door %s"
audit_file_delete: "Bestand %s verwijderd door %s"
audit_file_category_change: "Categorie van bestand %s gewijzigd door %s"
audit_genome_import: "%s genetische varianten geïmporteerd"
# Categorieën
category000: Beeldvorming
category001: Document
category002: Labuitslag
category003: Genoom
category004: Upload
category005: Consult
category006: Diagnose
category007: Beeldvormingsresultaat
category008: EEG-resultaat
category009: Vitale waarde
category010: Beweging
category011: Medicatie
category012: Supplement
category013: Voeding
category014: Vruchtbaarheid
category015: Symptoom
category016: Notitie
category017: Medische geschiedenis
category018: Familiegeschiedenis
category019: Operatie
category020: Ziekenhuisopname
category021: Geboortegegevens
category022: Medisch hulpmiddel
category023: Therapie
category024: Beoordeling
category025: Zorgverlener
category026: Vraag
# Categories (category000 = imaging, etc.)
category000: "Beeldvorming"
category001: "Document"
category002: "Labresultaat"
category003: "Genoom"
category004: "Upload"
category005: "Consult"
category006: "Diagnose"
category007: "Beeldvormingsbevinding"
category008: "EEG-bevinding"
category009: "Vitale waarde"
category010: "Beweging"
category011: "Medicatie"
category012: "Supplement"
category013: "Voeding"
category014: "Vruchtbaarheid"
category015: "Symptoom"
category016: "Notitie"
category017: "Medische geschiedenis"
category018: "Familiegeschiedenis"
category019: "Operatie"
category020: "Ziekenhuisopname"
category021: "Geboorteakte"
category022: "Medisch hulpmiddel"
category023: "Therapie"
category024: "Beoordeling"
category025: "Zorgverlener"
category026: "Vraag"
# Genome
genome_english_only: "Alle genetische informatie is in het Engels. Gebruik Claude om het in het Nederlands te bespreken."
genome_english_only: ""
genome_variants: "varianten"
genome_hidden: "verborgen"
genome_show_all_categories: "Toon alle %d categorieën"
# API
api_token: "API Token"
api_token_use: "[EN] Use this token to authenticate API requests:"
api_token_warning: "[EN] Keep this private. Anyone with this token can access your health data."
api_token_none: "[EN] Generate a token to access the API programmatically or connect AI assistants."
api_token_generate: "Generate Token"
api_token_regenerate: "Regenerate Token"
api_token_regenerate_confirm: "[EN] This will invalidate your current token. Any connected apps will need to be updated."
api_authentication: "Authentication"
api_auth_instructions: "[EN] Include your API token in the Authorization header:"
copy: "Copy"
api_token: "API-token"
api_token_use: "Gebruik dit token om API-verzoeken te authenticeren:"
api_token_warning: "Houd dit privé. Iedereen met dit token heeft toegang tot je gezondheidsdata."
api_token_none: "Genereer een token om programmatisch toegang te krijgen tot de API of AI-assistenten te verbinden."
api_token_generate: "Token genereren"
api_token_regenerate: "Token opnieuw genereren"
api_token_regenerate_confirm: "Dit maakt je huidige token ongeldig. Verbonden apps moeten worden bijgewerkt."
api_authentication: "Authenticatie"
api_auth_instructions: "Voeg je API-token toe in de Authorization-header:"
copy: "Kopiëren"
relation: "Relatie"
relation_to: "Aan"
me: "Ik"

View File

@ -1,3 +1,5 @@
language_name: Norsk
# Landing
headline_1: "Dine helsedata."
headline_2: "Din AI."

View File

@ -1,3 +1,5 @@
language_name: Português
# Landing
headline_1: "Seus dados de saúde."
headline_2: "Sua IA."

View File

@ -1,3 +1,5 @@
language_name: Русский
# Landing
headline_1: "Ваши медицинские данные."
headline_2: "Ваш ИИ."

View File

@ -1,3 +1,5 @@
language_name: Svenska
# Landing
headline_1: "Din hälsodata."
headline_2: "Din AI."

316
portal/lang/tr.yaml Normal file
View File

@ -0,0 +1,316 @@
language_name: Türkçe
# Landing
headline_1: Sağlık verilerin.
headline_2: "AI'ın."
headline_3: Cevapların.
intro: "Görüntüleme, laboratuvarlar ve daha fazlasını yükle. Ne baktığını anlamana yardımcı olması için AI'ını bağla."
email: E-posta
get_started: Başla
data_yours: Verilerin senin
never_training: Asla eğitim için kullanılmadı
never_training_desc: Görüntülerin asla AI modellerini eğitmek için kullanılmaz.
never_shared: Asla paylaşılmadı
never_shared_desc: Verilerini asla kimseyle paylaşmayız.
encrypted: Askeri düzey şifreleme
encrypted_desc: Hem durağan halde hem aktarımda. Verilerin asla korumasız seyahat etmez.
delete: İstediğin zaman sil
delete_desc: Verilerin, senin kontrolün.
# Verify
check_email: E-postanı kontrol et
code_sent_to: "6 haneli kodu gönderdik:"
verification_code: Doğrulama kodu
verify: Doğrula
use_different_email: Farklı bir e-posta kullan
invalid_code: Geçersiz veya süresi dolmuş kod. Lütfen tekrar dene.
# Onboard
create_dossier: Dosyanı oluştur
create_profile_intro: Başlamak için kendin hakkında bilgi ver.
name: İsim
name_placeholder: İsmin
date_of_birth: Doğum tarihi
sex_at_birth: Doğumdaki cinsiyet
female: Kadın
male: Erkek
create_my_dossier: Dosyamı oluştur
# Minor error
must_be_18: Hesap oluşturmak için 18 yaşında olmalısın
minor_explanation: Başkası için bunu kuruyorsan, önce kendi profilinle başla. Bu, yalnızca senin onların sağlık verilerine erişebildiğinden emin olur.
minor_next_steps: Dosyanı oluşturduktan sonra başkalarını ekleyebilirsin.
use_different_dob: Farklı bir doğum tarihi kullan
# Minor login block
minor_login_blocked: Giriş yapmak için 18 yaşında olmalısın
minor_ask_guardian: "Dosyana erişmek için %s'e sor."
minor_ask_guardian_generic: Dosyana erişmek için bir ebeveyne veya veliye sor.
# Dashboard
dossiers: Dosyalar
dossiers_intro: Kendin veya başkaları için sağlık verilerini yönet
you: sen
view: Görüntüle
save: Kaydet
cancel: İptal
add_dossier: Dosya ekle
edit_dossier: Dosyayı düzenle
care: bakım
logout: Çıkış yap
# Profile detail
back_to_dossiers: Dosyalara geri dön
born: Doğumlu
no_access_yet: Sadece sen erişebilirsin.
people_with_access: Erişimi olanlar
share_access: Erişimi paylaş
manage_permissions: İzinleri yönet
can_edit: veri ekleyebilir
remove: Kaldır
confirm_revoke: "Erişimi kaldır?"
# Dossier sections
section_imaging: Görüntüleme
section_labs: Laboratuvarlar
section_uploads: Yüklemeler
section_vitals: Vitals
section_medications: İlaçlar
section_records: Kayıtlar
section_journal: Günlük
section_checkin: Günlük Kontrol
section_procedures: Prosedürler
section_assessments: "Assessments"
section_genetics: Genetik
section_supplements: Takviyeler
section_symptoms: Belirtiler
section_hospitalizations: Hastaneye Yatışlar
section_therapies: Terapiler
section_consultations: Konsültasyonlar
section_diagnoses: Tanılar
section_exercise: Egzersiz
section_nutrition: Beslenme
section_fertility: Doğurganlık
section_notes: Notlar
section_history: Tıbbi Geçmiş
section_family_history: Aile Öyküsü
section_birth: Doğum
section_devices: Cihazlar
section_providers: Sağlık Sağlayıcıları
section_questions: Sorular
section_privacy: Gizlilik
# Daily Check-in
checkin_summary: Vitals, ilaçlar, belirtiler takip et
checkin_build_profile: Takip etmek istediğin şeyleri ekle
btn_vitals: Vitals
btn_medications: İlaçlar
btn_supplements: Takviyeler
btn_exercise: Egzersiz
# Plural forms (use %d for count)
slice_one: "%d dilim"
slice_other: "%d dilim"
series_one: "%d seri"
series_other: "%d seri"
order_one: "%d sipariş"
order_other: "%d sipariş"
result_one: "%d sonuç"
result_other: "%d sonuç"
# Section summaries
imaging_summary: "%d çalışma · %d dilim"
no_imaging: Görüntüleme verisi yok
no_lab_data: Laboratuvar verisi yok
no_files: Dosya yok
no_upload_access: Yükleme iznin yok
files_summary: "%d dosya (%s)"
series_count: "%d seriler"
vitals_desc: Tansiyon, kalp atışı, SpO₂, kilo, glukoz
medications_desc: Reçeteler ve takviyeler
records_desc: Klinik notlar ve tıbbi kayıtlar
journal_desc: Belirtiler, ağrı ve gözlemler
# Buttons and actions
open:
open_viewer: Görüntüleyiciyi aç
manage: Yönet
show_all_studies: "Tüm %d çalışmaları göster..."
coming_soon: Çok yakında
# Upload page
upload_files: Sağlık verileri yükle
upload_files_intro: Tıbbi görüntüleme, laboratuvar sonuçları, genom dosyaları veya sağlıkla ilgili herhangi bir belge yükle.
upload_hint_broad: DICOM, PDF, CSV, VCF ve daha fazlası
uploading: Yükleniyor...
files_uploaded: dosya yüklendi
upload_scans: Taramaları yükle
upload_scans_intro: Görüntüleme çalışmanızdan DICOM dosyaları içeren bir klasör yükleyin.
upload_drop: Bir klasörü buraya tıkla veya sürükle
upload_hint: Sadece DICOM klasörleri
# Add profile
add_dossier_intro: Sağlık verilerini yönetmek istediğiniz birini ekleyin.
email_optional: E-posta (isteğe bağlı)
email_optional_hint: 18 yaşındalarsa, kendileri giriş yapabilir
your_relation: Onlarla ilişkin
select_relation: İlişki seç...
i_provide_care: Bu kişiye bakım sağlıyorum
# Share access
share_access_intro: Erişim için birini davet et
their_relation: Bu kişiyle ilişkileri
can_add_data: Veri ekleyebilir
send_invitation: Davet gönder
back_to_dossier: Dosyaya geri dön
# Relations
# Invitation email
invite_email_subject: "%s seni inou'ya ekledi"
invite_email_body: "%s sağlık dosyanı inou'ya ekledi, böylece tıbbi verilerini görüntüleyebilir ve yönetebilirsin."
invite_email_cta: Görüntülemek için giriş yap
continue: Devam et
i_am_their: Ben onun...
# Simple relation names (for display)
my_role: rolüm
role: rol
section_privacy: Gizlilik
people_with_access_count: kişi erişimli
view_audit_log: Denetim günlüğünü görüntüle
export_data: Verilerimi indir
relation_with: İlişki
audit_log: Denetim günlüğü
audit_log_intro: "Etkinlik geçmişi:"
audit_log_desc: Bu dosyaya kimlerin eriştiğini veya değişiklik yaptığını takip edin
# Permissions (RBAC)
permissions_title: İzinler
permissions_subtitle: Bu dosyaya kimlerin erişebileceğini ve ne yapabileceklerini kontrol et
current_access: Mevcut erişim
grant_access: Erişim ver
no_grantees: Başka kimsenin bu dosyaya erişimi yok.
person_email: E-posta adresi
person_email_hint: Hesapları yoksa, oluşturmaya davet edilecekler.
person_name: İsim
select_role: Rol seç...
custom_role: Özel izinler
permissions: İzinler
op_read: Oku
op_write: Yaz
op_delete: Sil
op_manage: Yönet
grant: Erişim ver
revoke: İptal et
role_descriptions: Rol açıklamaları
ops_legend: İzin açıklamaları
op_read_desc: Verileri görüntüle
op_write_desc: Veri ekle/düzenle
op_delete_desc: Verileri kaldır
op_manage_desc: Kimin erişimi olduğunu yönet
permissions_updated: İzinler başarıyla güncellendi.
back: Geri
can_add_data: Veri ekleyebilir
install_title: "Claude'a Bağlan"
install_intro: "Claude'un sağlık verilerini analiz etmesi için inou bridge'i kur"
install_step1: "Adım 1: İndir"
install_step1_desc: "Platformun için bridge'i al"
install_download_intro: "İşletim sistemin için inou bridge'i indir:"
install_step2: "Adım 2: Yapılandır"
install_step2_desc: "Claude Desktop config'e ekle"
install_config_intro: "Claude Desktop yapılandırma dosyana bunu ekle:"
install_step3: "Adım 3: Test"
install_step3_desc: Bağlantıyı doğrula
install_test_intro: "Claude Desktop'u yeniden başlat ve sor: 'inou profillerimi göster'"
nav_install: "Claude'a Bağlan"
nav_home: Ana Sayfa
pending: beklemede
rate_limit_exceeded: Konumundan çok fazla kayıt denemesi. Lütfen yarın tekrar dene.
section_genetics: Genetik
no_genetics: Genetik veri yok
sex_0: bilinmiyor
sex_1: erkek
sex_2: kadın
sex_9: diğer
# Friend invite email
friend_invite_subject: "Bunu dene — %s"
friend_invite_p1: "<strong>inou</strong>'yu kullanıyorum, sağlık verilerini güvenli bir şekilde depolamak ve AI ile keşfetmek için. Tüm ailemin sağlık bilgilerini tek bir yerde tutuyorum — görüntüleme çalışmaları, laboratuvar sonuçları, tıbbi kayıtlar — ve sana da faydalı olabileceğini düşündüm."
friend_invite_p2: "Gerçek güç, hepsini anlamlandırmak için AI kullanabilmek: bir raporun ne anlama geldiğini anlamak, zaman içinde eğilimleri tespit etmek veya sadece sade bir dilde sorular sormak ve net yanıtlar almak."
friend_invite_btn: "inou'yu dene"
friend_invite_dear: "Sevgili %s,"
rel_0: sen
rel_1: Ebeveyn
rel_2: Çocuk
rel_3:
rel_4: Kardeş
rel_5: Veli
rel_6: Bakıcı
rel_7: Koç
rel_8: Doktor
rel_9: Arkadaş
rel_10: Diğer
rel_98: Diğer
rel_99: Demo
select_relation: İlişki seç...
audit_dossier_added: "%s için %s tarafından yeni dosya oluşturuldu"
audit_dossier_edited: "%s dosyası %s tarafından düzenlendi"
audit_access_granted: "%s için %s erişimi verildi"
audit_dossier_created: "%s tarafından hesap oluşturuldu"
audit_access_revoked: "%s için %s erişimi iptal edildi"
audit_file_upload: "%s dosyası %s tarafından yüklendi"
audit_file_delete: "%s dosyası %s tarafından silindi"
audit_file_category_change: "%s dosyasının kategorisi %s tarafından değiştirildi"
audit_genome_import: "%s genetik varyant içe aktarıldı"
# Categories (category000 = imaging, etc.)
category000: Görüntüleme
category001: Belge
category002: Laboratuvar sonucu
category003: Genom
category004: Yükleme
category005: Konsültasyon
category006: Tanı
category007: Görüntüleme bulgusu
category008: EEG bulgusu
category009: Vital bulgu
category010: Egzersiz
category011: İlaç
category012: Takviye
category013: Beslenme
category014: Doğurganlık
category015: Belirti
category016: Not
category017: Tıbbi geçmiş
category018: Aile öyküsü
category019: Ameliyat
category020: Hastaneye yatış
category021: Doğum kaydı
category022: Tıbbi cihaz
category023: Terapi
category024: Değerlendirme
category025: Sağlık sağlayıcı
category026: Soru
# Genome
genome_english_only: ""
genome_variants: varyant
genome_hidden: gizli
genome_show_all_categories: "Tüm %d kategoriyi göster"
# API
api_token: API Token
api_token_use: "API isteklerini kimlik doğrulamak için bu token'i kullanın:"
api_token_warning: "Bu özel tutun. Bu token'e sahip olan herkes sağlık verilerinize erişebilir."
api_token_none: "API'ye programatik olarak erişmek veya AI asistanları bağlamak için bir token oluşturun."
api_token_generate: Token Oluştur
api_token_regenerate: "Token'i Yeniden Oluştur"
api_token_regenerate_confirm: Bu, mevcut tokeninizi geçersiz kılacaktır. Bağlı tüm uygulamalar güncellenmelidir.
api_authentication: Kimlik doğrulama
api_auth_instructions: "API tokeninizi Authorization header'a ekleyin:"
copy: Kopyala
relation: İlişki
relation_to: İlişki
me: Ben

View File

@ -1,3 +1,5 @@
language_name: 中文
# Landing
headline_1: "你的健康数据。"
headline_2: "你的AI。"

View File

@ -2,6 +2,7 @@ package main
import (
"bufio"
"bytes"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
@ -144,7 +145,6 @@ type PageData struct {
HasLabs, HasGenome, Embed bool
StudyCount, LabCount, TotalSlices int
AuditList []AuditViewEntry
SelfStats DossierStats
APIToken string
TempToken string
TempTokenExpires string
@ -164,6 +164,16 @@ type PageData struct {
Languages []LangOption
RelationTargets []RelationTarget
RelationTo string
// OAuth consent
ClientName string
ClientID string
RedirectURI string
ResponseType string
State string
CodeChallenge string
CodeChallengeMethod string
UserName string
Content template.HTML
}
type CategoryAccess struct {
@ -194,12 +204,6 @@ func initDB() {
}
// Native names for languages (used in language selector dropdowns).
var langNativeNames = map[string]string{
"da": "Dansk", "de": "Deutsch", "en": "English", "es": "Español",
"fi": "Suomi", "fr": "Français", "it": "Italiano", "ja": "日本語",
"ko": "한국어", "nl": "Nederlands", "no": "Norsk", "pt": "Português",
"ru": "Русский", "sv": "Svenska", "zh": "中文",
}
func loadTranslations() {
translations = make(map[string]map[string]string)
@ -218,25 +222,29 @@ func loadTranslations() {
}
file.Close()
}
// Build sorted language options from loaded translations
codes := make([]string, 0, len(translations))
for code := range translations { codes = append(codes, code) }
sort.Strings(codes)
langOptions = make([]LangOption, 0, len(codes))
for _, code := range codes {
name := langNativeNames[code]
if name == "" { name = code }
flag := ""
// Reuse the same flag mapping as langFlag template func
// Build language options from lang_* keys in en.yaml, sorted by native name
en := translations["en"]
langOptions = nil
for k, name := range en {
if !strings.HasPrefix(k, "lang_") { continue }
code := strings.TrimPrefix(k, "lang_")
langOptions = append(langOptions, LangOption{Code: code, Name: name})
}
sort.Slice(langOptions, func(i, j int) bool { return langOptions[i].Name < langOptions[j].Name })
// Add flags
for i := range langOptions {
code := langOptions[i].Code
flags := map[string]string{
"en": "\U0001F1EC\U0001F1E7", "nl": "\U0001F1F3\U0001F1F1", "de": "\U0001F1E9\U0001F1EA",
"ru": "\U0001F1F7\U0001F1FA", "zh": "\U0001F1E8\U0001F1F3", "ja": "\U0001F1EF\U0001F1F5",
"ko": "\U0001F1F0\U0001F1F7", "fr": "\U0001F1EB\U0001F1F7", "es": "\U0001F1EA\U0001F1F8",
"pt": "\U0001F1E7\U0001F1F7", "it": "\U0001F1EE\U0001F1F9", "sv": "\U0001F1F8\U0001F1EA",
"no": "\U0001F1F3\U0001F1F4", "da": "\U0001F1E9\U0001F1F0", "fi": "\U0001F1EB\U0001F1EE",
"tr": "\U0001F1F9\U0001F1F7", "vi": "\U0001F1FB\U0001F1F3", "id": "\U0001F1EE\U0001F1E9",
"hi": "\U0001F1EE\U0001F1F3", "th": "\U0001F1F9\U0001F1ED", "pl": "\U0001F1F5\U0001F1F1",
"uk": "\U0001F1FA\U0001F1E6",
}
if f, ok := flags[code]; ok { flag = f }
langOptions = append(langOptions, LangOption{Code: code, Name: name, Flag: flag})
if f, ok := flags[code]; ok { langOptions[i].Flag = f }
}
}
@ -588,6 +596,23 @@ func render(w http.ResponseWriter, r *http.Request, data PageData) {
data.T = translations[data.Lang]
if data.T == nil { data.T = translations["en"] }
data.RequestPath = r.URL.Path
if data.Languages == nil { data.Languages = langOptions }
// Pre-render page content — use localized template if available
page := data.Page
if data.Lang != "en" {
if templates.Lookup(data.Page + "_" + data.Lang) != nil {
page = data.Page + "_" + data.Lang
}
}
if t := templates.Lookup(page); t != nil {
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
fmt.Fprintf(os.Stderr, "Page template %q error: %v\n", data.Page, err)
http.Error(w, "Template error", 500)
return
}
data.Content = template.HTML(buf.String())
}
if err := templates.ExecuteTemplate(w, "base.tmpl", data); err != nil {
fmt.Fprintf(os.Stderr, "Template error: %v\n", err)
http.Error(w, "Template error", 500)
@ -599,9 +624,7 @@ func handleLanding(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" { http.NotFound(w, r); return }
p := getLoggedInDossier(r)
lang := getLang(r)
pageName := "landing"
switch lang { case "nl", "ru", "de", "fr", "es", "pt", "it", "sv", "no", "da", "fi", "ja", "ko", "zh": pageName = "landing_" + lang }
render(w, r, PageData{Page: pageName, Lang: lang, Dossier: p})
render(w, r, PageData{Page: "landing", Lang: lang, Dossier: p})
}
func getClientIP(r *http.Request) string {
@ -770,6 +793,11 @@ func handlePrivacy(w http.ResponseWriter, r *http.Request) {
render(w, r, PageData{Page: "privacy", Lang: getLang(r), Dossier: p})
}
func handleDocs(w http.ResponseWriter, r *http.Request) {
p := getLoggedInDossier(r)
render(w, r, PageData{Page: "docs", Lang: getLang(r), Dossier: p})
}
func handlePricing(w http.ResponseWriter, r *http.Request) {
p := getLoggedInDossier(r)
render(w, r, PageData{Page: "pricing", Lang: getLang(r), Dossier: p})
@ -965,7 +993,7 @@ func getDossierStats(dossierID string) DossierStats {
if cj == 0 { cj = 1 }
return ci > cj
})
if len(all) > 5 {
if len(all) > 6 {
stats.OverflowCount = len(all) - 5
all = all[:5]
}
@ -979,8 +1007,6 @@ func handleDashboard(w http.ResponseWriter, r *http.Request) {
if p.Name == "" { http.Redirect(w, r, "/onboard", http.StatusSeeOther); return }
lang := getLang(r)
selfStats := getDossierStats(p.DossierID)
// All cat-0 entries I can access (self + granted) — RBAC checked
allDossiers, _ := lib.EntryRead(p.DossierID, "", &lib.Filter{Category: 0})
// Access grants for relation/ops metadata
@ -988,8 +1014,14 @@ func handleDashboard(w http.ResponseWriter, r *http.Request) {
grantMap := map[string]*lib.Access{}
for _, g := range grants { grantMap[g.DossierID] = g }
// Build accessible dossier list with names index
// Build accessible dossier list — self first, then others
nameMap := map[string]string{} // dossierID → name
self := AccessEntry{
DossierID: p.DossierID, Name: p.Name, DateOfBirth: p.DateOfBirth,
Sex: p.Sex, Lang: p.Preferences.Language,
Relation: T(lang, "you"), CanEdit: true, IsSelf: true,
Stats: getDossierStats(p.DossierID),
}
var accessible []AccessEntry
for _, entry := range allDossiers {
if entry.DossierID == p.DossierID { continue }
@ -1098,7 +1130,8 @@ func handleDashboard(w http.ResponseWriter, r *http.Request) {
prevTier, prevGroup = t, g
}
render(w, r, PageData{Page: "dashboard", Lang: lang, Embed: isEmbed(r), Dossier: p, AccessibleDossiers: accessible, SelfStats: selfStats})
accessible = append([]AccessEntry{self}, accessible...)
render(w, r, PageData{Page: "dashboard", Lang: lang, Embed: isEmbed(r), Dossier: p, AccessibleDossiers: accessible})
}
func handleDemo(w http.ResponseWriter, r *http.Request) {
@ -2151,6 +2184,7 @@ func setupMux() http.Handler {
mux.HandleFunc("/invite", handleInvite)
mux.HandleFunc("/privacy-policy", handlePrivacy)
mux.HandleFunc("/privacy-policy/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/privacy-policy", http.StatusMovedPermanently) })
mux.HandleFunc("/docs", handleDocs)
mux.HandleFunc("/pricing", handlePricing)
mux.HandleFunc("/faq", handleFAQ)
mux.HandleFunc("/security", handleSecurity)

View File

@ -52,68 +52,16 @@
<div class="lang-menu">
<span class="lang-current" style="text-transform: uppercase;">{{.Lang}}</span>
<div class="lang-dropdown">
<a href="#" onclick="setLang('en')"{{if eq .Lang "en"}} class="active"{{end}}>English</a>
<a href="#" onclick="setLang('nl')"{{if eq .Lang "nl"}} class="active"{{end}}>Nederlands</a>
<a href="#" onclick="setLang('de')"{{if eq .Lang "de"}} class="active"{{end}}>Deutsch</a>
<a href="#" onclick="setLang('fr')"{{if eq .Lang "fr"}} class="active"{{end}}>Français</a>
<a href="#" onclick="setLang('es')"{{if eq .Lang "es"}} class="active"{{end}}>Español</a>
<a href="#" onclick="setLang('pt')"{{if eq .Lang "pt"}} class="active"{{end}}>Português</a>
<a href="#" onclick="setLang('it')"{{if eq .Lang "it"}} class="active"{{end}}>Italiano</a>
<a href="#" onclick="setLang('ru')"{{if eq .Lang "ru"}} class="active"{{end}}>Русский</a>
<a href="#" onclick="setLang('sv')"{{if eq .Lang "sv"}} class="active"{{end}}>Svenska</a>
<a href="#" onclick="setLang('no')"{{if eq .Lang "no"}} class="active"{{end}}>Norsk</a>
<a href="#" onclick="setLang('da')"{{if eq .Lang "da"}} class="active"{{end}}>Dansk</a>
<a href="#" onclick="setLang('fi')"{{if eq .Lang "fi"}} class="active"{{end}}>Suomi</a>
<a href="#" onclick="setLang('ja')"{{if eq .Lang "ja"}} class="active"{{end}}>日本語</a>
<a href="#" onclick="setLang('ko')"{{if eq .Lang "ko"}} class="active"{{end}}>한국어</a>
<a href="#" onclick="setLang('zh')"{{if eq .Lang "zh"}} class="active"{{end}}>中文</a>
{{range .Languages}}{{if eq $.Lang .Code}}<a href="#" onclick="setLang('{{.Code}}')" class="active">{{.Name}}</a>
{{end}}{{end}}{{range .Languages}}{{if ne $.Lang .Code}}<a href="#" onclick="setLang('{{.Code}}')">{{.Name}}</a>
{{end}}{{end}}
</div>
</div>
</div>
</nav>
{{end}}
{{if eq .Page "landing"}}{{template "landing" .}}
{{else if eq .Page "landing_nl"}}{{template "landing_nl" .}}
{{else if eq .Page "landing_ru"}}{{template "landing_ru" .}}
{{else if eq .Page "landing_de"}}{{template "landing_de" .}}
{{else if eq .Page "landing_fr"}}{{template "landing_fr" .}}
{{else if eq .Page "landing_es"}}{{template "landing_es" .}}
{{else if eq .Page "landing_pt"}}{{template "landing_pt" .}}
{{else if eq .Page "landing_it"}}{{template "landing_it" .}}
{{else if eq .Page "landing_sv"}}{{template "landing_sv" .}}
{{else if eq .Page "landing_no"}}{{template "landing_no" .}}
{{else if eq .Page "landing_da"}}{{template "landing_da" .}}
{{else if eq .Page "landing_fi"}}{{template "landing_fi" .}}
{{else if eq .Page "landing_ja"}}{{template "landing_ja" .}}
{{else if eq .Page "landing_ko"}}{{template "landing_ko" .}}
{{else if eq .Page "landing_zh"}}{{template "landing_zh" .}}
{{else if eq .Page "verify"}}{{template "verify" .}}
{{else if eq .Page "onboard"}}{{template "onboard" .}}
{{else if eq .Page "minor_error"}}{{template "minor_error" .}}
{{else if eq .Page "dashboard"}}{{template "dashboard" .}}
{{else if eq .Page "dossier"}}{{template "dossier" .}}
{{else if eq .Page "add_dossier"}}{{template "add_dossier" .}}
{{else if eq .Page "share"}}{{template "share" .}}
{{else if eq .Page "upload"}}{{template "upload" .}}
{{else if eq .Page "audit"}}{{template "audit" .}}
{{else if eq .Page "connect"}}{{template "connect" .}}
{{else if eq .Page "connect_nl"}}{{template "connect_nl" .}}
{{else if eq .Page "connect_ru"}}{{template "connect_ru" .}}
{{else if eq .Page "invite"}}{{template "invite" .}}
{{else if eq .Page "login"}}{{template "login" .}}
{{else if eq .Page "privacy"}}{{template "privacy" .}}
{{else if eq .Page "security"}}{{template "security" .}}
{{else if eq .Page "dpa"}}{{template "dpa" .}}
{{else if eq .Page "terms"}}{{template "terms" .}}
{{else if eq .Page "styleguide"}}{{template "styleguide" .}}
{{else if eq .Page "pricing"}}{{template "pricing" .}}
{{else if eq .Page "faq"}}{{template "faq" .}}
{{else if eq .Page "trackers"}}{{template "trackers" .}}
{{else if eq .Page "permissions"}}{{template "permissions" .}}
{{else if eq .Page "edit_access"}}{{template "edit_access" .}}
{{else if eq .Page "edit_rbac"}}{{template "edit_rbac" .}}
{{end}}
{{.Content}}
<script>
function setLang(lang) {

View File

@ -0,0 +1,249 @@
{{define "dpa_de"}}
<style>
.dpa-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 24px 80px;
}
.dpa-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 48px;
margin-bottom: 24px;
}
.dpa-card h1 {
font-size: 2.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.dpa-card .intro {
font-size: 1.15rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 0;
}
.dpa-card h2 {
font-size: 1.4rem;
font-weight: 600;
color: var(--text);
margin-top: 0;
margin-bottom: 24px;
}
.dpa-card h3 {
font-size: 1.1rem;
font-weight: 600;
color: var(--text);
margin-top: 24px;
margin-bottom: 8px;
}
.dpa-card h3:first-child { margin-top: 0; }
.dpa-card p {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 16px;
}
.dpa-card p:last-child { margin-bottom: 0; }
.dpa-card strong {
font-weight: 600;
color: var(--text);
}
.dpa-card a {
color: var(--accent);
}
.inou-brand {
font-weight: 700;
color: var(--accent);
}
.dpa-card ul {
margin: 0 0 16px 0;
padding-left: 24px;
}
.dpa-card li {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 8px;
}
/* Mobile */
@media (max-width: 768px) {
.dpa-container { padding: 24px 16px 48px; }
.dpa-card { padding: 32px 24px; }
.dpa-card h1 { font-size: 2rem; }
.dpa-card .intro { font-size: 1.05rem; }
.dpa-card h2 { font-size: 1.25rem; }
.dpa-card h3 { font-size: 1rem; }
.dpa-card p { font-size: 0.95rem; }
}
@media (max-width: 480px) {
.dpa-container { padding: 16px 12px 32px; }
.dpa-card { padding: 24px 16px; }
.dpa-card h1 { font-size: 1.75rem; }
.dpa-card .intro { font-size: 1rem; }
.dpa-card h2 { font-size: 1.15rem; }
.dpa-card p { font-size: 0.9rem; }
}
</style>
<div class="dpa-container">
<div class="dpa-card">
<h1>Vertrag zur Datenverarbeitung</h1>
<p class="intro">Dieser Vertrag beschreibt, wie <span class="inou-brand">inou</span> deine Gesundheitsdaten verarbeitet. Er gilt für alle Nutzer*innen und alle Drittanbieter-Dienste, die über unsere Plattform auf deine Daten zugreifen.</p>
</div>
<div class="dpa-card">
<h2>Definitionen</h2>
<h3>Datenverantwortlicher.</h3>
<p>Du. Du entscheidest, welche Daten du hochlädst, wer darauf zugreifen darf und wann du sie löschen lässt.</p>
<h3>Datenverarbeiter.</h3>
<p><span class="inou-brand">inou</span>. Wir speichern, verschlüsseln und übertragen deine Daten gemäß deinen Anweisungen.</p>
<h3>Drittanbieter-Dienste.</h3>
<p>Du kannst externe Dienste mit deinem Konto verbinden, z.B. KIAssistenten. Diese Dienste agieren als unabhängige Verantwortliche oder als direkt von dir beauftragte Verarbeiter nicht als unsere Subverarbeiter. Wir beauftragen keine Subverarbeiter für Speicherung oder Kernfunktionalität.</p>
</div>
<div class="dpa-card">
<h2>Verarbeitete Daten</h2>
<h3>Gesundheitsdaten.</h3>
<p>Medizinische Bildgebung (DICOMDateien einschließlich MRI, CT, Röntgen), Laborergebnisse, genetische/genomische Daten und alle anderen Gesundheitsinformationen, die du hochlädst. Genetische und genomische Daten stellen besondere Kategorien von Daten gemäß Art.9 der GDPR dar und werden ausschließlich auf Grundlage deiner ausdrücklichen Zustimmung verarbeitet.</p>
<h3>Kontodaten.</h3>
<p>Name, EMail-Adresse, Geburtsdatum und Geschlecht. Werden für die Kontoverwaltung und den medizinischen Kontext verwendet.</p>
<h3>Technische Daten.</h3>
<p>IPAdressen und SitzungsIDs. Werden ausschließlich für Sicherheit und Zugriffskontrolle verwendet.</p>
</div>
<div class="dpa-card">
<h2>Wie wir sie verarbeiten</h2>
<h3>Speicherung.</h3>
<p>Alle Gesundheitsdaten werden mit FIPS1403 validierter Kryptografie verschlüsselt, bevor sie gespeichert werden. Die Daten liegen auf dedizierter Infrastruktur in den Vereinigten Staaten, die wir besitzen und betreiben.</p>
<h3>Übertragung.</h3>
<p>Alle Daten im Transit sind durch TLS1.3 verschlüsselt. Wenn du DrittanbieterDienste verbindest, reisen die Daten über eine verschlüsselte Brücke direkt zu deiner Sitzung.</p>
<h3>Zugriff.</h3>
<p>Nur du und von dir explizit autorisierte Konten können auf deine Daten zugreifen. Der Zugriff von Mitarbeitern erfordert deine ausdrückliche Anfrage, ist auf leitende Personen beschränkt und wird protokolliert.</p>
</div>
<div class="dpa-card">
<h2>Verarbeitungsbeschränkungen</h2>
<p>Wir verarbeiten deine Daten ausschließlich zur Bereitstellung des Dienstes. Insbesondere tun wir nicht:</p>
<ul>
<li>Deine Daten für das Training von KIModellen verwenden</li>
<li>Deine Daten an Dritte verkaufen, vermieten oder teilen</li>
<li>Deine Daten für Werbung oder Profiling analysieren</li>
<li>Auf deine Daten ohne deine ausdrückliche Anfrage zugreifen</li>
<li>Deine Daten nach Löschung des Kontos aufbewahren</li>
</ul>
</div>
<div class="dpa-card">
<h2>DrittanbieterVerbindungen</h2>
<p>Wenn du einen KIAssistenten oder einen anderen Dienst mit <span class="inou-brand">inou</span> verbindest:</p>
<ul>
<li>Du autorisierst jede Verbindung explizit</li>
<li>Daten werden nur für deine aktive Sitzung übertragen</li>
<li>Wir speichern keine Kopien der übertragenen Daten</li>
<li>Du kannst den Zugriff jederzeit widerrufen</li>
<li>Jeder Drittanbieter arbeitet nach seiner eigenen Datenschutzerklärung</li>
</ul>
<p>Wir empfehlen, die Datenschutzerklärung jedes Dienstes, den du verbindest, zu prüfen.</p>
</div>
<div class="dpa-card">
<h2>Sicherheitsmaßnahmen</h2>
<h3>Verschlüsselung.</h3>
<p>FIPS1403 validierte Verschlüsselung im Ruhezustand. TLS1.3 Verschlüsselung in der Übertragung. AnwendungsschichtVerschlüsselung vor der Datenbankspeicherung.</p>
<h3>Infrastruktur.</h3>
<p>Dedizierte Hardware. Keine geteilten CloudUmgebungen. Redundante Speicherung mit RAIDZ2. Unterbrechungsfreie Stromversorgung mit GeneratorBackup.</p>
<h3>Zugriffskontrolle.</h3>
<p>Rollenbasierte Zugriffskontrolle. Pflichtauthentifizierung. Alle Zugriffe werden protokolliert und auditierbar.</p>
<h3>Überwachung.</h3>
<p>Kontinuierliche automatisierte Überwachung. Eindringungserkennung. Regelmäßige Sicherheitsbewertungen.</p>
</div>
<div class="dpa-card">
<h2>Datenspeicherung</h2>
<p>Wir behalten deine Daten so lange auf, wie dein Konto aktiv ist. Wenn du dein Konto löschst:</p>
<ul>
<li>Alle personenbezogenen Daten werden dauerhaft gelöscht</li>
<li>Alle Gesundheitsdaten werden dauerhaft gelöscht</li>
<li>Löschung erfolgt sofort und unwiderruflich</li>
<li>Backups werden innerhalb von 30 Tagen überschrieben</li>
</ul>
<p>Wir bieten keine Wiederherstellung gelöschter Daten an.</p>
</div>
<div class="dpa-card">
<h2>Deine Rechte</h2>
<h3>Zugriff.</h3>
<p>Sieh dir alles an und exportiere alles, was wir speichern die von dir eingegebenen Daten, Kontodetails, Zugriffsprotokolle und AuditHistorie.</p>
<h3>Berichtigung.</h3>
<p>Korrigiere ungenaue Daten direkt oder auf Anfrage.</p>
<h3>Löschung.</h3>
<p>Lösche dein Konto und alle zugehörigen Daten sofort.</p>
<h3>Portabilität.</h3>
<p>Lade die von dir eingegebenen Daten in Standardformaten herunter. Deine hochgeladenen Dateien gehören bereits dir.</p>
<h3>Widerspruch.</h3>
<p>Widerrufe jede Erlaubnis jederzeit. Wir reagieren sofort.</p>
</div>
<div class="dpa-card">
<h2>Compliance</h2>
<p>Dieser Vertrag wurde entwickelt, um die folgenden Vorschriften einzuhalten:</p>
<ul>
<li><strong>GDPR</strong> (DatenschutzGrundverordnung der Europäischen Union)</li>
<li><strong>FADP</strong> (Schweizerisches Bundesgesetz über den Datenschutz)</li>
<li><strong>HIPAA</strong> (US Health Insurance Portability and Accountability Act)</li>
</ul>
<p>Wir wenden den höchsten Standard unabhängig von deiner Gerichtsbarkeit an.</p>
</div>
<div class="dpa-card">
<h2>Kontakt</h2>
<p>Datenschutzbeauftragter: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
<p>Fragen zur Datenverarbeitung: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
<p>Dieses Dokument wurde zuletzt am 8. Februar 2026 aktualisiert.</p>
</div>
{{template "footer"}}
</div>
{{end}}

1005
portal/templates/faq_de.tmpl Normal file

File diff suppressed because it is too large Load Diff

1096
portal/templates/faq_es.tmpl Normal file

File diff suppressed because it is too large Load Diff

1097
portal/templates/faq_nl.tmpl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,121 +1,519 @@
{{define "landing_de"}}
<style>
.landing-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 48px; width: 100%; margin-left: auto; margin-right: auto; margin-bottom: 24px; }
.hero-answer { text-align: center; font-size: 1.25rem; font-weight: 400; color: var(--text); line-height: 1.8; margin-top: 16px; margin-bottom: 32px; }
.hero-answer .inou { font-weight: 700; color: var(--accent); }
.hero-tagline { text-align: center; font-size: 1.3rem; font-weight: 600; color: var(--text); margin-bottom: 32px; }
.landing-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 48px;
width: 100%;
margin-left: auto;
margin-right: auto;
margin-bottom: 24px;
}
/* Carousel */
.carousel {
position: relative;
width: 100%;
aspect-ratio: 16/9;
overflow: hidden;
border-radius: 6px;
margin-bottom: 32px;
}
.carousel-track {
display: flex;
height: 100%;
transition: transform 0.5s ease;
}
.carousel-slide {
min-width: 100%;
height: 100%;
background-size: cover;
background-position: center;
}
.carousel-dots {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 32px;
}
.carousel-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--border);
border: none;
padding: 0;
cursor: pointer;
transition: background 0.2s;
}
.carousel-dot.active {
background: var(--accent);
}
/* Hero - Block 1 */
.hero-sources {
font-size: 1.1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.9;
margin-bottom: 32px;
}
.hero-sources span { display: block; }
.hero-sources .different {
}
.hero-pivot {
font-size: 1.15rem;
font-weight: 400;
color: var(--text);
line-height: 1.8;
margin-bottom: 32px;
}
.hero-pivot span { display: block; }
.hero-pivot .emphasis {
font-size: 1.3rem;
font-weight: 600;
margin-top: 12px;
}
.hero-answer {
text-align: center;
font-size: 1.7rem;
font-weight: 500;
color: var(--text);
line-height: 1.5;
margin-top: 16px;
margin-bottom: 8px;
}
.hero-answer .inou {
font-weight: 700;
color: var(--accent);
}
.hero-tagline {
text-align: center;
font-size: 2.8rem;
font-weight: 700;
color: var(--text);
margin-bottom: 12px;
}
.carousel-caption {
text-align: center;
font-size: 0.95rem;
color: var(--muted);
line-height: 1.5;
min-height: 3em;
padding: 0 24px;
margin-bottom: 24px;
}
.hero-cta { margin-bottom: 0; text-align: center; }
.hero-cta .btn { padding: 18px 56px; font-size: 0.9rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; border-radius: 4px; }
.story-prose.warm { font-size: 1.1rem; line-height: 1.8; color: var(--text); }
.story-prose.warm p { margin-bottom: 20px; }
.story-prose.warm .emphasis { font-weight: 600; font-size: 1.15rem; }
.story-title { font-size: 1.25rem; font-weight: 600; color: var(--text); margin-bottom: 32px; }
.story-pair { margin-bottom: 32px; }
.story-pair .data { font-size: 1.1rem; font-weight: 400; color: var(--text); margin-bottom: 4px; }
.story-pair .reality { font-size: 1rem; font-weight: 300; font-style: italic; color: var(--text-muted); }
.story-transition { font-size: 1.25rem; font-weight: 400; color: var(--text); line-height: 1.8; margin: 32px 0; padding: 24px 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.story-gaps { font-size: 1rem; font-weight: 300; color: var(--text-muted); line-height: 1.8; margin-bottom: 32px; }
.hero-cta .btn {
padding: 18px 56px;
font-size: 0.9rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
border-radius: 4px;
}
/* Story - Block 2 */
.story-prose.warm {
font-size: 1.1rem;
line-height: 1.8;
color: var(--text);
}
.story-prose.warm p {
margin-bottom: 20px;
}
.story-prose.warm .emphasis {
font-weight: 600;
font-size: 1.15rem;
}
.story-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text);
margin-bottom: 32px;
}
.story-pair {
margin-bottom: 32px;
}
.story-pair .data {
font-size: 1.1rem;
font-weight: 400;
color: var(--text);
margin-bottom: 4px;
}
.story-pair .reality {
font-size: 1rem;
font-weight: 300;
font-style: italic;
color: var(--text-muted);
}
.story-transition {
font-size: 1.25rem;
font-weight: 400;
color: var(--text);
line-height: 1.8;
margin: 32px 0;
padding: 24px 0;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
.story-gaps {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 32px;
}
.story-gaps span { display: block; }
.story-gaps .indent { font-style: italic; }
.story-connections { font-size: 1rem; font-weight: 300; color: var(--text-muted); line-height: 1.8; margin-bottom: 32px; }
.story-gaps .indent { font-style: italic; }
.story-connections {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 32px;
}
.story-connections span { display: block; }
.story-ai { font-size: 1.25rem; font-weight: 400; color: var(--text); line-height: 1.8; margin-bottom: 32px; }
.story-ai {
font-size: 1.25rem;
font-weight: 400;
color: var(--text);
line-height: 1.8;
margin-bottom: 32px;
}
.story-ai span { display: block; }
.story-ai .last { font-style: italic; }
.story-prose { font-size: 1rem; font-weight: 300; color: var(--text-muted); line-height: 1.8; margin-bottom: 20px; }
.story-ai .last {
font-style: italic;
}
.story-prose {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 20px;
}
.story-prose:last-of-type { margin-bottom: 32px; }
.story-prose strong { font-weight: 600; color: var(--text); }
.story-prose .inou { font-weight: 700; color: var(--accent); }
.story-closing { font-size: 1.25rem; font-weight: 400; color: var(--text); padding-top: 24px; border-top: 1px solid var(--border); }
.story-closing .inou { font-weight: 700; color: var(--accent); }
.trust-card { width: 100%; margin-left: auto; margin-right: auto; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 32px 48px; margin-bottom: 24px; }
.trust-card .section-label { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 24px; }
.trust-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; }
.trust-item { font-size: 0.9rem; font-weight: 300; color: var(--text-muted); line-height: 1.6; }
.trust-item strong { display: block; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.landing-footer { padding: 16px 0; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; }
.landing-footer-left { font-size: 0.9rem; color: var(--text-muted); display: flex; gap: 16px; align-items: center; }
.landing-footer-left a { color: var(--text-muted); text-decoration: none; }
.story-closing {
font-size: 1.25rem;
font-weight: 400;
color: var(--text);
padding-top: 24px;
border-top: 1px solid var(--border);
}
.story-closing .inou {
font-weight: 700;
color: var(--accent);
}
/* Trust section */
.trust-card {
width: 100%;
margin-left: auto;
margin-right: auto;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 32px 48px;
margin-bottom: 24px;
}
.trust-card .section-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
margin-bottom: 24px;
}
.trust-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 32px;
}
.trust-item {
font-size: 0.9rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.6;
}
.trust-item strong {
display: block;
font-weight: 600;
color: var(--text);
margin-bottom: 4px;
}
/* Footer */
.landing-footer {
padding: 16px 0;
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.landing-footer-left {
font-size: 0.9rem;
color: var(--text-muted);
display: flex;
gap: 16px;
align-items: center;
}
.landing-footer-left a {
color: var(--text-muted);
text-decoration: none;
}
.landing-footer-left a:hover { color: var(--accent); }
.landing-footer-right { font-size: 1rem; }
.landing-footer-right .inou { font-weight: 700; color: var(--accent); }
.landing-footer-right .health { font-weight: 400; color: var(--text-muted); }
@media (max-width: 768px) { .trust-card { padding: 24px; } .trust-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; } }
@media (max-width: 480px) { .trust-card { padding: 20px 16px; } .trust-grid { grid-template-columns: 1fr; gap: 20px; } .landing-footer { flex-direction: column; gap: 12px; text-align: center; } .landing-footer-left { flex-direction: column; gap: 8px; } }
.landing-footer-right .inou {
font-weight: 700;
color: var(--accent);
}
.landing-footer-right .health {
font-weight: 400;
color: var(--text-muted);
}
/* Mobile */
@media (max-width: 768px) {
.trust-card {
width: 100%;
margin-left: auto;
margin-right: auto; padding: 24px; }
.hero-sources {
font-size: 1rem; line-height: 1.8; margin-bottom: 32px; }
.hero-pivot { font-size: 1.1rem; margin-bottom: 32px;
}
.hero-pivot .emphasis { font-size: 1.3rem; }
.hero-answer {
text-align: center; font-size: 1.2rem; margin-top: 16px;
margin-bottom: 8px; }
.hero-tagline { font-size: 2rem; margin-bottom: 8px; }
.carousel-caption { font-size: 0.85rem; }
.hero-cta .btn { padding: 14px 40px; }
.story-pair .data { font-size: 1rem; }
.story-pair .reality { font-size: 0.95rem; }
.trust-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; }
}
@media (max-width: 480px) {
.trust-card {
width: 100%;
margin-left: auto;
margin-right: auto; padding: 20px 16px; }
.hero-sources {
font-size: 0.95rem; line-height: 1.9; }
.hero-pivot { font-size: 1rem; }
.hero-pivot .emphasis { font-size: 1.2rem; }
.story-pair { margin-bottom: 24px; }
.trust-grid { grid-template-columns: 1fr; gap: 20px; }
.landing-footer { flex-direction: column; gap: 12px; text-align: center; }
.landing-footer-left { flex-direction: column; gap: 8px; }
}
</style>
<div class="sg-container">
<div class="landing-card">
<div class="hero">
<div class="hero-answer"><span class="inou">inou</span> organisiert und teilt Ihre Gesundheitsakte mit Ihrer KI — sicher und privat.</div>
<div class="hero-tagline">Ihre Gesundheit, verstanden.</div>
<div class="hero-cta">{{if .Dossier}}<a href="/invite" class="btn btn-primary">Freund einladen</a>{{else}}<a href="/start" class="btn btn-primary">Anmelden</a>{{end}}{{if .Error}}<div class="error" style="margin-top: 24px;">{{.Error}}</div>{{end}}</div>
<div class="hero-tagline">Deine Gesundheit, verstanden.</div>
<div class="hero-answer">Alle deine Gesundheitsdaten — organisiert, privat und bereit für deine KI.</div>
<div class="carousel">
<div class="carousel-track">
<div class="carousel-slide" style="background-image: url('/static/carousel-1.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-2.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-3.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-4.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-5.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-6.webp')"></div>
</div>
</div>
<div class="carousel-caption" id="carousel-caption">Verfolge deine Labortrends im Zeitverlauf — sieh genau, was deine KI sieht, wenn sie eine Änderung meldet.</div>
<div class="carousel-dots">
<button class="carousel-dot active" data-index="0"></button>
<button class="carousel-dot" data-index="1"></button>
<button class="carousel-dot" data-index="2"></button>
<button class="carousel-dot" data-index="3"></button>
<button class="carousel-dot" data-index="4"></button>
<button class="carousel-dot" data-index="5"></button>
</div>
<div class="hero-cta">
{{if .Dossier}}<a href="/invite" class="btn btn-primary">Einen Freund einladen</a>{{else}}<a href="/start" class="btn btn-primary">Einloggen</a>{{end}}
{{if .Error}}<div class="error" style="margin-top: 24px;">{{.Error}}</div>{{end}}
</div>
</div>
</div>
<div class="landing-card">
<div class="story">
<h2 class="story-title">Sie brauchen KI für Ihre Gesundheit</h2>
<h2 class="story-title">Du brauchst KI für deine Gesundheit</h2>
<div class="story-prose warm">
<p>Ihre Gesundheitsdaten sind über Dutzende von Orten verstreut — bei Ihrem Kardiologen, Ihrem Neurologen, im Labor, in Ihrer Smartwatch, Ihren Apps, Ihrem 23andMe. Und nur Sie kennen den Rest: was Sie essen, was Sie trinken, welche Nahrungsergänzungsmittel Sie nehmen. Ihr Trainingsplan. Ihre Symptome. Ihre Ziele — ob Sie schwanger werden möchten, für einen Marathon trainieren oder einfach weniger müde sein wollen.</p>
<p>Ob Sie gesund sind und es bleiben wollen, mit einer schwierigen Diagnose kämpfen oder sich um ein Familienmitglied kümmern, das sich nicht selbst vertreten kann — kein einzelner Arzt sieht das vollständige Bild. Kein System verbindet alles.</p>
<p>Aber Sie haben Zugang zu allem. Ihnen fehlt nur die Expertise, um alles zu verstehen.</p>
<p class="emphasis">Ihre KI hat sie. inou gibt ihr das vollständige Bild.</p>
<p>Deine Gesundheitsdaten leben an einem Dutzend verschiedenen Orten — bei deinem Kardiologen, deinem Neurologen, deinem Labor, deiner Uhr, deinen Apps, deinem 23andMe. Und nur du kennst den Rest: was du isst, was du trinkst, welche Nahrungsergänzungsmittel du einnimmst. Deine Trainingsroutine. Deine Symptome. Deine Ziele — ob du versuchst schwanger zu werden, dich auf einen Marathon vorzubereiten oder einfach nur weniger erschöpft zu fühlen.</p>
<p>Ob du gesund bist und so bleiben willst, eine schwierige Diagnose durchläufst oder dich um ein Familienmitglied kümmerst, das sich nicht selbst vertreten kann — kein einziger Arzt sieht das komplette Bild. Kein System verbindet es.</p>
<p>Aber <strong><em>du</em></strong> hast Zugriff auf alles. Du hast einfach nicht die Expertise, um alles zu verstehen.</p>
<p class="emphasis">Deine KI macht es. inou gibt dir das komplette Bild.</p>
</div>
</div>
</div>
<div class="landing-card">
<div class="story">
<h2 class="story-title">Die Herausforderung</h2>
<div class="story-pair"><div class="data">Ihr MRT hat 4.000 Schichten.</div><div class="reality">Es wurde in 10 Minuten ausgewertet.</div></div>
<div class="story-pair"><div class="data">Ihr Genom hat Millionen von Varianten.</div><div class="reality">Sie haben nur Ihre Augenfarbe und Ihre Herkunft erfahren.</div></div>
<div class="story-pair"><div class="data">Ihr Blutbild hat Dutzende von Markern.</div><div class="reality">Ihr Arzt sagte "alles sieht gut aus."</div></div>
<div class="story-pair"><div class="data">Ihre Uhr hat 10.000 Stunden Schlaf aufgezeichnet.</div><div class="reality">Ihr Trainer weiß nicht, dass sie existiert.</div></div>
<div class="story-pair"><div class="data">Sie haben hundert verschiedene Nahrungsergänzungsmittel ausprobiert.</div><div class="reality">Niemand hat gefragt, welche.</div></div>
<div class="story-transition">Die Verbindungen sind da.<br>Sie sind nur zu komplex für eine einzelne Person.</div>
<div class="story-pair">
<div class="data">Deine MRT hat 4.000 Schichten.</div>
<div class="reality">Es wurde in 10 Minuten gelesen.</div>
</div>
<div class="story-pair">
<div class="data">Dein Genom hat Millionen von Varianten.</div>
<div class="reality">Alles, was du gelernt hast, war deine Augenfarbe und wo deine Vorfahren herkommen.</div>
</div>
<div class="story-pair">
<div class="data">Deine Blutuntersuchung hat Dutzende von Markern.</div>
<div class="reality">Dein Arzt sagte: 'Alles sieht gut aus.'</div>
</div>
<div class="story-pair">
<div class="data">Deine Uhr hat 10.000 Stunden Schlaf aufgezeichnet.</div>
<div class="reality">Dein Trainer weiß nicht, dass es existiert.</div>
</div>
<div class="story-pair">
<div class="data">Du hast einhundert verschiedene Nahrungsergänzungsmittel ausprobiert.</div>
<div class="reality">Niemand hat gefragt, welche.</div>
</div>
<div class="story-transition">
Die Verbindungen sind da.<br>
Sie sind einfach zu komplex, damit eine Person sie erfassen kann.
</div>
<div class="story-gaps">
<span>Niemand weiß, wie Ihr Körper Warfarin verarbeitet — nicht einmal Sie.</span>
<span class="indent">Aber die Antwort könnte bereits in Ihrem 23andMe versteckt sein.</span>
<span>Dieses "unauffällig" in Ihrem MRT — hat jemand wirklich alle 4.000 Schichten genau angesehen?</span>
<span>Ihre Schilddrüse ist "im Normbereich" — aber niemand hat sie mit Ihrer Müdigkeit, Ihrem Gewicht, dass Ihnen immer kalt ist, verbunden.</span>
<span>Niemand weiß, wie dein Körper Warfarin verarbeitet — nicht einmal du.</span>
<span class="indent">Aber die Antwort könnte bereits in deinem 23andMe versteckt sein.</span>
<span>Das 'unauffällige' auf deiner MRT — hat jemand alle 4.000 Schichten genau betrachtet?</span>
<span>Deine Schilddrüse ist 'im Bereich' — aber niemand hat es mit deiner Müdigkeit, deinem Gewicht, ständigem Kältegefühl verbunden.</span>
</div>
<div class="story-connections">
<span>Niemand verbindet Ihren Nachmittagskaffee mit Ihrer Schlafqualität.</span>
<span>Ihren Eisenspiegel mit Ihrer Trainingsmüdigkeit.</span>
<span>Ihre Genetik mit Ihrem Gehirnnebel.</span>
<span>Niemand verbindet deinen Nachmittagscafé mit deinen Schlafwerten.</span>
<span>Deine Eisenwerte mit deiner Trainingsmüdigkeit.</span>
<span>Deine Genetik mit deinem Gehirnnebel.</span>
</div>
<div class="story-ai">
<span>Ihre KI vergisst nicht.</span>
<span>Hetzt nicht.</span>
<span>Findet, was übersehen wurde.</span>
<span class="last">Spezialisiert sich nicht — sieht Sie als Ganzes.</span>
<span>Deine KI vergisst nicht.</span>
<span>Eilt nicht.</span>
<span>Findet, was verpasst wurde.</span>
<span class="last">Spezialisiert sich nicht — sieht das komplette du.</span>
</div>
<div class="story-closing"><span class="inou">inou</span> lässt Ihre KI alles berücksichtigen — jede Schicht, jeden Marker, jede Variante — verbindet alles und gibt Ihnen endlich Antworten, die niemand sonst geben konnte.</div>
<div class="story-closing"><span class="inou">inou</span> lässt deine KI alles berücksichtigen — jede Schicht, jeden Marker, jede Variante — verbindet alles und gibt dir endlich Antworten, die sonst niemand geben könnte.</div>
</div>
</div>
<div class="landing-card">
<div class="story">
<h2 class="story-title">Warum wir das gebaut haben</h2>
<p class="story-prose">Sie haben jahrelang Gesundheitsdaten gesammelt. Scans aus dem Krankenhaus. Blutwerte aus dem Labor. Ergebnisse aus dem Patientenportal. Daten von Ihrer Uhr. Vielleicht sogar Ihre DNA.</p>
<p class="story-prose">Und dann gibt es alles, was nur Sie wissen — Ihr Gewicht, Ihr Blutdruck, Ihr Trainingsplan, die Nahrungsergänzungsmittel, die Sie nehmen, die Symptome, die Sie immer vergessen zu erwähnen.</p>
<p class="story-prose">Es ist alles da — aber verstreut über Systeme, die nicht miteinander kommunizieren, bei Spezialisten, die nur ihren Teil sehen, oder in Ihrem eigenen Kopf eingeschlossen.</p>
<p class="story-prose">Ihr Kardiologe weiß nicht, was Ihr Neurologe gefunden hat. Ihr Trainer hat Ihre Blutwerte nicht gesehen. Ihr Arzt hat keine Ahnung, welche Nahrungsergänzungsmittel Sie nehmen. Und keiner von ihnen hat Zeit, sich mit Ihnen hinzusetzen und die Punkte zu verbinden.</p>
<p class="story-prose"><strong>KI kann das endlich.</strong> Sie kann zusammenführen, was kein einzelner Experte sieht — und es Ihnen auch noch erklären.</p>
<p class="story-prose">Aber diese Daten passen nicht in ein Chat-Fenster. Und das Letzte, was Sie wollen, ist Ihre Krankengeschichte auf fremden Servern, die deren Modelle trainiert.</p>
<p class="story-prose"><span class="inou">inou</span> bringt alles zusammen — Labor, Bildgebung, Genetik, Vitalwerte, Medikamente, Nahrungsergänzungsmittel — verschlüsselt, privat und mit niemandem geteilt. Ihre KI verbindet sich sicher. Ihre Daten bleiben Ihre.</p>
<div class="story-closing">Ihre Gesundheit, verstanden.</div>
<p class="story-prose">Du hast Jahre an Gesundheitsdaten gesammelt. Scans aus dem Krankenhaus. Blutuntersuchungen aus dem Labor. Ergebnisse aus dem Portal deines Arztes. Daten von deiner Uhr. Vielleicht sogar dein DNA.</p>
<p class="story-prose">Und dann gibt es alles, was nur du kennst — dein Gewicht, dein Blutdruck, dein Trainingsplan, die Nahrungsergänzungsmittel, die du einnimmst, die Symptome, die du erwähnen wolltest.</p>
<p class="story-prose">Alles ist da — aber verteilt über Systeme, die nicht miteinander kommunizieren, von Spezialisten gehalten, die nur ihr Stück sehen, oder in deinem eigenen Kopf verankert.</p>
<p class="story-prose">Dein Kardiologe weiß nicht, was dein Neurologe gefunden hat. Dein Trainer hat deine Blutuntersuchung nicht gesehen. Dein Arzt hat keine Ahnung, welche Nahrungsergänzungsmittel du einnimmst. Und keiner von ihnen hat Zeit, mit dir zu sitzen und die Punkte zu verbinden.</p>
<p class="story-prose"><strong>KI kann endlich.</strong> Sie kann zusammenziehen, was kein einziger Experte sieht — und es dir tatsächlich erklären.</p>
<p class="story-prose">Aber diese Daten passen nicht in ein Chatfenster. Und das Letzte, was du willst, ist deine medizinische Vorgeschichte auf fremden Servern, die ihre Modelle trainieren.</p>
<p class="story-prose"><span class="inou">inou</span> bringt alles zusammen — Labore, Bildgebung, Genetik, Vitalwerte, Medikamente, Nahrungsergänzungsmittel — verschlüsselt, privat und mit absolut niemandem geteilt. Deine KI verbindet sich sicher. Deine Daten bleiben deine.</p>
<div class="story-closing">Deine Gesundheit, verstanden.</div>
</div>
</div>
<div class="trust-card">
<div class="section-label">{{.T.data_yours}}</div>
<div class="trust-grid">
<div class="trust-item"><strong>{{.T.never_training}}</strong>{{.T.never_training_desc}}</div>
<div class="trust-item"><strong>{{.T.never_shared}}</strong>{{.T.never_shared_desc}}</div>
<div class="trust-item"><strong>{{.T.encrypted}}</strong>{{.T.encrypted_desc}}</div>
<div class="trust-item"><strong>{{.T.delete}}</strong>{{.T.delete_desc}}</div>
<div class="trust-item">
<strong>{{.T.never_training}}</strong>
{{.T.never_training_desc}}
</div>
<div class="trust-item">
<strong>{{.T.never_shared}}</strong>
{{.T.never_shared_desc}}
</div>
<div class="trust-item">
<strong>{{.T.encrypted}}</strong>
{{.T.encrypted_desc}}
</div>
<div class="trust-item">
<strong>{{.T.delete}}</strong>
{{.T.delete_desc}}
</div>
</div>
</div>
<footer class="landing-footer">
<div class="landing-footer-left"><span>© 2025</span><a href="/privacy-policy">Datenschutz</a></div>
<span class="landing-footer-right"><span class="inou">inou</span> <span class="health">health</span></span>
</footer>
{{template "footer"}}
</div>
{{end}}
<script>
(function() {
var track = document.querySelector('.carousel-track');
var dots = document.querySelectorAll('.carousel-dot');
var caption = document.getElementById('carousel-caption');
var captions = [
'Verfolge deine Labortrends im Zeitverlauf \u2014 sieh genau, was deine KI sieht, wenn sie eine Änderung meldet.',
'Deine Labore, Scans und dein Genom an einem Ort \u2014 durchsuche alles, auf das deine KI Zugriff hat.',
'Betrachte deine eigene MRT \u2014 zoome in die gleichen Schichten, die deine KI analysiert hat.',
'Deine Gehirnscans in 3D \u2014 navigiere durch jede Ebene, verifiziere jede Entdeckung, die deine KI gemacht hat.',
'Deine KI verbindet die Punkte über Labore und Genom \u2014 und erklärt es in einfacher Sprache.',
'Deine Röntgenaufnahme, volle Auflösung \u2014 zoome in die Befunde, die deine KI markiert hat.'
];
var count = dots.length;
var current = 0;
function go(i) {
current = i;
track.style.transform = 'translateX(-' + (i * 100) + '%)';
dots.forEach(function(d, j) { d.classList.toggle('active', j === i); });
caption.textContent = captions[i];
}
dots.forEach(function(d) {
d.addEventListener('click', function() { go(+d.dataset.index); });
});
setInterval(function() { go((current + 1) % count); }, 8000);
})();
</script>
{{end}}

View File

@ -1,121 +1,519 @@
{{define "landing_es"}}
<style>
.landing-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 48px; width: 100%; margin-left: auto; margin-right: auto; margin-bottom: 24px; }
.hero-answer { text-align: center; font-size: 1.25rem; font-weight: 400; color: var(--text); line-height: 1.8; margin-top: 16px; margin-bottom: 32px; }
.hero-answer .inou { font-weight: 700; color: var(--accent); }
.hero-tagline { text-align: center; font-size: 1.3rem; font-weight: 600; color: var(--text); margin-bottom: 32px; }
.landing-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 48px;
width: 100%;
margin-left: auto;
margin-right: auto;
margin-bottom: 24px;
}
/* Carousel */
.carousel {
position: relative;
width: 100%;
aspect-ratio: 16/9;
overflow: hidden;
border-radius: 6px;
margin-bottom: 32px;
}
.carousel-track {
display: flex;
height: 100%;
transition: transform 0.5s ease;
}
.carousel-slide {
min-width: 100%;
height: 100%;
background-size: cover;
background-position: center;
}
.carousel-dots {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 32px;
}
.carousel-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--border);
border: none;
padding: 0;
cursor: pointer;
transition: background 0.2s;
}
.carousel-dot.active {
background: var(--accent);
}
/* Hero - Block 1 */
.hero-sources {
font-size: 1.1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.9;
margin-bottom: 32px;
}
.hero-sources span { display: block; }
.hero-sources .different {
}
.hero-pivot {
font-size: 1.15rem;
font-weight: 400;
color: var(--text);
line-height: 1.8;
margin-bottom: 32px;
}
.hero-pivot span { display: block; }
.hero-pivot .emphasis {
font-size: 1.3rem;
font-weight: 600;
margin-top: 12px;
}
.hero-answer {
text-align: center;
font-size: 1.7rem;
font-weight: 500;
color: var(--text);
line-height: 1.5;
margin-top: 16px;
margin-bottom: 8px;
}
.hero-answer .inou {
font-weight: 700;
color: var(--accent);
}
.hero-tagline {
text-align: center;
font-size: 2.8rem;
font-weight: 700;
color: var(--text);
margin-bottom: 12px;
}
.carousel-caption {
text-align: center;
font-size: 0.95rem;
color: var(--muted);
line-height: 1.5;
min-height: 3em;
padding: 0 24px;
margin-bottom: 24px;
}
.hero-cta { margin-bottom: 0; text-align: center; }
.hero-cta .btn { padding: 18px 56px; font-size: 0.9rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; border-radius: 4px; }
.story-prose.warm { font-size: 1.1rem; line-height: 1.8; color: var(--text); }
.story-prose.warm p { margin-bottom: 20px; }
.story-prose.warm .emphasis { font-weight: 600; font-size: 1.15rem; }
.story-title { font-size: 1.25rem; font-weight: 600; color: var(--text); margin-bottom: 32px; }
.story-pair { margin-bottom: 32px; }
.story-pair .data { font-size: 1.1rem; font-weight: 400; color: var(--text); margin-bottom: 4px; }
.story-pair .reality { font-size: 1rem; font-weight: 300; font-style: italic; color: var(--text-muted); }
.story-transition { font-size: 1.25rem; font-weight: 400; color: var(--text); line-height: 1.8; margin: 32px 0; padding: 24px 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.story-gaps { font-size: 1rem; font-weight: 300; color: var(--text-muted); line-height: 1.8; margin-bottom: 32px; }
.hero-cta .btn {
padding: 18px 56px;
font-size: 0.9rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
border-radius: 4px;
}
/* Story - Block 2 */
.story-prose.warm {
font-size: 1.1rem;
line-height: 1.8;
color: var(--text);
}
.story-prose.warm p {
margin-bottom: 20px;
}
.story-prose.warm .emphasis {
font-weight: 600;
font-size: 1.15rem;
}
.story-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text);
margin-bottom: 32px;
}
.story-pair {
margin-bottom: 32px;
}
.story-pair .data {
font-size: 1.1rem;
font-weight: 400;
color: var(--text);
margin-bottom: 4px;
}
.story-pair .reality {
font-size: 1rem;
font-weight: 300;
font-style: italic;
color: var(--text-muted);
}
.story-transition {
font-size: 1.25rem;
font-weight: 400;
color: var(--text);
line-height: 1.8;
margin: 32px 0;
padding: 24px 0;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
.story-gaps {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 32px;
}
.story-gaps span { display: block; }
.story-gaps .indent { font-style: italic; }
.story-connections { font-size: 1rem; font-weight: 300; color: var(--text-muted); line-height: 1.8; margin-bottom: 32px; }
.story-gaps .indent { font-style: italic; }
.story-connections {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 32px;
}
.story-connections span { display: block; }
.story-ai { font-size: 1.25rem; font-weight: 400; color: var(--text); line-height: 1.8; margin-bottom: 32px; }
.story-ai {
font-size: 1.25rem;
font-weight: 400;
color: var(--text);
line-height: 1.8;
margin-bottom: 32px;
}
.story-ai span { display: block; }
.story-ai .last { font-style: italic; }
.story-prose { font-size: 1rem; font-weight: 300; color: var(--text-muted); line-height: 1.8; margin-bottom: 20px; }
.story-ai .last {
font-style: italic;
}
.story-prose {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 20px;
}
.story-prose:last-of-type { margin-bottom: 32px; }
.story-prose strong { font-weight: 600; color: var(--text); }
.story-prose .inou { font-weight: 700; color: var(--accent); }
.story-closing { font-size: 1.25rem; font-weight: 400; color: var(--text); padding-top: 24px; border-top: 1px solid var(--border); }
.story-closing .inou { font-weight: 700; color: var(--accent); }
.trust-card { width: 100%; margin-left: auto; margin-right: auto; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 32px 48px; margin-bottom: 24px; }
.trust-card .section-label { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 24px; }
.trust-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; }
.trust-item { font-size: 0.9rem; font-weight: 300; color: var(--text-muted); line-height: 1.6; }
.trust-item strong { display: block; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.landing-footer { padding: 16px 0; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; }
.landing-footer-left { font-size: 0.9rem; color: var(--text-muted); display: flex; gap: 16px; align-items: center; }
.landing-footer-left a { color: var(--text-muted); text-decoration: none; }
.story-closing {
font-size: 1.25rem;
font-weight: 400;
color: var(--text);
padding-top: 24px;
border-top: 1px solid var(--border);
}
.story-closing .inou {
font-weight: 700;
color: var(--accent);
}
/* Trust section */
.trust-card {
width: 100%;
margin-left: auto;
margin-right: auto;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 32px 48px;
margin-bottom: 24px;
}
.trust-card .section-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
margin-bottom: 24px;
}
.trust-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 32px;
}
.trust-item {
font-size: 0.9rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.6;
}
.trust-item strong {
display: block;
font-weight: 600;
color: var(--text);
margin-bottom: 4px;
}
/* Footer */
.landing-footer {
padding: 16px 0;
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.landing-footer-left {
font-size: 0.9rem;
color: var(--text-muted);
display: flex;
gap: 16px;
align-items: center;
}
.landing-footer-left a {
color: var(--text-muted);
text-decoration: none;
}
.landing-footer-left a:hover { color: var(--accent); }
.landing-footer-right { font-size: 1rem; }
.landing-footer-right .inou { font-weight: 700; color: var(--accent); }
.landing-footer-right .health { font-weight: 400; color: var(--text-muted); }
@media (max-width: 768px) { .trust-card { padding: 24px; } .trust-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; } }
@media (max-width: 480px) { .trust-card { padding: 20px 16px; } .trust-grid { grid-template-columns: 1fr; gap: 20px; } .landing-footer { flex-direction: column; gap: 12px; text-align: center; } .landing-footer-left { flex-direction: column; gap: 8px; } }
.landing-footer-right .inou {
font-weight: 700;
color: var(--accent);
}
.landing-footer-right .health {
font-weight: 400;
color: var(--text-muted);
}
/* Mobile */
@media (max-width: 768px) {
.trust-card {
width: 100%;
margin-left: auto;
margin-right: auto; padding: 24px; }
.hero-sources {
font-size: 1rem; line-height: 1.8; margin-bottom: 32px; }
.hero-pivot { font-size: 1.1rem; margin-bottom: 32px;
}
.hero-pivot .emphasis { font-size: 1.3rem; }
.hero-answer {
text-align: center; font-size: 1.2rem; margin-top: 16px;
margin-bottom: 8px; }
.hero-tagline { font-size: 2rem; margin-bottom: 8px; }
.carousel-caption { font-size: 0.85rem; }
.hero-cta .btn { padding: 14px 40px; }
.story-pair .data { font-size: 1rem; }
.story-pair .reality { font-size: 0.95rem; }
.trust-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; }
}
@media (max-width: 480px) {
.trust-card {
width: 100%;
margin-left: auto;
margin-right: auto; padding: 20px 16px; }
.hero-sources {
font-size: 0.95rem; line-height: 1.9; }
.hero-pivot { font-size: 1rem; }
.hero-pivot .emphasis { font-size: 1.2rem; }
.story-pair { margin-bottom: 24px; }
.trust-grid { grid-template-columns: 1fr; gap: 20px; }
.landing-footer { flex-direction: column; gap: 12px; text-align: center; }
.landing-footer-left { flex-direction: column; gap: 8px; }
}
</style>
<div class="sg-container">
<div class="landing-card">
<div class="hero">
<div class="hero-answer"><span class="inou">inou</span> organiza y comparte tu expediente de salud con tu IA — de forma segura y privada.</div>
<div class="hero-tagline">Tu salud, comprendida.</div>
<div class="hero-cta">{{if .Dossier}}<a href="/invite" class="btn btn-primary">Invitar a un amigo</a>{{else}}<a href="/start" class="btn btn-primary">Iniciar sesión</a>{{end}}{{if .Error}}<div class="error" style="margin-top: 24px;">{{.Error}}</div>{{end}}</div>
<div class="hero-tagline">Tu salud, entendida.</div>
<div class="hero-answer">Todos tus datos de salud — organizados, privados y listos para tu IA.</div>
<div class="carousel">
<div class="carousel-track">
<div class="carousel-slide" style="background-image: url('/static/carousel-1.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-2.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-3.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-4.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-5.webp')"></div>
<div class="carousel-slide" style="background-image: url('/static/carousel-6.webp')"></div>
</div>
</div>
<div class="carousel-caption" id="carousel-caption">Haz seguimiento de tus tendencias de laboratorio con el tiempo — ve exactamente lo que tu IA ve cuando marca un cambio.</div>
<div class="carousel-dots">
<button class="carousel-dot active" data-index="0"></button>
<button class="carousel-dot" data-index="1"></button>
<button class="carousel-dot" data-index="2"></button>
<button class="carousel-dot" data-index="3"></button>
<button class="carousel-dot" data-index="4"></button>
<button class="carousel-dot" data-index="5"></button>
</div>
<div class="hero-cta">
{{if .Dossier}}<a href="/invite" class="btn btn-primary">Invita a un amigo</a>{{else}}<a href="/start" class="btn btn-primary">Iniciar sesión</a>{{end}}
{{if .Error}}<div class="error" style="margin-top: 24px;">{{.Error}}</div>{{end}}
</div>
</div>
</div>
<div class="landing-card">
<div class="story">
<h2 class="story-title">Necesitas IA para tu salud</h2>
<div class="story-prose warm">
<p>Tus datos de salud están dispersos en docenas de lugares — con tu cardiólogo, tu neurólogo, el laboratorio, tu reloj inteligente, tus apps, tu 23andMe. Y solo tú conoces el resto: qué comes, qué bebes, qué suplementos tomas. Tu rutina de ejercicio. Tus síntomas. Tus objetivos — ya sea que estés intentando quedar embarazada, entrenando para un maratón, o simplemente tratando de sentirte menos cansado.</p>
<p>Ya sea que estés sano y quieras seguir así, navegando un diagnóstico difícil, o cuidando a un familiar que no puede defenderse solo — ningún médico ve el panorama completo. Ningún sistema lo conecta.</p>
<p>Pero tú tienes acceso a todo. Solo te falta la experiencia para entenderlo todo.</p>
<p class="emphasis">Tu IA la tiene. inou le da el panorama completo.</p>
<p>Tus datos de salud viven en una docena de lugares diferentes — con tu cardiólogo, tu neurólogo, tu laboratorio, tu reloj, tus aplicaciones, tu 23andMe. Y solo tú conoces el resto: lo que comes, lo que bebes, los suplementos que tomas. Tu rutina de ejercicio. Tus síntomas. Tus objetivos — ya sea que estés intentando quedar embarazada, entrenando para un maratón, o simplemente tratando de sentirte menos agotado.</p>
<p>Ya sea que estés sano y quieras mantenerte así, navegando un diagnóstico difícil, o cuidando a un familiar que no puede defenderse — ningún médico ve la imagen completa. Ningún sistema lo conecta.</p>
<p>Pero <strong><em>tú</em></strong> tienes acceso a todo ello. Simplemente no tienes la experiencia para darle sentido.</p>
<p class="emphasis">Tu IA sí la tiene. inou le da la imagen completa.</p>
</div>
</div>
</div>
<div class="landing-card">
<div class="story">
<h2 class="story-title">El desafío</h2>
<div class="story-pair"><div class="data">Tu resonancia tiene 4.000 cortes.</div><div class="reality">Se leyó en 10 minutos.</div></div>
<div class="story-pair"><div class="data">Tu genoma tiene millones de variantes.</div><div class="reality">Solo aprendiste el color de tus ojos y de dónde vienen tus ancestros.</div></div>
<div class="story-pair"><div class="data">Tu análisis de sangre tiene docenas de marcadores.</div><div class="reality">Tu médico dijo "todo se ve bien."</div></div>
<div class="story-pair"><div class="data">Tu reloj registró 10.000 horas de sueño.</div><div class="reality">Tu entrenador no sabe que existe.</div></div>
<div class="story-pair"><div class="data">Has probado cien suplementos diferentes.</div><div class="reality">Nadie preguntó cuáles.</div></div>
<div class="story-transition">Las conexiones están ahí.<br>Son demasiado complejas para una sola persona.</div>
<div class="story-pair">
<div class="data">Tu resonancia magnética tiene 4,000 cortes.</div>
<div class="reality">Fue leída en 10 minutos.</div>
</div>
<div class="story-pair">
<div class="data">Tu genoma tiene millones de variantes.</div>
<div class="reality">Lo único que aprendiste fue el color de tus ojos y de dónde vienen tus antepasados.</div>
</div>
<div class="story-pair">
<div class="data">Tu análisis de sangre tiene docenas de marcadores.</div>
<div class="reality">Tu médico dijo "todo está bien".</div>
</div>
<div class="story-pair">
<div class="data">Tu reloj rastreó 10,000 horas de sueño.</div>
<div class="reality">Tu entrenador no sabe que existe.</div>
</div>
<div class="story-pair">
<div class="data">Has intentado cientos de suplementos diferentes.</div>
<div class="reality">Nadie preguntó cuáles.</div>
</div>
<div class="story-transition">
Las conexiones están ahí.<br>
Simplemente son demasiado complejas para que una sola persona las comprenda.
</div>
<div class="story-gaps">
<span>Nadie sabe cómo tu cuerpo procesa la Warfarina — ni siquiera tú.</span>
<span class="indent">Pero la respuesta podría estar escondida en tu 23andMe.</span>
<span>Ese "sin hallazgos" en tu resonancia — ¿alguien miró cuidadosamente los 4.000 cortes?</span>
<span>Tu tiroides está "dentro del rango" — pero nadie lo conectó con tu fatiga, tu peso, que siempre tienes frío.</span>
<span class="indent">Pero la respuesta ya podría estar oculta en tu 23andMe.</span>
<span>Ese 'sin hallazgos significativos' en tu resonancia magnética — ¿alguien miró de cerca los 4,000 cortes?</span>
<span>Tu tiroides está 'dentro del rango' — pero nadie la conectó con tu fatiga, tu peso, siempre tener frío.</span>
</div>
<div class="story-connections">
<span>Nadie conecta tu café de la tarde con tu calidad de sueño.</span>
<span>Tus niveles de hierro con tu fatiga en el entrenamiento.</span>
<span>Nadie está conectando tu cafeína de la tarde con tus puntuaciones de sueño.</span>
<span>Tus niveles de hierro con tu fatiga durante el ejercicio.</span>
<span>Tu genética con tu niebla mental.</span>
</div>
<div class="story-ai">
<span>Tu IA no olvida.</span>
<span>No se apresura.</span>
<span>Encuentra lo que se pasó por alto.</span>
<span>No tiene prisa.</span>
<span>Encuentra lo que se perdió.</span>
<span class="last">No se especializa — te ve completo.</span>
</div>
<div class="story-closing"><span class="inou">inou</span> permite que tu IA tome todo en cuenta — cada corte, cada marcador, cada variante — conecta todo y finalmente te da respuestas que nadie más podía dar.</div>
<div class="story-closing"><span class="inou">inou</span> permite que tu IA lo tenga todo en cuenta — cada corte, cada marcador, cada variante — conecta todo y finalmente te da respuestas que nadie más podría darte.</div>
</div>
</div>
<div class="landing-card">
<div class="story">
<h2 class="story-title">Por qué construimos esto</h2>
<p class="story-prose">Has recopilado años de datos de salud. Estudios del hospital. Análisis del laboratorio. Resultados del portal del médico. Datos de tu reloj. Quizás incluso tu ADN.</p>
<p class="story-prose">Y luego está todo lo que solo tú sabes — tu peso, tu presión arterial, tu programa de entrenamiento, los suplementos que tomas, los síntomas que siempre olvidas mencionar.</p>
<p class="story-prose">Todo está ahí — pero disperso en sistemas que no se comunican, con especialistas que solo ven su parte, o encerrado en tu propia cabeza.</p>
<p class="story-prose">Tu cardiólogo no sabe lo que encontró tu neurólogo. Tu entrenador no ha visto tus análisis de sangre. Tu médico no tiene idea de qué suplementos tomas. Y ninguno de ellos tiene tiempo para sentarse contigo y conectar los puntos.</p>
<p class="story-prose"><strong>La IA finalmente puede.</strong> Puede unir lo que ningún experto solo ve — y además explicártelo.</p>
<p class="story-prose">Pero estos datos no caben en una ventana de chat. Y lo último que quieres es tu historial médico en los servidores de alguien más, entrenando sus modelos.</p>
<p class="story-prose"><span class="inou">inou</span> lo une todo — laboratorio, imágenes, genética, signos vitales, medicamentos, suplementos — encriptado, privado, y sin compartir con absolutamente nadie. Tu IA se conecta de forma segura. Tus datos siguen siendo tuyos.</p>
<div class="story-closing">Tu salud, comprendida.</div>
<p class="story-prose">Has recopilado años de datos de salud. Exploraciones del hospital. Análisis de sangre del laboratorio. Resultados del portal de tu médico. Datos de tu reloj. Quizás incluso tu ADN.</p>
<p class="story-prose">Y luego está todo lo que solo tú conoces — tu peso, tu presión arterial, tu calendario de entrenamiento, los suplementos que tomas, los síntomas que has estado pensando mencionar.</p>
<p class="story-prose">Todo está ahí — pero disperso en sistemas que no se hablan, en manos de especialistas que solo ven su parte, o bloqueado en tu propia cabeza.</p>
<p class="story-prose">Tu cardiólogo no sabe lo que encontró tu neurólogo. Tu entrenador no ha visto tus análisis de sangre. Tu médico no tiene idea de qué suplementos estás tomando. Y ninguno de ellos tiene tiempo para sentarse contigo y conectar los puntos.</p>
<p class="story-prose"><strong>La IA finalmente puede.</strong> Puede sintetizar lo que ningún experto individual ve — y realmente explicártelo.</p>
<p class="story-prose">Pero estos datos no caben en una ventana de chat. Y lo último que quieres es tu historial médico en los servidores de otra persona, entrenando sus modelos.</p>
<p class="story-prose"><span class="inou">inou</span> une todo — laboratorios, imágenes, genética, signos vitales, medicamentos, suplementos — encriptado, privado y compartido con absolutamente nadie. Tu IA se conecta de forma segura. Tus datos siguen siendo tuyos.</p>
<div class="story-closing">Tu salud, entendida.</div>
</div>
</div>
<div class="trust-card">
<div class="section-label">{{.T.data_yours}}</div>
<div class="trust-grid">
<div class="trust-item"><strong>{{.T.never_training}}</strong>{{.T.never_training_desc}}</div>
<div class="trust-item"><strong>{{.T.never_shared}}</strong>{{.T.never_shared_desc}}</div>
<div class="trust-item"><strong>{{.T.encrypted}}</strong>{{.T.encrypted_desc}}</div>
<div class="trust-item"><strong>{{.T.delete}}</strong>{{.T.delete_desc}}</div>
<div class="trust-item">
<strong>{{.T.never_training}}</strong>
{{.T.never_training_desc}}
</div>
<div class="trust-item">
<strong>{{.T.never_shared}}</strong>
{{.T.never_shared_desc}}
</div>
<div class="trust-item">
<strong>{{.T.encrypted}}</strong>
{{.T.encrypted_desc}}
</div>
<div class="trust-item">
<strong>{{.T.delete}}</strong>
{{.T.delete_desc}}
</div>
</div>
</div>
<footer class="landing-footer">
<div class="landing-footer-left"><span>© 2025</span><a href="/privacy-policy">Privacidad</a></div>
<span class="landing-footer-right"><span class="inou">inou</span> <span class="health">health</span></span>
</footer>
{{template "footer"}}
</div>
{{end}}
<script>
(function() {
var track = document.querySelector('.carousel-track');
var dots = document.querySelectorAll('.carousel-dot');
var caption = document.getElementById('carousel-caption');
var captions = [
'Haz seguimiento de tus tendencias de laboratorio con el tiempo — ve exactamente lo que tu IA ve cuando marca un cambio.',
'Tus laboratorios, exploraciones y genoma en un solo lugar — explora todo a lo que tu IA tiene acceso.',
'Ve tu propia resonancia magnética — amplía los mismos cortes que analizó tu IA.',
'Tu exploración cerebral en 3D — navega cada plano, verifica cada hallazgo que hizo tu IA.',
'Tu IA conecta los puntos entre laboratorios y genoma — y lo explica en lenguaje sencillo.',
'Tu radiografía, resolución completa — amplía los hallazgos que marcó tu IA.'
];
var count = dots.length;
var current = 0;
function go(i) {
current = i;
track.style.transform = 'translateX(-' + (i * 100) + '%)';
dots.forEach(function(d, j) { d.classList.toggle('active', j === i); });
caption.textContent = captions[i];
}
dots.forEach(function(d) {
d.addEventListener('click', function() { go(+d.dataset.index); });
});
setInterval(function() { go((current + 1) % count); }, 8000);
})();
</script>
{{end}}

View File

@ -0,0 +1,291 @@
{{define "pricing_da"}}
<style>
.pricing-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 24px 80px;
}
.pricing-header {
text-align: center;
margin-bottom: 48px;
}
.pricing-header h1 {
font-size: 2.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.pricing-header .tagline {
font-size: 1.15rem;
font-weight: 300;
color: var(--text-muted);
}
.pricing-table-wrapper {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
margin-bottom: 24px;
}
.pricing-table {
width: 100%;
border-collapse: collapse;
}
.pricing-table thead {
background: var(--bg-secondary);
}
.pricing-table th {
padding: 24px 16px;
text-align: left;
font-weight: 600;
color: var(--text);
border-bottom: 2px solid var(--border);
}
.pricing-table th:first-child {
width: 40%;
}
.tier-header {
text-align: center !important;
}
.tier-name {
font-size: 1.25rem;
margin-bottom: 8px;
}
.tier-price {
font-size: 1.75rem;
font-weight: 700;
color: var(--accent);
}
.tier-price .free {
color: #28a745;
}
.tier-price .small {
font-size: 1rem;
font-weight: 400;
}
.tier-price s {
color: var(--text-muted);
font-weight: 400;
}
.tier-free {
font-size: 0.85rem;
font-weight: 600;
color: #28a745;
margin-top: 4px;
}
.pricing-table tbody tr {
border-bottom: 1px solid var(--border);
}
.pricing-table tbody tr:last-child {
border-bottom: none;
}
.pricing-table td {
padding: 16px;
vertical-align: middle;
}
.feature-name {
font-weight: 500;
color: var(--text);
}
.feature-cell {
text-align: center;
font-size: 1.25rem;
}
.check {
color: #28a745;
}
.cross {
color: #dc3545;
}
.category-row {
background: var(--bg-secondary);
font-weight: 700;
color: var(--text);
}
.category-row td {
padding: 16px 16px;
font-size: 1.1rem;
border-left: 3px solid var(--accent);
letter-spacing: 0.02em;
}
/* Mobile */
@media (max-width: 768px) {
.pricing-container { padding: 24px 16px 48px; }
.pricing-header h1 { font-size: 2rem; }
.pricing-table th { padding: 16px 12px; }
.pricing-table td { padding: 12px; }
.tier-name { font-size: 1rem; }
.tier-price { font-size: 1.5rem; }
.feature-name { font-size: 0.9rem; }
}
@media (max-width: 480px) {
.pricing-container { padding: 16px 12px 32px; }
.pricing-header h1 { font-size: 1.75rem; }
.pricing-table th:first-child { width: 35%; }
.tier-name { font-size: 0.9rem; }
.tier-price { font-size: 1.25rem; }
}
</style>
<div class="pricing-container">
<div class="pricing-header">
<h1>Priser</h1>
<p class="tagline">Alle niveauer er gratis indtil 1. juli 2026. Intet kreditkort kræves.</p>
</div>
<div class="pricing-table-wrapper">
<table class="pricing-table">
<thead>
<tr>
<th></th>
<th class="tier-header">
<div class="tier-name">Overvågning</div>
<div class="tier-price"><span class="free">Gratis</span></div>
</th>
<th class="tier-header">
<div class="tier-name">Optimering</div>
<div class="tier-price"><s>$12<span class="small">/md</span></s></div>
<div class="tier-free">gratis indtil 1/7/26</div>
</th>
<th class="tier-header">
<div class="tier-name">Forskning</div>
<div class="tier-price"><s>$35<span class="small">/md</span></s></div>
<div class="tier-free">gratis til 1/7/26</div>
</th>
</tr>
</thead>
<tbody>
<tr class="category-row">
<td colspan="4">Sundhedsdata</td>
</tr>
<tr>
<td class="feature-name">Vitalparametre (blodtryk, puls, vægt, temp)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Symptomer og tilstande</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Medicin</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Motion og aktivitet</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Familiehistorik</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Laboratorieresultater</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Forbrugergenom (23andMe)</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Medicinsk billeddannelse (MR, CT, røntgen)</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Klinisk genomsekventering</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">AI-funktioner</td>
</tr>
<tr>
<td class="feature-name">MCP-integration (Claude, ChatGPT)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Personaliserede AI-svar</td>
<td class="feature-cell">Begrænset</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Sundhedstendensanalyse</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">Opbevaring og adgang</td>
</tr>
<tr>
<td class="feature-name">Understøttelse af flere mapper (familie)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">FIPS 140-3 kryptering</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Dataeksport</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
</tbody>
</table>
</div>
{{template "footer"}}
</div>
{{end}}

View File

@ -0,0 +1,291 @@
{{define "pricing_de"}}
<style>
.pricing-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 24px 80px;
}
.pricing-header {
text-align: center;
margin-bottom: 48px;
}
.pricing-header h1 {
font-size: 2.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.pricing-header .tagline {
font-size: 1.15rem;
font-weight: 300;
color: var(--text-muted);
}
.pricing-table-wrapper {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
margin-bottom: 24px;
}
.pricing-table {
width: 100%;
border-collapse: collapse;
}
.pricing-table thead {
background: var(--bg-secondary);
}
.pricing-table th {
padding: 24px 16px;
text-align: left;
font-weight: 600;
color: var(--text);
border-bottom: 2px solid var(--border);
}
.pricing-table th:first-child {
width: 40%;
}
.tier-header {
text-align: center !important;
}
.tier-name {
font-size: 1.25rem;
margin-bottom: 8px;
}
.tier-price {
font-size: 1.75rem;
font-weight: 700;
color: var(--accent);
}
.tier-price .free {
color: #28a745;
}
.tier-price .small {
font-size: 1rem;
font-weight: 400;
}
.tier-price s {
color: var(--text-muted);
font-weight: 400;
}
.tier-free {
font-size: 0.85rem;
font-weight: 600;
color: #28a745;
margin-top: 4px;
}
.pricing-table tbody tr {
border-bottom: 1px solid var(--border);
}
.pricing-table tbody tr:last-child {
border-bottom: none;
}
.pricing-table td {
padding: 16px;
vertical-align: middle;
}
.feature-name {
font-weight: 500;
color: var(--text);
}
.feature-cell {
text-align: center;
font-size: 1.25rem;
}
.check {
color: #28a745;
}
.cross {
color: #dc3545;
}
.category-row {
background: var(--bg-secondary);
font-weight: 700;
color: var(--text);
}
.category-row td {
padding: 16px 16px;
font-size: 1.1rem;
border-left: 3px solid var(--accent);
letter-spacing: 0.02em;
}
/* Mobile */
@media (max-width: 768px) {
.pricing-container { padding: 24px 16px 48px; }
.pricing-header h1 { font-size: 2rem; }
.pricing-table th { padding: 16px 12px; }
.pricing-table td { padding: 12px; }
.tier-name { font-size: 1rem; }
.tier-price { font-size: 1.5rem; }
.feature-name { font-size: 0.9rem; }
}
@media (max-width: 480px) {
.pricing-container { padding: 16px 12px 32px; }
.pricing-header h1 { font-size: 1.75rem; }
.pricing-table th:first-child { width: 35%; }
.tier-name { font-size: 0.9rem; }
.tier-price { font-size: 1.25rem; }
}
</style>
<div class="pricing-container">
<div class="pricing-header">
<h1>Preise</h1>
<p class="tagline">Alle Stufen sind bis zum 1. Juli 2026 kostenlos. Keine Kreditkarte erforderlich.</p>
</div>
<div class="pricing-table-wrapper">
<table class="pricing-table">
<thead>
<tr>
<th></th>
<th class="tier-header">
<div class="tier-name">Monitor</div>
<div class="tier-price"><span class="free">Kostenlos</span></div>
</th>
<th class="tier-header">
<div class="tier-name">Optimize</div>
<div class="tier-price"><s>$12<span class="small">/Monat</span></s></div>
<div class="tier-free">kostenlos bis 1. Juli 2026</div>
</th>
<th class="tier-header">
<div class="tier-name">Research</div>
<div class="tier-price"><s>$35<span class="small">/Monat</span></s></div>
<div class="tier-free">kostenlos bis 1. Juli 2026</div>
</th>
</tr>
</thead>
<tbody>
<tr class="category-row">
<td colspan="4">Gesundheitsdaten</td>
</tr>
<tr>
<td class="feature-name">Vitalparameter (BP, HR, Gewicht, Temperatur)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Symptome & Zustände</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Medikamente</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Bewegung & Aktivität</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Familienanamnese</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Laborergebnisse</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Konsumenten-Genom (23andMe)</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Medizinische Bildgebung (MRI, CT, Röntgen)</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Klinische Genomsequenzierung</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">KI-Funktionen</td>
</tr>
<tr>
<td class="feature-name">MCP-Integration (Claude, ChatGPT)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Personalisierte KI-Antworten</td>
<td class="feature-cell">Begrenzt</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Gesundheitstrend-Analyse</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">Speicherung & Zugriff</td>
</tr>
<tr>
<td class="feature-name">Mehrfach-Dossier-Unterstützung (Familie)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">FIPS 140-3 Verschlüsselung</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Datenexport</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
</tbody>
</table>
</div>
{{template "footer"}}
</div>
{{end}}

View File

@ -0,0 +1,291 @@
{{define "pricing_es"}}
<style>
.pricing-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 24px 80px;
}
.pricing-header {
text-align: center;
margin-bottom: 48px;
}
.pricing-header h1 {
font-size: 2.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.pricing-header .tagline {
font-size: 1.15rem;
font-weight: 300;
color: var(--text-muted);
}
.pricing-table-wrapper {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
margin-bottom: 24px;
}
.pricing-table {
width: 100%;
border-collapse: collapse;
}
.pricing-table thead {
background: var(--bg-secondary);
}
.pricing-table th {
padding: 24px 16px;
text-align: left;
font-weight: 600;
color: var(--text);
border-bottom: 2px solid var(--border);
}
.pricing-table th:first-child {
width: 40%;
}
.tier-header {
text-align: center !important;
}
.tier-name {
font-size: 1.25rem;
margin-bottom: 8px;
}
.tier-price {
font-size: 1.75rem;
font-weight: 700;
color: var(--accent);
}
.tier-price .free {
color: #28a745;
}
.tier-price .small {
font-size: 1rem;
font-weight: 400;
}
.tier-price s {
color: var(--text-muted);
font-weight: 400;
}
.tier-free {
font-size: 0.85rem;
font-weight: 600;
color: #28a745;
margin-top: 4px;
}
.pricing-table tbody tr {
border-bottom: 1px solid var(--border);
}
.pricing-table tbody tr:last-child {
border-bottom: none;
}
.pricing-table td {
padding: 16px;
vertical-align: middle;
}
.feature-name {
font-weight: 500;
color: var(--text);
}
.feature-cell {
text-align: center;
font-size: 1.25rem;
}
.check {
color: #28a745;
}
.cross {
color: #dc3545;
}
.category-row {
background: var(--bg-secondary);
font-weight: 700;
color: var(--text);
}
.category-row td {
padding: 16px 16px;
font-size: 1.1rem;
border-left: 3px solid var(--accent);
letter-spacing: 0.02em;
}
/* Mobile */
@media (max-width: 768px) {
.pricing-container { padding: 24px 16px 48px; }
.pricing-header h1 { font-size: 2rem; }
.pricing-table th { padding: 16px 12px; }
.pricing-table td { padding: 12px; }
.tier-name { font-size: 1rem; }
.tier-price { font-size: 1.5rem; }
.feature-name { font-size: 0.9rem; }
}
@media (max-width: 480px) {
.pricing-container { padding: 16px 12px 32px; }
.pricing-header h1 { font-size: 1.75rem; }
.pricing-table th:first-child { width: 35%; }
.tier-name { font-size: 0.9rem; }
.tier-price { font-size: 1.25rem; }
}
</style>
<div class="pricing-container">
<div class="pricing-header">
<h1>Precios</h1>
<p class="tagline">Todos los niveles son gratuitos hasta el 1 de julio de 2026. No se requiere tarjeta de crédito.</p>
</div>
<div class="pricing-table-wrapper">
<table class="pricing-table">
<thead>
<tr>
<th></th>
<th class="tier-header">
<div class="tier-name">Monitor</div>
<div class="tier-price"><span class="free">Gratis</span></div>
</th>
<th class="tier-header">
<div class="tier-name">Optimizar</div>
<div class="tier-price"><s>$12<span class="small">/mes</span></s></div>
<div class="tier-free">gratis hasta 1/7/26</div>
</th>
<th class="tier-header">
<div class="tier-name">Investigación</div>
<div class="tier-price"><s>$35<span class="small">/mes</span></s></div>
<div class="tier-free">gratis hasta 1/7/26</div>
</th>
</tr>
</thead>
<tbody>
<tr class="category-row">
<td colspan="4">Datos de salud</td>
</tr>
<tr>
<td class="feature-name">Signos vitales (PA, FC, peso, temp)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Síntomas y condiciones</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Medicamentos</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Ejercicio y actividad</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Historial familiar</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Resultados de laboratorio</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Genoma del consumidor (23andMe)</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Imágenes médicas (IRM, TC, rayos X)</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Secuenciación genómica clínica</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">Funciones de IA</td>
</tr>
<tr>
<td class="feature-name">Integración MCP (Claude, ChatGPT)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Respuestas de IA personalizadas</td>
<td class="feature-cell">Limitado</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Análisis de tendencias de salud</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">Almacenamiento y acceso</td>
</tr>
<tr>
<td class="feature-name">Soporte multi-expediente (familia)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Cifrado FIPS 140-3</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">Exportación de datos</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
</tbody>
</table>
</div>
{{template "footer"}}
</div>
{{end}}

View File

@ -0,0 +1,291 @@
{{define "pricing_ja"}}
<style>
.pricing-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 24px 80px;
}
.pricing-header {
text-align: center;
margin-bottom: 48px;
}
.pricing-header h1 {
font-size: 2.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.pricing-header .tagline {
font-size: 1.15rem;
font-weight: 300;
color: var(--text-muted);
}
.pricing-table-wrapper {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
margin-bottom: 24px;
}
.pricing-table {
width: 100%;
border-collapse: collapse;
}
.pricing-table thead {
background: var(--bg-secondary);
}
.pricing-table th {
padding: 24px 16px;
text-align: left;
font-weight: 600;
color: var(--text);
border-bottom: 2px solid var(--border);
}
.pricing-table th:first-child {
width: 40%;
}
.tier-header {
text-align: center !important;
}
.tier-name {
font-size: 1.25rem;
margin-bottom: 8px;
}
.tier-price {
font-size: 1.75rem;
font-weight: 700;
color: var(--accent);
}
.tier-price .free {
color: #28a745;
}
.tier-price .small {
font-size: 1rem;
font-weight: 400;
}
.tier-price s {
color: var(--text-muted);
font-weight: 400;
}
.tier-free {
font-size: 0.85rem;
font-weight: 600;
color: #28a745;
margin-top: 4px;
}
.pricing-table tbody tr {
border-bottom: 1px solid var(--border);
}
.pricing-table tbody tr:last-child {
border-bottom: none;
}
.pricing-table td {
padding: 16px;
vertical-align: middle;
}
.feature-name {
font-weight: 500;
color: var(--text);
}
.feature-cell {
text-align: center;
font-size: 1.25rem;
}
.check {
color: #28a745;
}
.cross {
color: #dc3545;
}
.category-row {
background: var(--bg-secondary);
font-weight: 700;
color: var(--text);
}
.category-row td {
padding: 16px 16px;
font-size: 1.1rem;
border-left: 3px solid var(--accent);
letter-spacing: 0.02em;
}
/* Mobile */
@media (max-width: 768px) {
.pricing-container { padding: 24px 16px 48px; }
.pricing-header h1 { font-size: 2rem; }
.pricing-table th { padding: 16px 12px; }
.pricing-table td { padding: 12px; }
.tier-name { font-size: 1rem; }
.tier-price { font-size: 1.5rem; }
.feature-name { font-size: 0.9rem; }
}
@media (max-width: 480px) {
.pricing-container { padding: 16px 12px 32px; }
.pricing-header h1 { font-size: 1.75rem; }
.pricing-table th:first-child { width: 35%; }
.tier-name { font-size: 0.9rem; }
.tier-price { font-size: 1.25rem; }
}
</style>
<div class="pricing-container">
<div class="pricing-header">
<h1>料金プラン</h1>
<p class="tagline">2026年7月1日まで全プラン無料です。クレジットカードは不要です。</p>
</div>
<div class="pricing-table-wrapper">
<table class="pricing-table">
<thead>
<tr>
<th></th>
<th class="tier-header">
<div class="tier-name">モニター</div>
<div class="tier-price"><span class="free">無料</span></div>
</th>
<th class="tier-header">
<div class="tier-name">最適化</div>
<div class="tier-price"><s>$12<span class="small">/月</span></s></div>
<div class="tier-free">26/7/1まで無料</div>
</th>
<th class="tier-header">
<div class="tier-name">リサーチ</div>
<div class="tier-price"><s>$35<span class="small">/月</span></s></div>
<div class="tier-free">26/7/1まで無料</div>
</th>
</tr>
</thead>
<tbody>
<tr class="category-row">
<td colspan="4">健康データ</td>
</tr>
<tr>
<td class="feature-name">バイタルサイン(血圧、心拍、体重、体温)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">症状と病状</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">薬</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">運動とアクティビティ</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">家族歴</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">検査結果</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">コンシューマーゲ23andMe</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">医用画像MRI、CT、X線</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">臨床ゲノムシーケンス</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">AI機能</td>
</tr>
<tr>
<td class="feature-name">MCP連携Claude、ChatGPT</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">パーソナライズされたAI回答</td>
<td class="feature-cell">制限あり</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">健康トレンド分析</td>
<td class="feature-cell cross">✗</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr class="category-row">
<td colspan="4">ストレージとアクセス</td>
</tr>
<tr>
<td class="feature-name">マルチドシエサポート(家族)</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">FIPS 140-3 暗号化</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
<tr>
<td class="feature-name">データエクスポート</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
<td class="feature-cell check">✓</td>
</tr>
</tbody>
</table>
</div>
{{template "footer"}}
</div>
{{end}}

View File

@ -0,0 +1,196 @@
{{define "terms_de"}}
<style>
.terms-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 24px 80px;
}
.terms-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 48px;
margin-bottom: 24px;
}
.terms-card h1 {
font-size: 2.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.terms-card .intro {
font-size: 1.15rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 0;
}
.terms-card h2 {
font-size: 1.4rem;
font-weight: 600;
color: var(--text);
margin-top: 0;
margin-bottom: 24px;
}
.terms-card h3 {
font-size: 1.1rem;
font-weight: 600;
color: var(--text);
margin-top: 24px;
margin-bottom: 8px;
}
.terms-card h3:first-child { margin-top: 0; }
.terms-card p {
font-size: 1rem;
font-weight: 300;
color: var(--text-muted);
line-height: 1.8;
margin-bottom: 16px;
}
.terms-card p:last-child { margin-bottom: 0; }
.terms-card strong {
font-weight: 600;
color: var(--text);
}
.terms-card a {
color: var(--accent);
}
.inou-brand {
font-weight: 700;
color: var(--accent);
}
/* Mobile */
@media (max-width: 768px) {
.terms-container { padding: 24px 16px 48px; }
.terms-card { padding: 32px 24px; }
.terms-card h1 { font-size: 2rem; }
.terms-card .intro { font-size: 1.05rem; }
.terms-card h2 { font-size: 1.25rem; }
.terms-card h3 { font-size: 1rem; }
.terms-card p { font-size: 0.95rem; }
}
@media (max-width: 480px) {
.terms-container { padding: 16px 12px 32px; }
.terms-card { padding: 24px 16px; }
.terms-card h1 { font-size: 1.75rem; }
.terms-card .intro { font-size: 1rem; }
.terms-card h2 { font-size: 1.15rem; }
.terms-card p { font-size: 0.9rem; }
}
</style>
<div class="terms-container">
<div class="terms-card">
<h1>Nutzungsbedingungen</h1>
<p class="intro">Diese Bedingungen regeln deine Nutzung von <span class="inou-brand">inou</span>. Durch das Erstellen eines Kontos stimmst du ihnen zu. Wenn du nicht zustimmst, nutze den Service nicht.</p>
</div>
<div class="terms-card">
<h2>Der Service</h2>
<h3>Was inou ist.</h3>
<p><span class="inou-brand">inou</span> ist eine persönliche Gesundheitsdatenplattform. Du lädst deine medizinischen Dateien hoch Bildgebung, Laborergebnisse, genetische Daten und wir speichern sie sicher, damit du sie ansehen und verwalten kannst. Du kannst Drittanbieter-Tools, wie z.B. KIAssistenten, verbinden, um auf deine Daten zuzugreifen.</p>
<h3>Was inou nicht ist.</h3>
<p><span class="inou-brand">inou</span> ist kein medizinisches Gerät. Es ist nicht für klinische Diagnosen, Behandlungen, Heilungen oder Prävention von Krankheiten oder medizinischen Zuständen bestimmt. <span class="inou-brand">inou</span> gibt keine medizinischen Ratschläge. Die Plattform zeigt deine Daten sie interpretiert sie nicht, empfiehlt keine Maßnahmen oder ersetzt einen qualifizierten Gesundheitsfachmann. Konsultiere immer deinen Arzt für medizinische Entscheidungen.</p>
</div>
<div class="terms-card">
<h2>Dein Konto</h2>
<h3>Kontobestimmungen.</h3>
<p>Du musst mindestens 18 Jahre alt sein, um ein Konto zu erstellen, es sei denn, ein Elternteil oder Erziehungsberechtigter erstellt und verwaltet das Konto in deinem Namen. Du bist dafür verantwortlich, deine Zugangsdaten sicher aufzubewahren. Ein Konto pro Person Konten sind nicht übertragbar.</p>
<h3>Deine Daten, deine Verantwortung.</h3>
<p>Du bist für die Genauigkeit und Rechtsmäßigkeit der hochgeladenen Daten verantwortlich. Du musst das Recht haben, die hochgeladenen Dateien zu speichern und zu verarbeiten. Lade keine Daten anderer hoch, ohne deren ausdrückliche Zustimmung.</p>
</div>
<div class="terms-card">
<h2>Unsere Verantwortlichkeiten</h2>
<h3>Was wir bereitstellen.</h3>
<p>Wir speichern deine Daten sicher unter Verwendung von FIPS1403 validierter Verschlüsselung, stellen sie dir über die Plattform zur Verfügung und übertragen sie an Drittanbieter-Dienste, die du ausdrücklich autorisierst. Weitere Informationen zum Schutz deiner Daten findest du auf unserer <a href="/security">Sicherheitsseite</a>. Wir informieren dich über wesentliche Änderungen dieser Bedingungen oder unserer Datenschutzpraktiken.</p>
<h3>Was wir nicht garantieren.</h3>
<p>Wir streben nach kontinuierlicher Verfügbarkeit, können sie jedoch nicht garantieren. Der Service kann vorübergehend wegen Wartung, Updates oder unvorhersehbarer Umstände nicht verfügbar sein. Wir haften nicht für Entscheidungen, die du oder andere auf Basis der über die Plattform angesehenen Daten treffen.</p>
</div>
<div class="terms-card">
<h2>Datenschutz und Datenverarbeitung</h2>
<p>Deine Nutzung von <span class="inou-brand">inou</span> unterliegt unserer <a href="/privacy-policy">Datenschutzerklärung</a> und dem <a href="/legal/dpa">Auftragsverarbeitungsvertrag</a>, die beschreiben, welche Daten wir erfassen, wie wir sie nutzen und welche Rechte du hast. Durch die Nutzung des Service erkennst du diese Bedingungen an und stimmst ihnen zu.</p>
</div>
<div class="terms-card">
<h2>Akzeptabler Gebrauch</h2>
<h3>Nicht.</h3>
<p>Versuche nicht, auf Daten anderer Nutzer zuzugreifen. Reversiere, prüfe oder attackiere die Plattform nicht. Verwende den Service nicht für illegale Zwecke. Lade keine bösartigen Dateien hoch. Lade keine Inhalte hoch, die nicht mit Gesundheitsdaten zusammenhängen dies ist eine medizinische Plattform, kein allgemeines Speichersystem. Teile deine Zugangsdaten nicht. Verkaufe keinen Zugang zum Service weiter.</p>
<h3>Wenn du das tust.</h3>
<p>Wir können dein Konto suspendieren oder kündigen. Bei illegalen Aktivitäten werden wir mit den Strafverfolgungsbehörden zusammenarbeiten.</p>
</div>
<div class="terms-card">
<h2>Geistiges Eigentum</h2>
<p>Deine Daten gehören dir. Du behältst alle Rechte an den Gesundheitsdaten, Dateien und Informationen, die du hochlädst. Wir beanspruchen kein Eigentum an deinem Inhalt.</p>
<p>Die <span class="inou-brand">inou</span>-Plattform ihre Software, ihr Design, ihre Marken und Dokumentation gehört uns. Diese Bedingungen gewähren dir eine persönliche, nichtexklusive, nicht übertragbare Lizenz zur Nutzung des Service. Sie gewähren dir keine Rechte an unserem Code, Design oder unserer Marke.</p>
</div>
<div class="terms-card">
<h2>Bezahlung</h2>
<h3>Preise.</h3>
<p>Pläne und Preise werden auf unserer <a href="/pricing">Preisseite</a> beschrieben. Die Preise können sich ändern wir informieren dich im Voraus über jede Erhöhung. Die Zahlung wird von Drittanbietern abgewickelt. Wir sehen oder speichern deine Zahlungsdetails nie.</p>
<h3>Rückerstattungen.</h3>
<p>Wenn du unzufrieden bist, kontaktiere uns. Wir bearbeiten Rückerstattungen individuell.</p>
</div>
<div class="terms-card">
<h2>Kündigung</h2>
<h3>Du kannst jederzeit kündigen.</h3>
<p>Lösche dein Konto, und alle deine Daten werden dauerhaft gelöscht. Keine Benachrichtigung erforderlich. Keine Strafe.</p>
<h3>Wir können ebenfalls kündigen.</h3>
<p>Wir können dein Konto wegen Verstoßes gegen diese Bedingungen kündigen, sofern möglich, mit Vorankündigung. Wenn wir den Service vollständig einstellen, geben wir dir angemessene Zeit, deine Daten zu exportieren.</p>
</div>
<div class="terms-card">
<h2>Haftung</h2>
<h3>Haftungsbeschränkung.</h3>
<p>In dem maximalen Umfang, der gesetzlich zulässig ist, ist die Gesamthaftung von <span class="inou-brand">inou</span> dir gegenüber für jegliche Ansprüche, die sich aus diesen Bedingungen oder dem Service ergeben, auf den Betrag begrenzt, den du in den 12 Monaten vor dem Anspruch an uns gezahlt hast. Wir haften nicht für indirekte, beiläufige, besondere, Folgeschäden oder Strafschäden.</p>
<h3>Entschädigung.</h3>
<p>Du verpflichtest dich, <span class="inou-brand">inou</span> gegen Ansprüche zu entschädigen, die sich aus deiner Nutzung des Service, deinen Daten oder deinem Verstoß gegen diese Bedingungen ergeben.</p>
</div>
<div class="terms-card">
<h2>Anwendbares Recht</h2>
<p>Diese Bedingungen unterliegen den Gesetzen des Bundesstaates Florida, USA. Streitigkeiten werden vor den Gerichten Floridas entschieden.</p>
</div>
<div class="terms-card">
<h2>Änderungen</h2>
<p>Wir können diese Bedingungen aktualisieren. Registrierte Nutzer werden per EMail über wesentliche Änderungen informiert. Die fortgesetzte Nutzung nach Änderungen gilt als Zustimmung.</p>
<p>Datenschutzbeauftragter: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
<p>Zuletzt aktualisiert: 8. Februar 2026.</p>
</div>
{{template "footer"}}
</div>
{{end}}

515
tools/translate/main.go Normal file
View File

@ -0,0 +1,515 @@
// translate: MiniMax M2.5 draft → GLM 5 review → write
//
// translate --lang de --what faq
// translate --lang all --what all
// translate --lang de --what yaml --no-review
package main
import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
const apiURL = "https://openrouter.ai/api/v1/chat/completions"
var (
translateModel = "openai/gpt-oss-20b:nitro"
reviewModel = "z-ai/glm-5"
apiKey string
)
var langName = map[string]string{
"da": "Danish", "de": "German", "es": "Spanish", "fi": "Finnish",
"fr": "French", "hi": "Hindi", "id": "Indonesian", "it": "Italian",
"ja": "Japanese", "ko": "Korean", "nl": "Dutch", "no": "Norwegian",
"pl": "Polish", "pt": "Portuguese", "ru": "Russian", "sv": "Swedish",
"th": "Thai", "tr": "Turkish", "uk": "Ukrainian", "vi": "Vietnamese",
"zh": "Chinese (Simplified)",
}
var langNative = map[string]string{
"da": "Dansk", "de": "Deutsch", "es": "Español", "fi": "Suomi",
"fr": "Français", "hi": "हिन्दी", "id": "Bahasa Indonesia", "it": "Italiano",
"ja": "日本語", "ko": "한국어", "nl": "Nederlands", "no": "Norsk",
"pl": "Polski", "pt": "Português", "ru": "Русский", "sv": "Svenska",
"th": "ไทย", "tr": "Türkçe", "uk": "Українська", "vi": "Tiếng Việt",
"zh": "中文",
}
var langAddress = map[string]string{
"da": "du/din", "de": "du/dein (informal)", "es": "tú (informal)",
"fr": "tu/ton (informal)", "hi": "तुम (informal)", "id": "kamu (informal)",
"it": "tu (informal)", "ja": "polite form (です/ます)", "ko": "존댓말 (polite)",
"nl": "je/jouw (informal)", "no": "du/din", "pl": "ty (informal)",
"pt": "você", "ru": "ты (informal)", "sv": "du/din", "th": "คุณ (polite)",
"tr": "sen (informal)", "uk": "ти (informal)", "vi": "bạn (informal)",
"zh": "你 (informal)",
}
var knownTemplates = []string{"landing", "faq", "pricing", "privacy", "security", "dpa", "terms", "connect", "docs", "consent"}
func main() {
lang := flag.String("lang", "", "language(s) or 'all'")
what := flag.String("what", "", "template name(s), 'yaml', or 'all'")
key := flag.String("key", "", "OpenRouter API key")
portal := flag.String("portal", "", "portal directory")
noReview := flag.Bool("no-review", false, "skip review pass")
flag.Parse()
apiKey = pick(*key, os.Getenv("OPENROUTER_API_KEY"), envKey("OPENROUTER_API_KEY"))
if apiKey == "" {
die("no API key")
}
portalDir := *portal
if portalDir == "" {
portalDir = findPortalDir()
}
if *lang == "" || *what == "" {
die("Usage: translate --lang <code|all> --what <name|yaml|all>")
}
langs := resolveLangs(*lang)
tmpls, doYAML := resolveWhat(*what)
review := !*noReview
total := len(langs) * (len(tmpls) + b2i(doYAML))
n, errors := 0, 0
for _, l := range langs {
for _, t := range tmpls {
n++
fmt.Printf("[%d/%d] %s/%s ", n, total, l, t)
if err := doTemplate(portalDir, l, t, review); err != nil {
fmt.Printf("ERROR: %v\n", err)
errors++
}
}
if doYAML {
n++
fmt.Printf("[%d/%d] %s/yaml ", n, total, l)
if err := doYAML_(portalDir, l, review); err != nil {
fmt.Printf("ERROR: %v\n", err)
errors++
}
}
}
fmt.Printf("\nDone: %d/%d succeeded\n", total-errors, total)
if errors > 0 {
os.Exit(1)
}
}
// --- Template ---
func doTemplate(portalDir, lang, name string, review bool) error {
src, err := os.ReadFile(filepath.Join(portalDir, "templates", name+".tmpl"))
if err != nil {
return err
}
ln, addr := langName[lang], langAddress[lang]
prompt := fmt.Sprintf(`Translate this web page template from English to %s.
RULES:
- Preserve ALL HTML tags, attributes, classes, IDs exactly
- Preserve ALL Go template syntax ({{.T.key}}, {{range}}, {{end}}, {{if}}, etc.)
- Preserve ALL JavaScript and CSS untranslated
- Never translate "inou"
- Keep English loanwords %s speakers actually use
- Use %s address forms
- Keep technical terms: DICOM, MRI, CT, FLAIR, MCP, API, JSON, CSV, HIPAA, GDPR, OCR, PDF
- Rename {{define "X"}} to {{define "X_%s"}}
- Output ONLY the translated template`, ln, ln, addr, lang)
fmt.Printf("draft... ")
translated, err := callLLM(translateModel, prompt, string(src), 0)
if err != nil {
return err
}
translated = stripFences(translated)
translated = ensureDefine(translated, name, lang)
if review {
fmt.Printf("GLM review... ")
translated = reviewContent(translated, ln, "template")
}
os.WriteFile(filepath.Join(portalDir, "templates", name+"_"+lang+".tmpl"), []byte(translated), 0644)
fmt.Println("ok")
return nil
}
// --- YAML ---
func doYAML_(portalDir, lang string, review bool) error {
src, err := os.ReadFile(filepath.Join(portalDir, "lang", "en.yaml"))
if err != nil {
return err
}
ln, addr := langName[lang], langAddress[lang]
allKeys := extractKeys(string(src))
keys := make(map[string]string)
passthrough := make(map[string]string)
for k, v := range allKeys {
switch {
case strings.HasPrefix(k, "lang_"):
passthrough[k] = v
case k == "language_name":
passthrough[k] = langNative[lang]
default:
keys[k] = v
}
}
keysJSON, _ := json.Marshal(keys)
prompt := fmt.Sprintf(`Translate these UI strings from English to %s.
Return a JSON object with same keys and translated values.
RULES: keep format placeholders (%%s %%d), never translate "inou", use %s address, keep technical terms.
Return ONLY valid JSON.`, ln, addr)
fmt.Printf("draft... ")
result, err := callLLM(translateModel, prompt, string(keysJSON), 0)
if err != nil {
return err
}
result = stripFences(result)
var translated map[string]string
if err := json.Unmarshal([]byte(result), &translated); err != nil {
return fmt.Errorf("bad JSON: %w", err)
}
if review {
fmt.Printf("GLM review... ")
reviewYAML_(keys, translated, ln)
}
for k, v := range passthrough {
translated[k] = v
}
os.WriteFile(filepath.Join(portalDir, "lang", lang+".yaml"), []byte(rebuildYAML(string(src), translated)), 0644)
fmt.Println("ok")
return nil
}
// --- LLM call ---
func callLLM(model, system, user string, maxTokHint int) (string, error) {
body := map[string]any{
"model": model,
"messages": []map[string]string{{"role": "system", "content": system}, {"role": "user", "content": user}},
"temperature": 0.1,
"provider": map[string]any{"order": []string{"Groq"}, "allow_fallbacks": true},
}
if maxTokHint > 0 {
body["max_tokens"] = maxTokHint
}
data, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", apiURL, bytes.NewReader(data))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
// MiniMax M2.5 reasons before generating — needs generous timeout
// pricing (8KB) = 40s, landing (16KB) ~80s, faq (51KB) ~200s
timeout := time.Duration(len(user)/80+60) * time.Second
if timeout < 3*time.Minute {
timeout = 3 * time.Minute
}
t0 := time.Now()
resp, err := (&http.Client{Timeout: timeout}).Do(req)
if err != nil {
return "", fmt.Errorf("[%.0fs] %v", time.Since(t0).Seconds(), err)
}
defer resp.Body.Close()
respData, _ := io.ReadAll(resp.Body)
fmt.Printf("[%.0fs %dKB] ", time.Since(t0).Seconds(), len(respData)/1024)
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP %d: %.300s", resp.StatusCode, respData)
}
var r struct {
Choices []struct {
Message struct {
Content string `json:"content"`
Reasoning string `json:"reasoning"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
Error *struct {
Message string `json:"message"`
} `json:"error"`
}
if err := json.Unmarshal(respData, &r); err != nil {
return "", fmt.Errorf("bad JSON: %.300s", respData)
}
if r.Error != nil {
return "", fmt.Errorf("API: %s", r.Error.Message)
}
if len(r.Choices) == 0 {
return "", fmt.Errorf("no choices: %.300s", respData)
}
content := r.Choices[0].Message.Content
if content == "" {
content = r.Choices[0].Message.Reasoning
}
if content == "" {
return "", fmt.Errorf("empty content: %.500s", respData)
}
if r.Choices[0].FinishReason == "length" {
return "", fmt.Errorf("truncated (output hit max_tokens)")
}
return content, nil
}
// --- Review ---
type fix struct {
Old string `json:"old"`
New string `json:"new"`
Reason string `json:"reason"`
}
func reviewContent(content, ln, kind string) string {
prompt := fmt.Sprintf(`Review this %s translation to %s. Check: wrong words, grammar, broken HTML tags, broken Go template tags, stray whitespace.
Return JSON array: [{"old":"exact string","new":"fix","reason":"why"}]. If perfect return [].`, ln, kind)
result, err := callLLM(reviewModel, prompt, content, 16384)
if err != nil {
fmt.Printf("(review error: %v) ", err)
return content
}
fixes := parseFixes(result)
applied := 0
for _, f := range fixes {
if f.Old != "" && f.New != "" && strings.Contains(content, f.Old) {
content = strings.Replace(content, f.Old, f.New, 1)
applied++
}
}
if applied > 0 {
fmt.Printf("(%d fixes) ", applied)
}
return content
}
func reviewYAML_(english, translated map[string]string, ln string) {
type pair struct {
EN string `json:"en"`
TR string `json:"tr"`
}
combined := make(map[string]pair)
for k, v := range english {
combined[k] = pair{EN: v, TR: translated[k]}
}
data, _ := json.Marshal(combined)
prompt := fmt.Sprintf(`Review these %s UI string translations. "en" is original, "tr" is translation.
Check: wrong translations, grammar, broken format placeholders.
Return JSON: [{"old":"yaml_key","new":"corrected value","reason":"why"}]. If perfect return [].`, ln)
result, err := callLLM(reviewModel, prompt, string(data), 16384)
if err != nil {
fmt.Printf("(review error: %v) ", err)
return
}
fixes := parseFixes(result)
for _, f := range fixes {
if _, ok := translated[f.Old]; ok {
translated[f.Old] = f.New
}
}
if len(fixes) > 0 {
fmt.Printf("(%d fixes) ", len(fixes))
}
}
func parseFixes(raw string) []fix {
raw = stripFences(strings.TrimSpace(raw))
var fixes []fix
if json.Unmarshal([]byte(raw), &fixes) != nil {
if s := strings.Index(raw, "["); s >= 0 {
if e := strings.LastIndex(raw, "]"); e > s {
json.Unmarshal([]byte(raw[s:e+1]), &fixes)
}
}
}
return fixes
}
// --- Helpers ---
func ensureDefine(content, name, lang string) string {
want := fmt.Sprintf(`{{define "%s_%s"}}`, name, lang)
if strings.Contains(content, want) {
return content
}
if i := strings.Index(content, `{{define "`); i >= 0 {
if j := strings.Index(content[i:], `"}}`); j > 0 {
return content[:i] + want + content[i+j+3:]
}
}
return want + "\n" + content
}
func extractKeys(src string) map[string]string {
keys := make(map[string]string)
scanner := bufio.NewScanner(strings.NewReader(src))
for scanner.Scan() {
line := scanner.Text()
if t := strings.TrimSpace(line); strings.HasPrefix(t, "#") || t == "" {
continue
}
if idx := strings.Index(line, ":"); idx > 0 {
k := strings.TrimSpace(line[:idx])
v := strings.TrimSpace(line[idx+1:])
if len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' {
v = v[1 : len(v)-1]
}
if k != "" && v != "" {
keys[k] = v
}
}
}
return keys
}
func rebuildYAML(src string, translated map[string]string) string {
var out strings.Builder
scanner := bufio.NewScanner(strings.NewReader(src))
for scanner.Scan() {
line := scanner.Text()
t := strings.TrimSpace(line)
if strings.HasPrefix(t, "#") || t == "" {
out.WriteString(line + "\n")
continue
}
idx := strings.Index(line, ":")
if idx < 0 {
out.WriteString(line + "\n")
continue
}
key := strings.TrimSpace(line[:idx])
if val, ok := translated[key]; ok {
if needsQuote(val) {
val = strings.ReplaceAll(val, `\`, `\\`)
val = strings.ReplaceAll(val, `"`, `\"`)
out.WriteString(key + ": \"" + val + "\"\n")
} else {
out.WriteString(key + ": " + val + "\n")
}
} else {
out.WriteString(line + "\n")
}
}
return out.String()
}
func needsQuote(s string) bool {
return s == "" || strings.ContainsAny(s, `:{}[]&*?|>!%@`+"`\"'#") ||
s[0] == ' ' || s[len(s)-1] == ' ' || s[0] == '-' || s[0] == '{' ||
s == "true" || s == "false" || s == "null" || s == "yes" || s == "no"
}
func stripFences(s string) string {
s = strings.TrimSpace(s)
for _, p := range []string{"```json", "```yaml", "```html", "```"} {
if strings.HasPrefix(s, p) {
s = s[len(p):]
break
}
}
if strings.HasSuffix(s, "```") {
s = s[:len(s)-3]
}
return strings.TrimSpace(s)
}
func envKey(name string) string {
for _, p := range []string{"anthropic.env", "/tank/inou/anthropic.env"} {
if data, err := os.ReadFile(p); err == nil {
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(strings.TrimSpace(line), name+"=") {
return strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), name+"="))
}
}
}
}
return ""
}
func findPortalDir() string {
dir, _ := os.Getwd()
for {
if _, err := os.Stat(filepath.Join(dir, "portal", "templates")); err == nil {
return filepath.Join(dir, "portal")
}
p := filepath.Dir(dir)
if p == dir {
break
}
dir = p
}
die("cannot find portal/")
return ""
}
func resolveLangs(spec string) []string {
if spec == "all" {
var out []string
for l := range langName {
out = append(out, l)
}
sort.Strings(out)
return out
}
return strings.Split(spec, ",")
}
func resolveWhat(spec string) ([]string, bool) {
if spec == "all" {
return knownTemplates, true
}
var t []string
yaml := false
for _, p := range strings.Split(spec, ",") {
if p == "yaml" {
yaml = true
} else {
t = append(t, p)
}
}
return t, yaml
}
func pick(ss ...string) string {
for _, s := range ss {
if s != "" {
return s
}
}
return ""
}
func b2i(b bool) int {
if b {
return 1
}
return 0
}
func die(msg string) {
fmt.Fprintln(os.Stderr, msg)
os.Exit(1)
}