diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-08 10:40:05 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-22 19:18:04 +0200 |
| commit | 124621aa023e289a3cb4823b17e1c8bb0e240be7 (patch) | |
| tree | df28d5dfd34730a9a389ee4510d27e3b879e19ca | |
| parent | 47219611aad9061b04cbbe12c9bbbaf933431241 (diff) | |
Übersetzungen DE/EN/LA/JA
| -rw-r--r-- | README.md | 36 | ||||
| -rw-r--r-- | app/README.md | 107 | ||||
| -rw-r--r-- | app/i18n.de.js | 582 | ||||
| -rw-r--r-- | app/i18n.en.js | 575 | ||||
| -rw-r--r-- | app/i18n.ja.js | 570 | ||||
| -rw-r--r-- | app/i18n.la.js | 574 | ||||
| -rw-r--r-- | app/index.html | 1163 | ||||
| -rw-r--r-- | app/manifest.json | 24 | ||||
| -rw-r--r-- | app/pruefe.js | 217 | ||||
| -rw-r--r-- | app/service-worker.js | 15 | ||||
| -rw-r--r-- | bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx | bin | 118375 -> 121001 bytes | |||
| -rw-r--r-- | build_bav.py | 50 |
12 files changed, 3386 insertions, 527 deletions
@@ -93,6 +93,41 @@ Diese werden von anderen Blättern referenziert — beim Einfügen von Zeilen ** (RV voll, KV zu 96 %, PV voll). Genauigkeit ca. 1 % - Beiträge unterjährig: Verzinsung mit (1+r)^0,5 im Einzahlungsjahr +### Arbeitgeberzuschuss: zwei additive Komponenten + +Seit der Erweiterung zerfällt der Zuschuss in `agz = agz_var + agz_fx`: + +| Komponente | Zelle | Rechtsgrund | Verhalten | +|---|---|---|---| +| `agz_var` — prozentual | `C26`, Modus `C30` | § 1a Abs. 1a BetrAVG bzw. Versorgungsordnung | proportional zum Beitrag, im Modus 2 auf die tatsächliche AG-Ersparnis gedeckelt | +| `agz_fx` — Festbetrag | `C27`, `C28`, `C29` | Tarifvertrag / Betriebsvereinbarung (z. B. Deutsche Bahn) | betragsunabhängig, vom Zuschussmodus unberührt, oben drauf | + +Der Festbetrag ist **arbeitgeberfinanzierte Zuwendung nach § 3 Nr. 63 EStG** und belegt +deshalb den 8-%-Topf des § 3 Nr. 63 EStG *und* die 4-%-Beitragsfreiheit des +§ 1 Abs. 1 S. 1 Nr. 9 SvEV **vorrangig** (Vertrauen ~80 %): + +``` +agz_fx = aktiv · [beitrag_mtl ≥ agz_fix_min] · 12 · agz_fix · (dyn ? Lohnindex : 1) +bav_svf = min(bav_br, max(0, 4 % · BBG_RV − agz_fx)) ← Verdrängung +agz_var = f(bav_svf, sverp_ag, Modus) +bav_stf = min(bav_br, max(0, 8 % · BBG_RV − agz_fx − agz_var)) +``` + +Die Reihenfolge ist zyklenfrei: `agz_fx` hängt nur am Beitrag, nicht an `sverp_ag`. +Ökonomisch ist der Festbetrag ein **Sockel, kein Hebel** — er hebt den Durchschnittsertrag +stark, die Grenzrendite des nächsten Beitragseuros senkt er sogar leicht +(∂²Ertrag/∂agz_fix ∂beitrag ≤ 0), weil er dem Eigenbeitrag Freibetrag wegnimmt. + +Die Mindestschwelle `agz_fix_min` macht N(B) **unstetig**: an der Schwelle springt der +Nettoaufwand nach oben (Basisfall, 150 €/Monat fest ab 300 €/Monat Eigenbeitrag: +164,54 € → 173,08 €). Monoton bleibt N, die Bisektion konvergiert also weiter, aber +Zielwerte im Sprungintervall sind schlicht nicht erreichbar — `resid` zeigt das an. + +**Nicht modelliert:** Übersteigt `agz_fx` allein 4 % der BBG-RV, wäre der Überhang beim +Arbeitnehmer beitragspflichtiges Arbeitsentgelt. Diese Beitragslast wird nicht gegen- +gerechnet; das Blatt `Kennzahlen` und die Diagnosetabelle der Web-App weisen den Überhang +separat aus. Bei 2026er Werten greift das ab 338 €/Monat Festzuschuss. + ### Schichtenmodell Beide geförderten Produkte werden in **zwei Schichten** geführt, weil sonst derselbe Euro @@ -188,6 +223,7 @@ Alle im Blatt `Parameter` änderbar. | Beitragsfreiheit der Altersvorsorgedepot-Auszahlung | ~85 % | Keine Versorgungsbezüge | | Halber Unterschiedsbetrag für nicht geförderte Schichten | ~80 % | § 22 Nr. 5 S. 2 Buchst. b i.V.m. § 20 Abs. 1 Nr. 6 S. 2 EStG | | Zuschuss nur bis zur tatsächlichen AG-Ersparnis (Modus 2) | ~75 % | Gegenauffassung liest 15 % als reine Pauschale = Modus 1 | +| Fester AG-Zuschuss belegt 4-%- und 8-%-Topf vorrangig | ~80 % | Arbeitgeberfinanzierte Zuwendung nach § 3 Nr. 63 EStG; verdrängt eigene Umwandlung | | **Sonderausgaben-Höchstbetrag 2.340 €** | **~70 %** | **Modellannahme.** Bestimmt, ob ein Kind den Wert des AV-Depots hebt oder senkt | | Günstigerprüfung als Maximum, nicht Summe | ~90 % | § 10a Abs. 2 EStG | | Modell unterstellt KVdR-Pflichtversicherung im Ruhestand | ~70 % | Freiwillig Versicherte nicht geprüft | diff --git a/app/README.md b/app/README.md index 3a87f05..8727649 100644 --- a/app/README.md +++ b/app/README.md @@ -5,7 +5,11 @@ Ergebnisse, aber bedienbar von Menschen, die keine Tabellenkalkulation aufmachen | Datei | Rolle | |---|---| -| `index.html` | **Die ganze App.** Rechenkern, Oberfläche, Diagramme, Texte — eine Datei, keine externen Abhängigkeiten. Läuft per Doppelklick. | +| `index.html` | **Die ganze App.** Rechenkern, Oberfläche, Diagramme — eine Datei, keine externen Abhängigkeiten außer den vier Wörterbüchern. Läuft per Doppelklick. | +| `i18n.de.js` | Wörterbuch Deutsch. Hauptsprache und Rückfallebene. | +| `i18n.en.js` | Wörterbuch Englisch. | +| `i18n.la.js` | Wörterbuch Latein. | +| `i18n.ja.js` | Wörterbuch Japanisch. | | `manifest.json` | Macht die App installierbar (Name, Farben, Symbole). | | `service-worker.js` | Offline-Cache, `stale-while-revalidate`. | | `icon-*.png` | App-Symbole, 192 / 512 / 512-maskable. | @@ -14,7 +18,8 @@ Ergebnisse, aber bedienbar von Menschen, die keine Tabellenkalkulation aufmachen ## 1. Weitergeben — drei Wege -**a) Nur die Datei.** `index.html` verschicken. Sie ist vollständig autark: Doppelklick genügt, +**a) Nur die Dateien.** `index.html` samt den vier `i18n.*.js` verschicken — sie gehören +zusammen und müssen im selben Ordner liegen. Doppelklick genügt, alles rechnet, Eingaben werden im Browser gespeichert. Was fehlt, ist nur die PWA-Hülle — kein Symbol auf dem Startbildschirm, kein Offline-Cache. Beides braucht es hier auch nicht, die Datei liegt ja schon auf dem Gerät. Die App sagt das dem Nutzer auch selbst. @@ -45,14 +50,15 @@ selbst zu aktualisieren. ## 3. Aufbau der Datei -`index.html` ist in elf nummerierte Teile gegliedert: +`index.html` ist in nummerierte Teile gegliedert: | Teil | Inhalt | |---|---| +| 0 | **Sprachen** — `t()`, Sprachwahl, Rückfall auf Deutsch | | 1 | **Rechenkern** — Portierung von `build_bav.py`. Reine Funktionen, kein DOM | -| 2 | Felddefinitionen: 80 Eingaben mit Bereich, Einheit, Erklärung, Fundstelle | +| 2 | Felddefinitionen: 80 Eingaben mit Bereich, Einheit, Wörterbuchschlüssel | | 3 | Zustand, `localStorage`, Kodierung für den Teilen-Link | -| 4 | Formatierung (de-DE, Komma als Dezimaltrenner in beiden Richtungen) | +| 4 | Formatierung und Zahleneingabe, sprachabhängig | | 5 | Kennzahlen | | 6 | Aufbau der Oberfläche | | 7 | Ergebniskarten mit Δ-Anzeige | @@ -63,6 +69,7 @@ selbst zu aktualisieren. | 11 | Installation und Offline-Betrieb | | 12 | Fensterrahmen: Titelleisten und Minimieren | | 13 | Kachelung: ein, zwei oder drei Fensterspalten | +| 14 | Sprachumschaltung in der Oberfläche | Der Rechenkern in Teil 1 ist **die einzige Stelle, an der gerechnet wird.** Er ist zeichengenau aus `build_bav.py` übertragen; Änderungen an der Rechtslage gehören in beide Dateien. @@ -99,6 +106,85 @@ Bruttobeitrags. Fällt der Nettoaufwand für das erste Jahr auf null — weil man dann bereits ausgeschieden ist —, existiert die Umkehrung nicht. Die App sagt das statt still nichts zu tun. +## 3b. Vier Sprachen + +Deutsch ist die Hauptsprache; Englisch, Latein und Japanisch sind Übersetzungen derselben +Rechtslage, kein zweiter Rechtsstand. **Die Normzitate bleiben in allen Sprachen deutsch** +(`§ 1a BetrAVG`, `§ 226 Abs. 2 S. 2 SGB V`, …) — sie sind die Primärquellen und haben keine +amtliche Übersetzung; eine eigene wäre hier keine Hilfe, sondern eine Fehlerquelle. Ebenso +bleiben zwei Produktnamen stehen, weil sie Eigennamen sind: `Altersvorsorgedepot` und +`Versorgungsbezüge`; im Japanischen mit Glosse (`Versorgungsbezüge(企業年金等の給付)`). + +**Wahl der Sprache**, in dieser Reihenfolge: + +1. `?lang=de|en|la|ja` in der Adresse — teilbar, überschreibt alles, +2. die gemerkte Wahl in `localStorage` unter `altersvorsorge.lang`, +3. `navigator.language`: `ja…` → Japanisch, `en…` → Englisch, sonst Deutsch. + **Latein wird nie automatisch gewählt** — ein Browser, der `la` meldet, meint es fast sicher + nicht als Anzeigesprache. + +**Zu den beiden neuen Fassungen.** Das Latein ist pragmatisches Neulatein: klassische Syntax, +aber gebildete Neuprägungen für Moderne (`pensio operativa`, `impensa pura`, `valor praesens`, +`usura interna`). Römische Ziffern wären ein Witz auf Kosten der Lesbarkeit — und die +Trennzeichenkonvention ist ohnehin jünger als jede lateinische Quelle. Das Japanische folgt +dem, was für Software dieser Art dort üblich ist: Erklärtexte in です・ます, Feldbeschriftungen +als Nominalphrasen im 体言止め. Für die Schrift ist der `--ui`-Stapel um MS PGothic, Hiragino, +Yu Gothic und Meiryo erweitert — eine Rasterschrift mit harten Kanten passt zum Thema. + +Der japanische Satzbau erzwingt, wofür die Funktionsform der Einträge da ist. Das Fazit lautet +im Deutschen „X liegt vorn mit W — D mehr als Y“, im Japanischen dagegen +`Xが首位です(W)。YをD上回ります。` — Verb am Ende, Vergleichsgröße mit を statt „als“. Als +Verkettung fester Bausteine ließe sich das nicht bauen. + +Der Umschalter oben im Kopffenster schreibt beides fort, Speicher und Adresse. Der Teilen-Link +nimmt die Suchzeichenfolge mit, der Zustand steckt weiter im Fragment — beides stört sich nicht. + +**Aufbau der Wörterbücher.** Ein Eintrag ist entweder eine Zeichenkette oder eine Funktion +`(p, f) → string`. Die Funktionsform ist keine Bequemlichkeit, sondern notwendig: ein Satz ist +nicht die Verkettung seiner Teile. Stellung, Numerus und Rektion hängen an der Sprache, und +`'mehr als ' + nom` funktioniert nur, solange beide Sprachen denselben Satzbau haben. Deshalb +bekommen die Funktionen **Rohwerte** — Zahlen, keine fertigen Textstücke — und formatieren +selbst über `f` (`f.eur`, `f.pct`, `f.nf2`, …). Sichtbar wird der Unterschied etwa im Fazit: +das Deutsche braucht dort einen Genitiv (`der bAV`), das Englische eine Präposition. + +``` +t('n.beitrag.ueber8', { v8: v8 }) + de: 'Über 8 % der Beitragsbemessungsgrenze RV (' + f.eur(p.v8) + ' im Jahr). …' + en: 'Above 8 % of the pension-insurance contribution ceiling (' + f.eur(p.v8) + ' per year). …' +``` + +**Schlüsselschema.** Eingabefelder liegen unter `f.<src>:<key>.label|hint|unit|o<wert>`, +die Optionen also unter ihrem *Wert*, nicht unter ihrem Index — dann bleibt die Zuordnung +richtig, wenn eine Option dazukommt. `FIELDS`, `OPTS` und `METRICS` tragen ihre Texte nicht +mehr als Feld, sondern als `Object.defineProperty`-Zugriffsfunktion: `f.label` steht unverändert +an jeder Aufrufstelle und liefert nach einem Sprachwechsel von selbst den neuen Wert. + +**Fehlt ein Eintrag**, greift Deutsch; fehlt auch der, erscheint der Schlüssel selbst im +Klartext. Ein stiller Leerstring wäre die schlechtere Wahl — er verschwindet unbemerkt. + +**Zahlen.** Umgeschaltet wird nur die Zifferngruppierung — `de` und `la` → `de-DE`, +`en` → `en-GB`, `ja` → `ja-JP`; die Währung bleibt überall der nachgestellte Euro: +`75.000,00 €` bzw. `75,000.00 €`. Die Eingabe nimmt **beide** +Schreibweisen an, damit ein Sprachwechsel keine getippte Zahl entwertet. Die Regel in +`parseNum`: stehen beide Zeichen im Text, ist das rechte das Dezimalzeichen; steht nur eines +und folgen ihm genau drei Ziffern, ist es die Tausendergruppe der aktuellen Sprache +(`GRUPPEN`: `.` für de und la, `,` für en und ja). Damit +bleibt allein `1,234` mehrdeutig — und genau dort entscheidet die Sprache. + +**Was ein Sprachwechsel nicht anfasst:** die Eingaben, den Referenzstand und die Ergebnisse. +`setLang` baut nur die Teile neu, deren Beschriftungen beim Aufbau festgeschrieben werden +(Kennzahlenleiste, Registerkarten, Felder, Fenstertitel); alles Übrige zeichnet `refresh()` +ohnehin bei jedem Durchgang. Der Referenzstand merkt sich deshalb den *Feldschlüssel* und +nicht den fertigen Satz — sonst stünde nach dem Wechsel „vor der Änderung von …“ noch in der +alten Sprache da. + +**Prüfung.** `pruefe.js` (Entwicklungswerkzeug, wird vom Service Worker nicht ausgeliefert) lädt die Seite kopflos in +jsdom, schaltet alle vier Sprachen durch, klappt jeden Abschnitt auf, erzwingt über fünf Szenarien +die bedingten Texte und prüft: kein unaufgelöster Schlüssel, keine deutschen Reste im +englischen Modus außer den erlaubten Fachbegriffen, kein sichtbar gebliebener Schlüssel, und +— die eigentliche Invariante — +**identische Rechenergebnisse in allen vier Sprachen**. + ## 4. Prüfung Der Kern ist gegen die Excel-Mappe geprüft, sechs unabhängige Fälle, jeweils rund 20 Aggregate: @@ -111,6 +197,10 @@ Der Kern ist gegen die Excel-Mappe geprüft, sechs unabhängige Fälle, jeweils | **AVD verrentet** | 5,2 · 10⁻¹⁵ | | **AVD verrentet + 30 % Einmalbetrag** | 3,6 · 10⁻¹⁵ | | **beide Produkte verrentet, Splitting, PKV, andere Zeitachse, 2 Kinder** | 4,4 · 10⁻¹⁵ | +| **fester AG-Zuschuss 100 €/Monat** | exakt (< 10⁻⁹ auf allen Jahreswerten) | +| **fester Zuschuss 400 €/Monat, 4-%-Topf erschöpft** | exakt | +| **fester Zuschuss mit Mindestschwelle, ober- und unterhalb** | exakt | +| **Mindestschwelle + Modus 2 (Bisektion über die Sprungstelle)** | exakt | > Der Prüfanker ist **erhalten geblieben**: `build_bav.py` wurde für die Depotverrentung > mitgezogen (neue Eingaben, drei neue Spalten im Blatt `Auszahlung`, zweite Memo-Zeile im @@ -177,6 +267,13 @@ Die Testskripte liegen unter `outputs/tests/`, `./tests/alle.sh` fährt alles du sättigt; der Monatsbeitrag meldet das Überschreiten der 4-%- und der 8-%-Grenze der Beitragsbemessungsgrenze RV. Beides sind Schwellen, an denen der Regler seine Wirkung verliert oder die Rechtslage wechselt — ohne Hinweis dreht man ratlos weiter. +- **Fester Arbeitgeberzuschuss** (Regler „Fester Zuschuss zusätzlich“, darunter der Schalter + für die Dynamisierung, unter „Weitere Angaben“ die Mindest-Eigenleistung). Tarifvertragliche + Festbeträge — Deutsche Bahn und viele andere — hängen nicht am Umwandlungsbetrag und stehen + neben den 15 % des § 1a Abs. 1a BetrAVG. Der Regler meldet, wenn der Festbetrag den 4-%-Topf + des § 1 Abs. 1 S. 1 Nr. 9 SvEV vorrangig belegt und dadurch die eigene Umwandlung verdrängt, + und warnt oberhalb von 4 % der BBG-RV (2026: 338 €/Monat), wo das Modell die dann fällige + Beitragspflicht des Überhangs bewusst nicht gegenrechnet. - **Klartext-Fazit** in einem Satz: wer vorn liegt, um wie viel, und welcher Posten des Wasserfalls den Abstand hauptsächlich trägt. Beschreibend formuliert, nicht empfehlend. Angehängt ein Belastbarkeitstest über ±1 Prozentpunkt Bruttorendite, der ausdrücklich sagt, diff --git a/app/i18n.de.js b/app/i18n.de.js new file mode 100644 index 0000000..dcc7993 --- /dev/null +++ b/app/i18n.de.js @@ -0,0 +1,582 @@ +/* ===================================================================== + Wörterbuch Deutsch — Hauptsprache und zugleich Rückfallebene. + Werte sind entweder Zeichenketten oder Funktionen (p, f): + p Parameterobjekt der Aufrufstelle (Rohwerte, keine fertigen Texte) + f Formatierer der aktuellen Sprache: eur, eur2, pct, num, nf0, nf2 + Schlüssel der Eingabefelder: 'f.<src>:<key>.label|hint|unit|o<wert>'. + ===================================================================== */ +window.I18N_DE = { + 'f.I:gehalt.label': 'Jahresbrutto heute', + 'f.I:gehalt.hint': 'Ohne Einmalzahlungen, gegebenenfalls einschließlich anteiligem 13. Gehalt. Bestimmt Ihren Grenzsteuersatz und ob Sie über den Beitragsbemessungsgrenzen liegen.', + 'f.I:beitrag.label': 'Monatsbeitrag bAV brutto', + 'f.I:beitrag.hint': 'Bruttoentgeltumwandlung nach § 1a BetrAVG. Ein Rechtsanspruch besteht nur bis 4 % der Beitragsbemessungsgrenze RV, darüber braucht es die Zustimmung des Arbeitgebers.', + 'f.I:netto_ziel.label': 'Monatlicher Nettoaufwand', + 'f.I:netto_ziel.hint': 'Der Betrag, um den Ihr verfügbares Einkommen im ersten Jahr sinkt — der tatsächlich aufgegebene Konsum und damit die ökonomisch richtige Entscheidungsvariable. Genau dieser Betrag wird in allen drei Optionen investiert; das ist die Bedingung für einen fairen Vergleich. Er hängt eineindeutig am Bruttobeitrag darüber: N(B) = B − SV-Ersparnis − Steuerersparnis ist streng monoton wachsend, also umkehrbar. Geschlossen geht das wegen der Knicke an den Beitragsbemessungsgrenzen und den Tarifeckwerten nicht, deshalb 34 Halbierungen — Genauigkeit unter einem Zehntelcent.', + 'f.I:agzus.label': 'Arbeitgeberzuschuss', + 'f.I:agzus.hint': 'Gesetzliches Minimum 15 % (§ 1a Abs. 1a BetrAVG); viele Arbeitgeber zahlen mehr. Break-even gegenüber dem Privatdepot grob dort, wo (1+z)·(1−0,211) ≈ 1, also z ≈ 26,7 % — nachprüfbar mit der Sensitivitätskurve. Im Zuschussmodus „spitz“ ist dieser Wert oft gar nicht erreichbar, weil der Zuschuss vorher sättigt.', + 'f.I:agz_fix.label': 'Fester Zuschuss zusätzlich', + 'f.I:agz_fix.hint': 'Betragsunabhängiger Festzuschuss aus Tarifvertrag oder Betriebsvereinbarung — bei der Deutschen Bahn und in vielen Tarifwerken üblich, dort zusätzlich zu den 15 % nach § 1a Abs. 1a BetrAVG. Er ist arbeitgeberfinanzierte Zuwendung nach § 3 Nr. 63 EStG und belegt den 8-%-Topf sowie die 4-%-Beitragsfreiheit (§ 1 Abs. 1 S. 1 Nr. 9 SvEV) VORRANGIG: Ihre eigene Umwandlung wird insoweit verdrängt und teurer. Ökonomisch ist er ein Sockel, kein Hebel: die Grenzrendite Ihres nächsten Beitragseuros ändert er nicht (∂²Ertrag/∂agz_fix ∂beitrag ≤ 0, er kann sie über die Verdrängung sogar senken), den Durchschnitt hebt er stark. In der Sensitivitätskurve ist er deshalb näherungsweise affin — bis der 4-%- bzw. 8-%-Topf erschöpft ist, dann knickt sie.', + 'f.I:agz_fix_dyn.label': 'Festen Zuschuss dynamisieren', + 'f.I:agz_fix_dyn.hint': 'Festbeträge stehen meist nominal im Tarifvertrag und werden nur bei Tarifrunden angehoben — Voreinstellung deshalb „nominal konstant“. Über 27 Jahre bei 2 % Inflation bleibt davon der Faktor 1,02⁻²⁷ ≈ 0,59 übrig; wer das für zu pessimistisch hält, schaltet auf „mit dem Gehalt“ und bekommt einen realen Zuschuss mit Wachstumsrate gsteig − infl.', + 'f.I:agz_fix_dyn.o0': 'nominal konstant', + 'f.I:agz_fix_dyn.o1': 'mit dem Gehalt', + 'f.I:agzus_sv.label': 'Zuschussmodus', + 'f.I:agzus_sv.hint': '„Spitz“ ist der Gesetzeswortlaut: § 1a Abs. 1a BetrAVG schuldet den Zuschuss nur, SOWEIT der Arbeitgeber tatsächlich Sozialabgaben spart. Liegt Ihr Entgelt über beiden Beitragsbemessungsgrenzen, spart er nichts und schuldet nichts. Deshalb sättigt der Regler darüber im Basisfall bei rund 21 %. „Voller Beitrag“ ist eine freiwillige oder tarifvertragliche Zusage, „sv-freier Anteil“ die verbreitete pauschale Praxis. Vertrauensgrad der spitzen Lesart ca. 75 %.', + 'f.I:agzus_sv.o0': 'voller Beitrag', + 'f.I:agzus_sv.o1': 'sv-freier Anteil', + 'f.I:agzus_sv.o2': 'spitz gerechnet', + 'f.I:dyn.label': 'Beitrag dynamisieren', + 'f.I:dyn.hint': 'Steigt der Beitrag mit der Gehaltssteigerung, bleibt die Ausschöpfung des 8-%-Topfes über die Jahre ungefähr konstant. Bleibt er nominal konstant, sinkt sein realer Wert mit der Inflation — und der Nettoaufwand, den die beiden anderen Optionen zum Vergleich bekommen, sinkt mit.', + 'f.I:dyn.o1': 'mit dem Gehalt', + 'f.I:dyn.o0': 'nominal konstant', + 'f.I:rendite.label': 'Bruttorendite ETF p.a.', + 'f.I:rendite.hint': 'Vor Kosten, vor Steuern. In allen drei Optionen derselbe ETF — nur so vergleicht man Förderarchitekturen statt Anlageentscheidungen.', + 'f.I:alter.label': 'Alter heute', + 'f.I:alter.hint': 'Startpunkt der Ansparphase. Kalenderjahr 0 = 2026.', + 'f.I:alter.unit': 'Jahre', + 'f.I:rentbeg.label': 'Auszahlungsbeginn', + 'f.I:rentbeg.hint': 'Beginn der Auszahlungsphase aller drei Optionen. Das Altersvorsorgedepot ist frühestens ab 65 auszahlbar (Reform 2026).', + 'f.I:rentbeg.unit': 'Jahre', + 'f.I:kv_ruhe.label': 'Krankenversicherung im Ruhestand', + 'f.I:kv_ruhe.hint': 'Der entscheidende Hebel. In der GKV sind Betriebsrenten Versorgungsbezüge und tragen den vollen Beitragssatz (§§ 229, 250 SGB V); private Altersvorsorge nicht. Allein diese Einstellung dreht das Ergebnis.', + 'f.I:kv_ruhe.o1': 'gesetzlich', + 'f.I:kv_ruhe.o2': 'privat', + 'f.I:auszform.label': 'bAV-Auszahlung', + 'f.I:auszform.hint': 'Kapital: im Zuflussjahr voll steuerpflichtig, Beiträge zur KV auf 120 Monate verteilt (§ 229 Abs. 1 S. 3 SGB V). Die Rente streckt den Bezug auf die Restlebenszeit und schirmt deshalb ein Vielfaches an Kapital unter demselben Freibetrag ab. Das Modell kennt nur ganz oder gar nicht; eine Teilkapitalisierung der bAV ist nicht abgebildet.', + 'f.I:auszform.o1': 'Kapital', + 'f.I:auszform.o2': 'lebenslange Rente', + 'f.I:teilkap.label': 'Altersvorsorgedepot: Einmalbetrag zu Beginn', + 'f.I:teilkap.hint': 'Bis 30 % des angesparten Kapitals sind zu Beginn der Auszahlungsphase als Einmalbetrag möglich — in beiden Auszahlungsformen. Er kostet aus zwei Gründen: volle Progression im Zuflussjahr und danach nur noch der Zinssatz nach Abgeltungsteuer. Der Überschuss über den Jahreshöchstbetrag („Sleeve“) wird ohne Einmalbetrag verrentet; das ist eine Modellkonvention, kein Rechtssatz.', + 'f.I:av_auszform.label': 'Altersvorsorgedepot: Rest', + 'f.I:av_auszform.hint': 'Was mit dem Kapital jenseits des Einmalbetrags geschieht — unabhängig von der bAV-Entscheidung darüber. Auszahlungsplan: das Geld bleibt investiert und wird verzinst entnommen; die nicht geförderte Schicht wird mit dem halben Unterschiedsbetrag besteuert. Lebenslange Rente: das Kapital geht an den Anbieter, dafür wird die nicht geförderte Schicht nur mit dem Ertragsanteil besteuert (§ 22 Nr. 5 S. 2 Buchst. a EStG, 17 % ab Alter 67 statt hier rund 26 %). Die geförderte Schicht ist in beiden Fällen voll nachgelagert steuerpflichtig, das ändert sich nicht.', + 'f.I:av_auszform.o1': 'Auszahlungsplan', + 'f.I:av_auszform.o2': 'lebenslange Rente', + 'f.I:agz_fix_min.label': 'Mindest-Eigenbeitrag für den festen Zuschuss', + 'f.I:agz_fix_min.hint': 'Matching-Modelle knüpfen den Festbetrag an eine Mindest-Eigenleistung („wer mindestens X umwandelt, bekommt Y“). 0 heißt bedingungslos — dann fließt der Zuschuss in jedem aktiven Jahr, auch bei Beitrag null. Achtung auf die Modellsemantik: ein wirklich bedingungsloser Zuschuss wäre kein Differenzeffekt der Umwandlungsentscheidung und schlüge dem Vergleich zugunsten der bAV zu Buche, obwohl er auch ohne Umwandlung flösse. Nur eintragen, was tatsächlich an der Teilnahme hängt.', + 'f.I:rentfak_av.label': 'Rentenfaktor Altersvorsorgedepot', + 'f.I:rentfak_av.hint': 'Nur bei Verrentung des Depots. Voreingestellt wie bei der bAV; ein Depotvertrag kommt ohne Versicherungsmantel aus und kann deshalb günstiger sein — den Wert aus dem konkreten Angebot eintragen. Bei 0 gibt es keine Rente.', + 'f.I:rentfak_av.unit': '€ je 10.000 €', + 'f.I:rentdyn_av.label': 'Dynamik der Depotrente p.a.', + 'f.I:rentdyn_av.hint': 'Überschussbeteiligung bzw. Fondsentwicklung in der Rentenphase des Depotvertrags.', + 'f.I:rentfak.label': 'Rentenfaktor bAV', + 'f.I:rentfak.hint': 'Nur bei Auszahlungsform „Rente“. 26 € Monatsrente je 10.000 € entspricht etwa 3,1 % Entnahmequote. Steht im Produktinformationsblatt.', + 'f.I:rentfak.unit': '€ je 10.000 €', + 'f.I:rentdyn.label': 'Dynamik der bAV-Rente p.a.', + 'f.I:rentdyn.hint': 'Überschussbeteiligung bzw. Fondsentwicklung in der Rentenphase.', + 'f.I:fuenftel.label': 'Fünftelregelung prüfen', + 'f.I:fuenftel.hint': '§ 34 EStG ist bei planmäßiger Kapitalabfindung aus § 3 Nr. 63-Verträgen nach BFH X R 23/15 nur ausnahmsweise anwendbar. Konservativ: nein.', + 'f.I:fuenftel.o0': 'nein (konservativ)', + 'f.I:fuenftel.o1': 'ja', + 'f.I:planende.label': 'Vergleichshorizont bis Alter', + 'f.I:planende.hint': 'Auszahlungspläne laufen bis hierhin. Lebenslange Renten werden über denselben Horizont verglichen; was danach noch fließt, steht als Memo unter „Diagnose“. Wer alt wird, gewinnt dort.', + 'f.I:planende.unit': 'Jahre', + 'f.I:ausscheid.label': 'Ausscheiden aus dem Job', + 'f.I:ausscheid.hint': 'Ab hier kein Gehalt und keine Beiträge mehr. Das Kapital verzinst sich weiter bis zum Auszahlungsbeginn.', + 'f.I:ausscheid.unit': 'Jahre', + 'f.I:tzalter.label': 'Teilzeit ab Alter', + 'f.I:tzalter.hint': 'Jeder Wert ab dem Auszahlungsbeginn bedeutet: keine Teilzeit. Voreingestellt ist deshalb 67.', + 'f.I:tzalter.unit': 'Jahre', + 'f.I:tzfaktor.label': 'Teilzeitfaktor', + 'f.I:tzfaktor.hint': 'Anteil des Vollzeitgehalts. 60 % = 60-%-Stelle.', + 'f.I:gsteig.label': 'Gehalts- und Lohnsteigerung p.a.', + 'f.I:gsteig.hint': 'Schreibt auch Beitragsbemessungsgrenzen, Bezugsgröße, Durchschnittsentgelt und Rentenwert fort.', + 'f.I:stkl.label': 'Veranlagung', + 'f.I:stkl.hint': 'Maßgeblich ist die VERANLAGUNGSART, nicht die Lohnsteuerklasse: die Klasse steuert nur den unterjährigen Abzug, der Jahresbescheid rechnet ohnehin mit Splitting nach § 32a Abs. 5 EStG. Bei „zusammen“ unbedingt das zvE des Partners eintragen.', + 'f.I:stkl.o1': 'einzeln', + 'f.I:stkl.o3': 'zusammen (Splitting)', + 'f.I:partner.label': 'zvE des Partners', + 'f.I:partner.hint': 'Nur bei Zusammenveranlagung. Gerechnet wird 2·ESt((zvE + zvE_Partner)/2).', + 'f.I:kist.label': 'Kirchensteuersatz', + 'f.I:kist.hint': '8 % in Bayern und Baden-Württemberg, sonst 9 %, 0 bei Kirchenaustritt. Wirkt auch auf die Abgeltungsteuer: 0,25/(1+0,25k)·(1+0,055+k).', + 'f.I:tarifanp.label': 'Anpassung der Tarifeckwerte p.a.', + 'f.I:tarifanp.hint': 'Der ganze Tarif wird um den Faktor λ = (1+x)^t gestreckt: ESt = s·λ·T(zvE/s/λ). Das ist exakt äquivalent zu einer proportionalen Verschiebung aller Eckwerte. 0 % zeigt volle kalte Progression.', + 'f.I:kinder.label': 'Anzahl Kinder', + 'f.I:kinder.hint': 'Kinderzulage im Altersvorsorgedepot: 1 € je eingezahltem € bis 300 € je Kind.', + 'f.I:kind_jahre.label': 'Jahre mit Kinderzulage', + 'f.I:kind_jahre.hint': 'Die Kinderzulage hängt am Kindergeldanspruch (§ 85 EStG), läuft also nicht über die ganze Ansparphase.', + 'f.I:kind_jahre.unit': 'Jahre', + 'f.I:kinderlos.label': 'Kinderlos (PV-Zuschlag)', + 'f.I:kinderlos.hint': '0,6 Prozentpunkte Zuschlag zur Pflegeversicherung, allein vom Arbeitnehmer zu tragen (§ 55 Abs. 3 SGB XI).', + 'f.I:kinderlos.o0': 'nein', + 'f.I:kinderlos.o1': 'ja', + 'f.I:k_bav.label': 'Kosten bAV-Produkt p.a.', + 'f.I:k_bav.hint': 'Versicherungsmantel plus TER. Die Effektivkosten stehen im Produktinformationsblatt — dort nachsehen, nicht schätzen.', + 'f.I:k_av.label': 'Kosten Altersvorsorgedepot p.a.', + 'f.I:k_av.hint': 'Beim Standarddepot sind die Effektivkosten gesetzlich auf 1,0 % p.a. gedeckelt.', + 'f.I:k_priv.label': 'Kosten privates ETF-Depot p.a.', + 'f.I:k_priv.hint': 'Reine TER eines breiten Welt-ETF.', + 'f.I:infl.label': 'Inflation p.a.', + 'f.I:infl.hint': 'Diskontierungssatz für alle Angaben „in heutiger Kaufkraft“. Ändert die Rangfolge kaum, aber alle Niveaus.', + 'f.I:sonst_zve.label': 'sonstiges zvE im Ruhestand', + 'f.I:sonst_zve.hint': 'Vor allem der steuerpflichtige Teil der gesetzlichen Rente, ohne die hier verglichenen Produkte. Bestimmt den Grenzsteuersatz in der Auszahlungsphase — und damit die Tarifarbitrage t₀ − t₁.', + 'f.I:rente_br.label': 'gesetzliche Bruttorente p.a.', + 'f.I:rente_br.hint': 'Nur für den Deckel in der Krankenversicherung: gesetzliche Rente und Versorgungsbezüge teilen sich EINE Beitragsbemessungsgrenze (§ 223 Abs. 3 SGB V).', + 'f.P:bbg_rv.label': 'BBG Rentenversicherung (jährlich)', + 'f.P:bbg_rv.hint': 'SVBezGrV 2026 (BGBl. 2025 I Nr. 116), bundeseinheitlich.', + 'f.P:bbg_kv.label': 'BBG Kranken-/Pflegeversicherung', + 'f.P:bbg_kv.hint': 'SVBezGrV 2026; 5.812,50 € monatlich.', + 'f.P:bezug.label': 'Bezugsgröße (monatlich)', + 'f.P:bezug.hint': 'SVBezGrV 2026, 47.460 € jährlich. Ein Zwanzigstel davon ist der Freibetrag für Versorgungsbezüge in der KV (§ 226 Abs. 2 S. 2 SGB V).', + 'f.P:de_rv.label': 'Durchschnittsentgelt RV', + 'f.P:de_rv.hint': 'Anlage 1 zu SGB VI, Wert 2026 — Nenner für die Entgeltpunkte.', + 'f.P:rw.label': 'Aktueller Rentenwert', + 'f.P:rw.hint': 'Rentenwertbestimmungsverordnung 2026, ab 1.7.2026: 42,52 € (+4,24 %).', + 'f.P:rw.unit': '€', + 'f.P:rv_an.label': 'RV-Beitrag Arbeitnehmeranteil', + 'f.P:rv_an.hint': '18,6 % hälftig.', + 'f.P:av_an.label': 'AV-Beitrag Arbeitnehmeranteil', + 'f.P:av_an.hint': '2,6 % hälftig.', + 'f.P:kv_allg.label': 'KV allgemeiner Beitragssatz', + 'f.P:kv_allg.hint': '§ 241 SGB V.', + 'f.P:kv_zus.label': 'KV durchschnittlicher Zusatzbeitrag', + 'f.P:kv_zus.hint': 'Bekanntmachung des BMG; kassenindividuell abweichend — den Satz Ihrer Kasse eintragen.', + 'f.P:pv_ges.label': 'PV Gesamtbeitragssatz', + 'f.P:pv_ges.hint': '§ 55 SGB XI. Kinderabschläge ab dem zweiten Kind sind nicht modelliert.', + 'f.P:pv_an.label': 'PV Arbeitnehmeranteil', + 'f.P:pv_an.hint': 'In Sachsen abweichend (2,3 %).', + 'f.P:pv_kl.label': 'PV Zuschlag für Kinderlose', + 'f.P:pv_kl.hint': '§ 55 Abs. 3 SGB XI, allein vom Arbeitnehmer.', + 'f.P:gfb.label': 'Grundfreibetrag (Ende Zone 1)', + 'f.P:gfb.hint': '§ 32a Abs. 1 EStG i.d.F. des Steuerfortentwicklungsgesetzes.', + 'f.P:e2.label': 'Ende Zone 2', + 'f.P:e2.hint': 'Grenzsteuersatz steigt von 14 % auf 23,97 %.', + 'f.P:e3.label': 'Ende Zone 3', + 'f.P:e3.hint': 'Grenzsteuersatz steigt von 23,97 % auf 42 %.', + 'f.P:e4.label': 'Beginn Zone 5 (Reichensteuer)', + 'f.P:e4.hint': 'Ab 277.826 € gilt 45 %.', + 'f.P:a2.label': 'Koeffizient a₂', + 'f.P:a2.hint': 'ESt = (a₂·y + b₂)·y mit y = (zvE − Grundfreibetrag)/10.000.', + 'f.P:b2.label': 'Koeffizient b₂', + 'f.P:a3.label': 'Koeffizient a₃', + 'f.P:a3.hint': 'ESt = (a₃·z + b₃)·z + c₃ mit z = (zvE − 17.799)/10.000.', + 'f.P:b3.label': 'Koeffizient b₃', + 'f.P:c3.label': 'Konstante c₃', + 'f.P:c4.label': 'Konstante c₄', + 'f.P:c4.hint': 'ESt = 0,42·zvE − c₄.', + 'f.P:c5.label': 'Konstante c₅', + 'f.P:c5.hint': 'ESt = 0,45·zvE − c₅.', + 'f.P:soli.label': 'Solidaritätszuschlag', + 'f.P:soli.hint': 'SolzG 1995.', + 'f.P:soli_fg.label': 'Soli-Freigrenze', + 'f.P:soli_fg.hint': '2026, Grundtarif, bezogen auf die festgesetzte ESt; im Splitting doppelt. Daneben eine Milderungszone.', + 'f.P:soli_mz.label': 'Milderungszone Grenzbelastung', + 'f.P:soli_mz.hint': '§ 3 Abs. 2a SolzG.', + 'f.P:an_pausch.label': 'Arbeitnehmer-Pauschbetrag', + 'f.P:an_pausch.hint': '§ 9a S. 1 Nr. 1a EStG.', + 'f.P:sa_pausch.label': 'Sonderausgaben-Pauschbetrag', + 'f.P:sa_pausch.hint': '§ 10c EStG.', + 'f.P:kapst.label': 'Abgeltungsteuersatz', + 'f.P:kapst.hint': '§ 32d Abs. 1 EStG.', + 'f.P:tfs.label': 'Teilfreistellung Aktienfonds', + 'f.P:tfs.hint': '§ 20 Abs. 1 Nr. 3 i.V.m. § 20 InvStG, Aktienfondsanteil über 50 %.', + 'f.P:sparerpb.label': 'Sparer-Pauschbetrag', + 'f.P:sparerpb.hint': '§ 20 Abs. 9 EStG; im Splitting verdoppelt. Absorbiert einen großen Teil der Vorabpauschale — siehe Wasserfall.', + 'f.P:basiszins.label': 'Basiszins Vorabpauschale', + 'f.P:basiszins.hint': 'BMF-Schreiben vom 13.01.2026, § 18 Abs. 4 InvStG (2025: 2,53 %). Auf 0 setzen heißt: keine Vorabpauschale.', + 'f.P:vp_faktor.label': 'Faktor für den Basisertrag', + 'f.P:vp_faktor.hint': '§ 18 Abs. 1 S. 1 InvStG.', + 'f.P:st_frei_q.label': 'steuerfrei § 3 Nr. 63 (Anteil BBG-RV)', + 'f.P:st_frei_q.hint': '8 % der BBG-RV = 8.112 € in 2026.', + 'f.P:sv_frei_q.label': 'sozialabgabenfrei (Anteil BBG-RV)', + 'f.P:sv_frei_q.hint': '§ 1 Abs. 1 S. 1 Nr. 9 SvEV; 4 % = 4.056 € in 2026. Zugleich die Grenze des Rechtsanspruchs nach § 1a BetrAVG.', + 'f.P:zul_g1.label': 'Zulage Stufe 1 (je € Eigenbeitrag)', + 'f.P:zul_g1.hint': 'Altersvorsorgereformgesetz, in Kraft seit Mai 2026, Anwendung ab 1.1.2027.', + 'f.P:zul_s1.label': 'Grenze Stufe 1', + 'f.P:zul_s1.hint': 'Bis 360 € Eigenbeitrag: 50 Cent je €.', + 'f.P:zul_g2.label': 'Zulage Stufe 2 (je € Eigenbeitrag)', + 'f.P:zul_g2.hint': 'Von 361 bis 1.800 €: 25 Cent je €.', + 'f.P:zul_s2.label': 'Grenze Stufe 2', + 'f.P:zul_s2.hint': 'Höchstbetrag geförderter Eigenbeitrag; ergibt eine maximale Grundzulage von 540 €.', + 'f.P:kind_zul.label': 'Kinderzulage je Kind', + 'f.P:kind_zul.hint': '1 € je eingezahltem € bis 300 € je Kind.', + 'f.P:av_max.label': 'Höchstbetrag Einzahlung p.a.', + 'f.P:av_max.hint': 'BMF-FAQ zur Reform der geförderten privaten Altersvorsorge, Stand 05.05.2026. Was darüber liegt, läuft in eine Nebenschiene, die wie ein Privatdepot besteuert wird.', + 'f.P:av_sa_max.label': 'Höchstbetrag Sonderausgabenabzug § 10a', + 'f.P:av_sa_max.hint': 'MODELLANNAHME, Vertrauensgrad ca. 70 %. Eigenbeitrag bis 1.800 € zuzüglich Zulagen. Diese eine Zahl entscheidet, ob ein Kind den Wert des AV-Depots hebt oder senkt.', + 'f.P:ertragsq.label': 'Ertragsanteil (Alter 67)', + 'f.P:ertragsq.hint': '§ 22 Nr. 1 S. 3 Buchst. a Doppelbuchst. bb EStG, für nicht geförderte Rententeile.', + 'f.P:halb.label': 'steuerpflichtiger Anteil des Unterschiedsbetrags', + 'f.P:halb.hint': '§ 20 Abs. 1 Nr. 6 S. 2 EStG analog (Vertrag über 12 Jahre, Auszahlung nach 62): halber Unterschiedsbetrag. Vertrauensgrad ca. 80 %.', + + /* ---------------- Rahmen, Fenstertitel, Kopf und Fuß ---------------- */ + 'app.title': 'Altersvorsorge-Vergleich', + 'app.meta': 'Betriebliche Altersvorsorge, Altersvorsorgedepot und privates ETF-Depot bei gleichem Nettoaufwand vergleichen. Rechtsstand 2026.', + 'hdr.title': 'Altersvorsorge‑Vergleich', + 'hdr.sub': 'Betriebliche Altersvorsorge, Altersvorsorgedepot und privates ETF‑Depot — bei <b>gleichem Nettoaufwand</b> und mit demselben ETF. Rechtsstand 2026.', + 'win.header': 'Altersvorsorge-Vergleich', + 'win.result': 'Ergebnis', + 'win.input': 'Ihre Angaben', + 'win.chart': 'Diagramme', + 'win.extra': 'Weitere Angaben', + 'win.calc': 'Rechenweg', + 'win.share': 'Speichern und weitergeben', + 'win.min': 'Minimieren', + 'win.restore': 'Wiederherstellen', + 'win.n': function(p){ return 'Fenster ' + p.n; }, + 'input.sub': 'Die Zahlen rechnen sich bei jeder Änderung sofort neu. Mit <span class="qm" style="cursor:default">?</span> erklärt sich jedes Feld selbst.', + 'extra.sub': 'Alles hier hat sinnvolle Voreinstellungen. Nur anfassen, wenn es auf Sie zutrifft.', + 'calc.sub': 'Für alle, die es nachvollziehen wollen.', + 'share.sub': 'Ihre Eingaben liegen ausschließlich in diesem Browser (<kbd>localStorage</kbd>). Nichts wird übertragen, es gibt keinen Server und keine Tracker.', + 'ui.share': 'Link mit meinen Zahlen kopieren', + 'ui.export': 'Als Datei sichern', + 'ui.import': 'Datei laden', + 'ui.reset': 'Alles zurücksetzen', + 'ui.pin.set': 'Als Referenz merken', + 'ui.pin.clear': 'Referenz lösen', + 'ui.lang': 'Sprache', + 'ui.lang.de': 'Deutsch', + 'ui.lang.en': 'Englisch', + 'ui.lang.la': 'Latein', + 'ui.lang.ja': 'Japanisch', + 'ui.lang.aria': 'Sprache wählen', + 'sb.up': 'Nach oben zum Ergebnis', + 'footer.1': '<b>Keine Steuer- oder Anlageberatung.</b> Ein Modell ist kein Vertrag. Die Rechtslage 2026 zur reformierten privaten Altersvorsorge ist an mehreren Stellen noch nicht abschließend geklärt; die Annahmen mit dem geringsten Vertrauensgrad stehen unter „Rechenweg → Belastbarkeit der Annahmen“ und sind dort auch änderbar.', + 'footer.2': 'Rechenkern eins zu eins portiert aus dem Excel-Modell <span class="pill">bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx</span> und gegen drei Testfälle auf Maschinengenauigkeit geprüft (größte relative Abweichung 1,3·10<sup>−14</sup>).', + + /* ---------------- Die drei Optionen ---------------- */ + 'opt.bav.name': 'bAV (Entgeltumwandlung)', + 'opt.bav.short': 'bAV', + 'opt.bav.nom': 'die betriebliche Altersvorsorge', + 'opt.bav.satz': 'Die betriebliche Altersvorsorge', + 'opt.bav.gen': 'der bAV', + 'opt.av.name': 'Altersvorsorgedepot', + 'opt.av.short': 'AV-Depot', + 'opt.av.nom': 'das Altersvorsorgedepot', + 'opt.av.satz': 'Das Altersvorsorgedepot', + 'opt.av.gen': 'des Altersvorsorgedepots', + 'opt.priv.name': 'privates ETF-Depot', + 'opt.priv.short': 'privat', + 'opt.priv.nom': 'das private ETF-Depot', + 'opt.priv.satz': 'Das private ETF-Depot', + 'opt.priv.gen': 'des privaten ETF-Depots', + + /* ---------------- Kennzahlen ---------------- */ + 'metric.monat.label': 'Monatsleistung', + 'metric.monat.unit': '€/Monat', + 'metric.monat.desc': 'Konstante Monatszahlung über die gesamte Auszahlungsphase mit demselben Wert, in heutiger Kaufkraft. Die einzige Zahl, die sich unmittelbar mit Ihrem heutigen Nettoeinkommen vergleichen lässt.', + 'metric.ertrag.label': 'Netto-Ertrag (Barwert)', + 'metric.ertrag.unit': '€', + 'metric.ertrag.desc': 'Was die Option in heutiger Kaufkraft über den eigenen Einsatz hinaus abwirft: Barwert aller Netto-Leistungen minus Barwert des Nettoaufwands.', + 'metric.irr.label': 'Interner Zinsfuß', + 'metric.irr.unit': '% p.a.', + 'metric.irr.desc': 'Rendite auf den tatsächlich eingesetzten Nettobetrag, nominal. Direkt mit der eingestellten Bruttorendite des ETF vergleichbar — die Differenz ist genau das, was Förderung und Abgaben zusammen kosten oder bringen.', + 'metric.kapital.label': 'Kapital bei Rentenbeginn', + 'metric.kapital.unit': '€', + 'metric.kapital.desc': 'Vor Steuern und vor Sozialabgaben, nominal. Bewusst NICHT die Entscheidungsgröße: die bAV führt hier oft, verliert aber in der Auszahlung.', + 'metric.pa': ' p.a.', + 'metric.pp': ' Pp.', + 'metric.short.monat': 'Monat', + 'metric.short.ertrag': 'Ertrag', + 'metric.short.irr': 'IRR', + 'metric.short.kapital': 'Kapital', + + /* ---------------- Eingabefelder, Rahmenwerk ---------------- */ + 'unit.pct': '%', + 'unit.eur': '€', + 'unit.eurm': '€/Monat', + 'ui.hintFor': function(p){ return 'Erklärung zu: ' + p.label; }, + 'ui.couple': '<span>⇅</span> Zwei Sichten auf dieselbe Größe. Ändern Sie eine, folgt die andere.', + 'ui.cnt.legal': function(p){ return p.n + ' Rechengrößen'; }, + 'ui.cnt.fields': function(p){ return p.n + ' Felder'; }, + 'ui.legalNote': 'Gesetzliche Größen mit Fundstelle. Nur ändern, wenn sich das Recht geändert hat oder Ihre Kasse abweicht.', + 'group.bav': 'Zuschuss und Auszahlungsformen im Detail', + 'group.person': 'Zeitachse, Ausscheiden, Teilzeit', + 'group.steuer': 'Steuer, Veranlagung, Kinder', + 'group.markt': 'Kosten und Inflation', + 'group.ruhe': 'Ruhestand', + 'group.p_sv': 'Rechengrößen: Sozialversicherung 2026', + 'group.p_est': 'Rechengrößen: Einkommensteuertarif 2026 (§ 32a EStG)', + 'group.p_kap': 'Rechengrößen: Kapitalertrag- und Investmentsteuer', + 'group.p_bav': 'Rechengrößen: betriebliche und geförderte private Vorsorge', + + /* ---------------- Live-Notizen an den Feldern ---------------- */ + 'n.beitrag.ueber8': function(p, f){ + return 'Über 8 % der Beitragsbemessungsgrenze RV (' + f.eur(p.v8) + ' im Jahr). Der Teil darüber ist ' + + 'weder steuer- noch sozialabgabenbegünstigt — er wandert in den Versicherungsmantel und holt ' + + 'sich dort nur die Beitragspflicht in der Krankenversicherung ab.'; }, + 'n.beitrag.ueber4': function(p, f){ + return 'Über 4 % der Beitragsbemessungsgrenze RV (' + f.eur(p.v4) + ' im Jahr). Bis hierhin haben Sie ' + + 'einen Rechtsanspruch nach § 1a BetrAVG, darüber braucht es die Zustimmung des Arbeitgebers.'; }, + 'n.agzus.gedeckelt': function(p, f){ + return 'Gedeckelt bei ' + f.pct(p.quote, 1) + '. Der Arbeitgeber spart tatsächlich nur ' + + f.eur2(p.ersparnis) + ' im Jahr — höhere Werte ändern hier nichts, solange der Zuschussmodus ' + + '„spitz gerechnet“ bleibt.'; }, + 'n.agz_fix.fliesstNicht': function(p, f){ + return 'Fließt nicht: Ihr Monatsbeitrag von ' + f.eur2(p.beitrag) + ' liegt unter der ' + + 'Mindestschwelle von ' + f.eur2(p.schwelle) + ' (Feld unter „Weitere Angaben“).'; }, + 'n.agz_fix.ueber4': function(p, f){ + return 'Der feste Zuschuss allein überschreitet den 4-%-Topf (' + f.eur(p.topf) + + ' im Jahr) um ' + f.eur(p.ueber) + '. Dieser Teil wäre bei Ihnen beitragspflichtiges ' + + 'Arbeitsentgelt — das Modell rechnet die zusätzliche Beitragslast NICHT gegen und ' + + 'überschätzt die bAV insoweit.'; }, + 'n.agz_fix.verdraengung': function(p, f){ + return 'Belegt den 4-%-Topf vorrangig: von Ihrem eigenen Beitrag bleiben nur noch ' + + f.eur(p.rest) + ' sozialabgabenfrei. Ihr Nettoaufwand steigt dadurch — der Zuschuss ' + + 'ist trotzdem geschenktes Geld, er verschiebt nur die Grenze der Eigenförderung.'; }, + 'n.agz_fix.ueber8': 'Zusammen mit dem Eigenbeitrag über 8 % der BBG-RV — der Überhang ist weder steuer- noch beitragsbegünstigt.', + 'n.agz_fix_min.knapp': function(p, f){ + return 'Sie liegen nur ' + f.eur2(p.d) + ' über der Schwelle. Darunter fällt der feste Zuschuss ' + + 'komplett weg: die Grenzrendite des letzten Beitragseuros ist an dieser Stelle unendlich, ' + + 'die Kopplung Brutto ↔ Netto dort unstetig.'; }, + 'n.agz_fix_min.verfehlt': function(p, f){ + return 'Schwelle nicht erreicht — ' + f.eur2(p.fehlt) + ' mehr im Monat lösen ' + f.eur2(p.zuschuss) + + ' Zuschuss aus. Das ist fast immer der lohnendste Euro im ganzen Modell.'; }, + 'n.teilkap.kosten': function(p, f){ + return 'Kostet ' + (p.gewinn ? 'nichts, im Gegenteil: ' + p.dtxt : p.dtxt + ' ' + p.unit) + + ' gegenüber reiner Verrentung. Grund: der Einmalbetrag ist im Zuflussjahr voll ' + + 'steuerpflichtig (Grenzbelastung dort ' + f.pct(p.grenz, 1) + ') und verzinst sich danach nur ' + + 'noch mit dem Satz nach Abgeltungsteuer statt mit der Produktrendite.'; }, + 'n.av_auszform.horizont': function(p, f){ + return 'Über den Vergleichshorizont hinaus zahlt diese Rente noch ' + f.eur(p.rest) + + ', die hier NICHT mitgezählt werden. Ein fester Horizont unterschätzt jede lebenslange ' + + 'Rente systematisch — wer alt wird, gewinnt hier.'; }, + + /* ---------------- Ergebniskarten ---------------- */ + 'card.lead': 'vorn', + 'card.unchanged': 'unverändert', + 'base.pinned': 'Δ gegenüber dem gemerkten Stand', + 'base.delta': function(p){ return 'Δ ' + p.desc; }, + 'base.none': 'Bewegen Sie einen Regler — die Änderung erscheint hier als Δ.', + 'base.desc.prev': 'vorheriger Stand', + 'base.desc.pinned': 'gemerkter Stand', + 'base.desc.change': function(p){ return 'vor der Änderung von „' + p.label + '“'; }, + 'rank.swap': function(p, f){ + return '<b>Die Kennzahlen sind sich uneinig.</b> Nach Monatsleistung liegt ' + + p.a + ' vor ' + p.b + ', nach Barwert des Netto-Ertrags ist es umgekehrt. ' + + 'Grund: der Barwert zinst mit der Inflation ab (' + f.pct(p.infl, 2) + '), die Monatsleistung ' + + 'verrentet mit dem Wiederanlagezins nach Steuern (' + f.pct(p.wiederanl, 2) + '). Je weiter beide ' + + 'auseinanderliegen, desto besser steht eine frühe Kapitalauszahlung da. Welche Kennzahl gilt, hängt ' + + 'davon ab, ob Sie das Geld anlegen oder ausgeben würden — bei „ausgeben“ den Wert im Feld ' + + '„Kosten privates ETF-Depot“ so setzen, dass der Wiederanlagezins gegen null geht.'; }, + 'warn.unreachable': 'Dieser Nettoaufwand ist nicht erreichbar — er übersteigt Ihr Gehalt. Eingestellt wurde der höchste mögliche Wert.', + 'warn.inactive': 'Im ersten Jahr sind Sie nach Ihren Angaben nicht mehr erwerbstätig — es gibt keine Entgeltumwandlung, also auch keinen Nettoaufwand, zu dem sich ein Bruttobeitrag zurückrechnen ließe. Prüfen Sie „Ausscheiden aus dem Job“ und „Alter heute“.', + + /* ---------------- Klartext-Fazit ---------------- */ + 'verdict.lead': function(p){ + return '<b>' + p.satz + '</b> liegt vorn mit ' + p.wert + ' — ' + p.d2 + ' mehr als ' + p.nom2 + + (p.nom3 ? ' und ' + p.d3 + ' mehr als ' + p.nom3 : '') + '.'; }, + 'verdict.pp': function(p){ return p.v + ' Prozentpunkte'; }, + 'verdict.top': function(p, f){ + return ' Den größten Einzelposten im Abstand ' + p.gen + ' zum privaten ETF-Depot bildet ' + + '„' + p.label + '“ — und zwar ' + (p.pro ? 'zugunsten' : 'zulasten') + ' ' + p.gen + + ', mit ' + f.eur(p.wert) + ' Barwert. Die vollständige Zerlegung steht unter „Werttreiber“.'; }, + 'verdict.band': function(p, f){ + return '<span class="band">Wie belastbar ist das? Bei einer Bruttorendite von ' + f.pct(p.lo, 1) + + ' statt ' + f.pct(p.mid, 1) + ' wären es ' + p.vlo + ', bei ' + f.pct(p.hi, 1) + ' wären es ' + + p.vhi + ' — die Reihenfolge ' + + (p.stabil ? 'bleibt in dieser Spanne dieselbe.' + : '<b>kehrt sich in dieser Spanne um</b>, das Ergebnis trägt hier also nicht.') + + '</span>'; }, + 'demo.text': '<b>Das sind noch Beispielwerte</b> — 40 Jahre alt, 75.000 € Jahresbrutto, 300 € Entgeltumwandlung im Monat, gesetzlich krankenversichert. Tragen Sie unter „Ihre Angaben“ Ihre eigenen ein; dieser Hinweis verschwindet dann von selbst.', + + /* ---------------- Mitlaufende Leiste ---------------- */ + 'sb.cap': function(p){ return p.label + ' · ' + p.unit + (p.kaufkraft ? ', heutige Kaufkraft' : ''); }, + 'sb.aria': function(p){ return 'Ergebnis: ' + p.label; }, + + /* ---------------- Diagramme ---------------- */ + 'tab.sens': 'Sensitivität', + 'tab.wf': 'Werttreiber', + 'tab.kap': 'Kapitalverlauf', + 'tab.zer': 'Monatszerlegung', + 'chart.error': function(p){ return 'Diagramm nicht darstellbar: ' + p.msg; }, + 'sens.readout': 'Gestrichelt: Ihr aktueller Wert. Ringe: Schnittpunkte mit dem Privatdepot. Über der Kurve fahren oder tippen für Werte.', + 'sens.legendY': function(p){ return 'y: ' + p.label + ' (' + p.unit + ')'; }, + 'sens.note': function(p){ + return 'Der zuletzt bewegte Regler wird über seine ganze Spanne durchgerechnet — ' + p.n + + ' vollständige Modellläufe. Aktuell: „' + p.label + '“. ' + + 'Bewegen Sie einen anderen Regler, wechselt die Kurve mit.'; }, + 'sens.noteNetto': function(p, f){ + return ' Achtung: dieser Regler verändert auch den Nettoaufwand (hier ' + f.eur2(p.lo) + ' bis ' + + f.eur2(p.hi) + ' im Monat) und damit den Betrag, der in allen drei Optionen investiert wird. ' + + 'Deshalb sind die drei Kurven bei jedem einzelnen x fair vergleichbar, das Niveau zwischen ' + + 'verschiedenen x aber nicht. Die Knicke sitzen an den Beitragsbemessungsgrenzen (' + + f.eur(p.bbgkv) + ' für KV/PV, ' + f.eur(p.bbgrv) + ' für RV/AV) und an den Tarifeckwerten.'; }, + 'wf.up': 'hebt den Ertrag', + 'wf.down': 'senkt den Ertrag', + 'wf.legend': 'Barwert des Netto-Ertrags, heutige Kaufkraft', + 'wf.check': function(p, f){ + return 'Probe: ' + f.eur(p.start) + ' + ' + f.eur(p.summe) + ' = ' + f.eur(p.start + p.summe) + + ' (Soll ' + f.eur(p.ende) + ', Abweichung ' + f.nf2(p.start + p.summe - p.ende) + ' €).'; }, + 'wf.note': 'Teleskopierende Zerlegung, deshalb geht sie exakt auf — aber sie ist <b>reihenfolgeabhängig</b>: die Effekte sind nicht additiv separierbar, jeder Balken misst den Beitrag <i>gegeben</i> die darüberliegenden. Tippen Sie einen Balken an für die Herleitung.', + 'wf.barinfo': function(p){ return p.label + ': ' + p.note; }, + 'wfb.vp.label': 'Wegfall der Vorabpauschale', + 'wfb.vp.note': 'Weder bAV noch Altersvorsorgedepot kennen die Vorabpauschale (§ 18 InvStG). Gemessen als Privatdepot ohne Vorabpauschale minus mit. Der Balken kann negativ sein: die Vorabpauschale läuft zu großen Teilen in den Sparer-Pauschbetrag, erhöht aber in voller Höhe die Kostenbasis — dann ist sie für das Privatdepot netto ein Vorteil.', + 'wfb.zul.label': 'Zulagen und Sonderausgabenabzug', + 'wfb.zul.note': 'Fremdgeld nach dem neuen Zulagenmodell plus der Abzug nach § 10a EStG. Gemessen durch Abschalten beider Förderwege.', + 'wfb.arch.label': 'Besteuerungsarchitektur (Rest)', + 'wfb.arch.note': 'Tarifarbitrage t₀ − t₁ und alles Übrige. Residual bestimmt, damit die Kette exakt aufgeht.', + 'wfb.agz.label': 'Arbeitgeberzuschuss', + 'wfb.agz.note': 'Fremdgeld — der einzige strukturelle Vorteil der bAV, der nicht aus dem Steuerrecht kommt. Gemessen durch Abschalten des Zuschusses.', + 'wfb.tarif.label': 'Tarifarbitrage und Rest', + 'wfb.tarif.note': 'Grenzsteuersatz beim Einzahlen minus beim Auszahlen, plus alles Übrige. Residual bestimmt, damit die Kette exakt aufgeht. Steueraufschub allein ist wertlos: (1−t)(1+r)ⁿ = (1+r)ⁿ(1−t), Multiplikation kommutiert.', + 'wfb.kvpv.label': 'KV/PV auf Versorgungsbezüge', + 'wfb.kvpv.note': '§§ 229, 250 SGB V: den vollen Beitragssatz trägt der Rentner allein. Gemessen als Differenz zur Einstellung „PKV im Ruhestand“.', + 'wfb.ep.label': 'Verlust an Entgeltpunkten', + 'wfb.ep.note': 'Entgeltumwandlung mindert das beitragspflichtige Entgelt und damit die gesetzliche Rente. Netto gegengerechnet.', + 'kap.rentbeg': 'Rentenbeginn', + 'kap.swap': 'Kapital → Rentenanspruch', + 'kap.legend': 'durchgezogen: gesamt · gestrichelt: frei verfügbar', + 'kap.readout': function(p, f){ + return 'Am Rentenbeginn gebunden: bAV ' + f.eur(p.bavGeb) + ' von ' + f.eur(p.bav) + + ' · AV-Depot ' + f.eur(p.avGeb) + ' von ' + f.eur(p.av) + + ' · privates Depot ' + f.eur(0) + ' von ' + f.eur(p.priv) + + '. Die äquivalente Monatsleistung wird verbraucht, deshalb endet jede Option bei null.'; }, + 'kap.note': function(p, f){ + return 'Kontostand im Produkt, in der Auszahlungsphase zuzüglich der netto wieder angelegten ' + + 'Auszahlungen (' + f.pct(p.wiederanl, 2) + ' nach Steuern) und abzüglich der äquivalenten ' + + 'Monatsleistung, die tatsächlich verbraucht wird. Deshalb läuft jede Option punktgenau auf null. ' + + 'Weil der Verbrauch nur aus der Wiederanlage abgeht und das Produktkapital im Produkt bleibt, wo es ' + + 'mit der Produktrendite vor Steuern wächst, verlaufen die Kurven trotzdem nicht proportional. ' + + '— Die gestrichelte Linie ist der frei verfügbare Teil: die bAV ist bis zum Rentenbeginn ' + + 'vollständig gebunden (§ 1b BetrAVG kennt Unverfallbarkeit, nicht Verfügbarkeit), beim ' + + 'Altersvorsorgedepot ist eine Entnahme vor 65 förderschädlich und frei nur der Überschuss über den ' + + 'Jahreshöchstbetrag, das Privatdepot ist jederzeit frei. Das ist die einzige Achse, auf der eine ' + + 'Kapitalauszahlung gut aussieht. ' + + (p.verrentet ? 'Weil Sie das Depot verrenten, geht auch dort das Kapital an den Anbieter — die ' + + 'Kurve fällt entsprechend, obwohl nichts verloren ist. ' : '') + + 'Und noch einmal ausdrücklich: die Kurven zeigen das Vermögen aus DIESER einen Entscheidung, ' + + 'nicht Ihr Gesamtvermögen.'; }, + 'zer.netto': 'netto', + 'zer.tax': 'Steuer', + 'zer.kv': 'KV/PV', + 'zer.pension': 'Rente', + 'zer.legend': '<span><i style="background:#808080"></i>Steuer</span><span><i style="background:#800000"></i>KV/PV</span><span><i style="background:#800080"></i>entgangene gesetzliche Rente</span><span style="margin-left:auto">farbiger Sockel = das, was übrig bleibt</span>', + 'zer.readout': 'Alles als konstante Monatsleistung in heutiger Kaufkraft. Weil Annuitisierung linear ist, addieren sich die Blöcke exakt.', + 'zer.note': function(p, f){ + return 'Der Bezugszeitraum ist für alle drei Optionen identisch. Eine Kapitalauszahlung wird nicht ' + + 'durch zwölf geteilt, sondern mit dem Wiederanlagezins nach Steuern (' + f.pct(p.wiederanl, 2) + + ' p.a.) auf denselben Zeitraum verrentet. Wer das Kapital verkonsumiert statt anzulegen, steht ' + + 'schlechter.'; }, + + /* ---------------- Rechenweg ---------------- */ + 'det.h.bav': 'bAV', + 'det.h.av': 'AV-Depot', + 'det.h.priv': 'privat', + 'det.h.kennzahl': 'Kennzahl', + 'det.h.wert': 'Wert', + 'det.erg.netto': 'Summe eingezahlt aus dem Netto (nominal)', + 'det.erg.fremd': 'Fremdgeld (Zuschuss bzw. Zulagen)', + 'det.erg.kapital': 'Kapital bei Auszahlungsbeginn', + 'det.erg.brutto': 'Summe Bruttoauszahlungen', + 'det.erg.steuer': 'davon Einkommen-/Abgeltungsteuer', + 'det.erg.kvpv': 'davon Kranken- und Pflegeversicherung', + 'det.erg.rentenverlust': 'Verlust an gesetzlicher Rente (kumuliert)', + 'det.erg.nettoausz': 'Summe Netto-Auszahlungen (nominal)', + 'det.erg.bwausz': 'Barwert der Netto-Auszahlungen', + 'det.erg.bwaufw': 'Barwert des Nettoaufwands', + 'det.erg.ertrag': 'Barwert des Netto-Ertrags', + 'det.erg.verhaeltnis': 'Verhältnis Ertrag zu Aufwand', + 'det.erg.irr': 'Interner Zinsfuß (nominal)', + 'det.erg.vorsprung': 'Vorsprung gegenüber dem Privatdepot', + 'det.mon.brutto': 'Bruttoleistung pro Monat', + 'det.mon.steuer': 'abzüglich Einkommen-/Abgeltungsteuer', + 'det.mon.kvpv': 'abzüglich Kranken- und Pflegeversicherung', + 'det.mon.ep': 'abzüglich Verlust an gesetzlicher Rente', + 'det.mon.netto': 'NETTO PRO MONAT (heutige Kaufkraft)', + 'det.diag.beitrag': 'Bruttobeitrag bAV pro Monat', + 'det.diag.aufwand': 'Nettoaufwand pro Monat (erstes Jahr)', + 'det.diag.grenzsteuer': 'Grenzsteuersatz in der Ansparphase (erstes Jahr)', + 'det.diag.ersparnis': 'Ersparnisquote gesamt (Steuer + SV), erstes Jahr', + 'det.diag.svAG': 'tatsächliche SV-Ersparnis des Arbeitgebers', + 'det.diag.svAN': 'tatsächliche SV-Ersparnis des Arbeitnehmers', + 'det.diag.ngef': 'Anteil der bAV-Beiträge ohne Förderung (erstes Jahr)', + 'det.diag.aussch': 'Ausschöpfung des Rechtsanspruchs (4 % BBG-RV)', + 'det.diag.agzVar': 'davon Arbeitgeberzuschuss: prozentual (erstes Jahr)', + 'det.diag.agzFix': 'davon Arbeitgeberzuschuss: fester Betrag (erstes Jahr)', + 'det.diag.agzFixUeber4': 'fester Zuschuss über dem 4-%-Topf (nicht als Beitragslast erfasst)', + 'det.diag.vbMonat': 'Versorgungsbezug pro Monat zu Auszahlungsbeginn', + 'det.diag.freibetrag': 'Freibetrag Versorgungsbezüge im selben Jahr', + 'det.diag.vbQuote': 'Verhältnis Versorgungsbezug zu Freibetrag', + 'det.diag.kapFreiKapital': 'beitragsfrei mögliches Kapital (Kapitalauszahlung)', + 'det.diag.kapFreiRente': 'beitragsfrei mögliches Kapital (lebenslange Rente)', + 'det.diag.memoRest': 'MEMO: bAV-Rentenzahlungen nach dem Horizont', + 'det.diag.memoRestAv': 'MEMO: Depotrente nach dem Horizont', + 'det.diag.wiederanl': 'Wiederanlagezins nach Steuern', + 'det.diag.kapstEff': 'effektive Abgeltungsteuer inkl. Soli/KiSt', + 'det.diag.bestAnteil': 'Besteuerungsanteil der gesetzlichen Rente', + 'det.warn.lead': 'Sie liegen außerhalb des begünstigten Korridors: ', + 'det.warn.aussch': 'Über 100 % Ausschöpfung besteht kein Rechtsanspruch auf Entgeltumwandlung mehr. ', + 'det.warn.ngef': 'Ein spürbarer Teil der Beiträge ist weder steuer- noch sozialabgabenbegünstigt — diese Schicht wandert in einen Versicherungsmantel, ohne dafür etwas zu bekommen außer der Beitragspflicht in der KV. ', + 'det.warn.vb': 'Der Versorgungsbezug übersteigt den Freibetrag deutlich; die Schutzwirkung des absoluten Freibetrags fällt wie 1/Kapital. ', + 'det.anspar.h.jahr': 'Jahr', + 'det.anspar.h.brutto': 'Brutto', + 'det.anspar.h.beitrag': 'bAV-Beitrag', + 'det.anspar.h.agzVar': 'AG-Zuschuss %', + 'det.anspar.h.agzFix': 'AG fest', + 'det.anspar.h.sv': 'SV-Erspar.', + 'det.anspar.h.st': 'Steuer-Erspar.', + 'det.anspar.h.netto': 'Nettoaufwand', + 'det.anspar.h.zul': 'Zulagen', + 'det.anspar.h.kbav': 'Kapital bAV', + 'det.anspar.h.kav': 'Kapital AV', + 'det.anspar.h.kpriv': 'Kapital privat', + 'det.row.year': function(p){ return p.jahr + ' (Alter ' + p.alter + ')'; }, + 'det.ausz.h.jahr': 'Jahr', + 'det.ausz.h.bavBr': 'bAV brutto', + 'det.ausz.h.kvpv': 'KV/PV', + 'det.ausz.h.steuer': 'Steuer', + 'det.ausz.h.mind': 'Rentenminderung', + 'det.ausz.h.bavNet': 'bAV netto', + 'det.ausz.h.avBr': 'AV brutto', + 'det.ausz.h.avNet': 'AV netto', + 'det.ausz.h.privBr': 'privat brutto', + 'det.ausz.h.privNet': 'privat netto', + 'det.ann.intro': 'Die Rangfolge hängt an Rechtsfragen, die 2026 noch nicht alle geklärt sind. Die Zahlen in Klammern sind subjektive Vertrauensgrade.', + 'det.ann.h.annahme': 'Annahme', + 'det.ann.h.vertrauen': 'Vertrauen', + 'det.ann.h.anmerkung': 'Anmerkung', + 'det.ann.r1.a': 'Rechengrößen SV 2026, § 32a-Tarif 2026', + 'det.ann.r1.c': 'SVBezGrV 2026, Steuerfortentwicklungsgesetz', + 'det.ann.r2.a': 'KV/PV-Pflicht auf Versorgungsbezüge', + 'det.ann.r2.c': '§§ 229, 250 SGB V', + 'det.ann.r3.a': 'Freibetrag § 226 Abs. 2 S. 2 SGB V (KV)', + 'det.ann.r3.c': 'unstreitig', + 'det.ann.r4.a': 'Freigrenze statt Freibetrag in der PV', + 'det.ann.r4.c': 'die häufigste Fehlerquelle in Ratgebertexten', + 'det.ann.r5.a': 'Beitragsfreiheit der AV-Depot-Auszahlung', + 'det.ann.r5.c': 'keine Versorgungsbezüge', + 'det.ann.r6.a': 'halber Unterschiedsbetrag für nicht geförderte Schichten', + 'det.ann.r6.c': '§ 22 Nr. 5 S. 2 Buchst. b i.V.m. § 20 Abs. 1 Nr. 6 S. 2 EStG', + 'det.ann.r7.a': 'Zuschuss nur bis zur tatsächlichen AG-Ersparnis', + 'det.ann.r7.c': 'Gegenauffassung liest 15 % als reine Pauschale', + 'det.ann.r8.a': 'Sonderausgaben-Höchstbetrag 2.340 €', + 'det.ann.r8.c': 'MODELLANNAHME. Entscheidet, ob ein Kind den Wert des AV-Depots hebt oder senkt', + 'det.ann.r9.a': 'KVdR-Pflichtversicherung im Ruhestand unterstellt', + 'det.ann.r9.c': 'freiwillig Versicherte nicht geprüft', + 'det.ann.notmodelled': '<b>Nicht modelliert:</b> Abschläge bei vorzeitigem Rentenbezug, Wirkung der Entgeltumwandlung auf Kranken-, Arbeitslosen- und Elterngeld, Hinterbliebenenversorgung, Insolvenzsicherung (PSVaG), Portabilität bei Arbeitgeberwechsel, Kinderabschläge in der PV, Günstigerprüfung nach § 32d Abs. 6 EStG, Pfändungs- und Grundsicherungsschutz, Langlebigkeitsrisiko jenseits des Horizonts.', + 'det.prinzip': '<p>Ein Vergleich von <i>Bruttobeiträgen</i> wäre unfair: Entgeltumwandlung kommt aus dem Brutto, privates Sparen aus dem Netto. Deshalb wird der <b>Nettoaufwand</b> gleichgesetzt:</p>' + + '<p style="font-family:var(--mono);font-size:.82rem;background:var(--chip);padding:9px 11px;border-radius:9px">Nettoaufwand = Bruttobeitrag − Lohnsteuerersparnis − ersparter AN-Anteil zur Sozialversicherung</p>' + + '<p>Genau dieser Betrag wird in allen drei Optionen investiert. Was übrig bleibt, ist ein Vergleich der <i>Förderarchitekturen</i>, nicht der Einzahlungshöhen.</p>' + + '<p><b>Steueraufschub ist für sich genommen wertlos:</b> (1−t)·(1+r)ⁿ = (1+r)ⁿ·(1−t). Multiplikation kommutiert. Es bleiben nur vier Werttreiber:</p>' + + '<ol style="padding-left:1.2em;margin:.3em 0"><li>Tarifarbitrage t₀ − t₁</li>' + + '<li>Fremdgeld — Arbeitgeberzuschuss bzw. Zulagen</li>' + + '<li>steuerfreie Thesaurierung — keine Vorabpauschale</li>' + + '<li>neue Abgaben, die nur eine Option treffen — KV/PV auf Versorgungsbezüge</li></ol>' + + '<p>Punkt 4 ist der dominante Nachteil der bAV, Punkt 2 ihr einziger struktureller Vorteil. Die Rendite der bAV <b>fällt streng monoton in der Beitragshöhe</b>, weil der Freibetrag absolut ist und seine Schutzwirkung wie 1/K abnimmt — es gibt kein inneres Optimum.</p>' + + '<p>Bei Vorgabe des Nettoaufwands wird der Bruttobeitrag per Bisektion zurückgerechnet: N(B) ist stetig und streng monoton wachsend (dN/dB = 1 − Grenz-SV-Satz − Grenzsteuersatz > 0), wegen der Knicke bei 4 % und 8 % der BBG-RV, an den Beitragsbemessungsgrenzen und an den Tarifeckwerten aber nicht geschlossen invertierbar. 34 Halbierungen liefern unter einem Zehntelcent.</p>', + 'det.t.prinzip': 'Das Vergleichsprinzip', + 'det.t.erg': 'Ergebnis im Detail', + 'det.s.erg': 'Alle drei Optionen kosten in der Ansparphase exakt denselben Betrag aus dem Netto.', + 'det.t.mon': 'Monatliche Leistung, zerlegt', + 'det.s.mon': 'Alle Optionen über denselben Zeitraum verrentet, in heutiger Kaufkraft. Weil Annuitisierung linear ist, addieren sich die Zeilen exakt.', + 'det.t.diag': 'Diagnose', + 'det.s.diag': 'Woran es liegt — und wo Sie außerhalb des begünstigten Korridors stehen.', + 'det.t.anspar': 'Ansparphase, Jahr für Jahr', + 'det.t.ausz': 'Auszahlungsphase, Jahr für Jahr', + 'det.t.ann': 'Belastbarkeit der Annahmen', + + /* ---------------- Speichern und weitergeben ---------------- */ + 'msg.linkCopied': 'Link kopiert. Wer ihn öffnet, sieht genau diese Zahlen — nichts davon läuft über einen Server.', + 'msg.linkFailed': 'Kopieren ging nicht. Der Link steht jetzt in der Adresszeile.', + 'msg.loaded': 'Geladen.', + 'msg.loadError': function(p){ return 'Die Datei ließ sich nicht lesen: ' + p.msg; }, + 'msg.reset': 'Zurückgesetzt auf den Basisfall.', + + /* ---------------- PWA ---------------- */ + 'pwa.offline': '<b>Offlinefähig.</b> Die App liegt jetzt im Browser-Cache und läuft auch ohne Verbindung. Zum Installieren: auf dem iPhone „Teilen“ → „Zum Home-Bildschirm“, auf Android das Menü → „App installieren“.', + 'pwa.installAsk': '<b>Als App installieren?</b> Dann liegt sie mit eigenem Symbol auf dem Startbildschirm und läuft offline. ', + 'pwa.installBtn': 'Installieren', + 'pwa.installed': '<b>Installiert.</b> Sie finden die App jetzt unter Ihren übrigen Apps.', + 'pwa.declined': 'Alles klar — die App läuft auch so, und zwar offline.', + 'pwa.file': '<b>Sie haben die Datei direkt geöffnet</b> (<kbd>file://</kbd>). Alles rechnet, alles wird gespeichert. Nur die beiden PWA-Eigenschaften fehlen: kein Symbol auf dem Startbildschirm und kein Offline-Cache — den bräuchte es hier auch nicht, die Datei liegt ja bereits auf dem Gerät. Für die volle App-Erfahrung den Ordner auf einen beliebigen HTTPS-Webspace legen und von dort öffnen.' +}; diff --git a/app/i18n.en.js b/app/i18n.en.js new file mode 100644 index 0000000..5e70eb1 --- /dev/null +++ b/app/i18n.en.js @@ -0,0 +1,575 @@ +/* ===================================================================== + Dictionary English. Same key set as i18n.de.js; anything missing here + falls back to the German entry at runtime. + Statutory citations keep their German designations on purpose — they + are the primary sources and have no official English equivalent. + ===================================================================== */ +window.I18N_EN = { + 'f.I:gehalt.label': 'Gross annual salary today', + 'f.I:gehalt.hint': 'Excluding one-off payments, including a pro-rated 13th month if applicable. Determines your marginal tax rate and whether you are above the social-security contribution ceilings.', + 'f.I:beitrag.label': 'Monthly bAV contribution, gross', + 'f.I:beitrag.hint': 'Gross salary sacrifice under § 1a BetrAVG. You have a statutory entitlement only up to 4 % of the pension-insurance contribution ceiling (BBG-RV); above that your employer has to agree.', + 'f.I:netto_ziel.label': 'Monthly net outlay', + 'f.I:netto_ziel.hint': 'The amount by which your disposable income falls in the first year — the consumption actually given up, and therefore the economically correct decision variable. Exactly this amount is invested in all three options; that is the condition for a fair comparison. It is in one-to-one correspondence with the gross contribution above: N(B) = B − social-security saving − tax saving is strictly increasing, hence invertible. It has no closed-form inverse because of the kinks at the contribution ceilings and the tariff break points, so 34 bisections are used — accuracy below a tenth of a cent.', + 'f.I:agzus.label': 'Employer top-up', + 'f.I:agzus.hint': 'Statutory minimum 15 % (§ 1a Abs. 1a BetrAVG); many employers pay more. Break-even against the private account is roughly where (1+z)·(1−0.211) ≈ 1, i.e. z ≈ 26.7 % — check it on the sensitivity curve. In top-up mode „actual saving“ this value is often unreachable because the top-up saturates first.', + 'f.I:agz_fix.label': 'Additional flat top-up', + 'f.I:agz_fix.hint': 'A flat employer contribution independent of your own — customary at Deutsche Bahn and under many collective agreements, there on top of the 15 % under § 1a Abs. 1a BetrAVG. It is an employer-financed contribution under § 3 Nr. 63 EStG and takes PRIORITY in the 8 % tax-free allowance and the 4 % social-security exemption (§ 1 Abs. 1 S. 1 Nr. 9 SvEV): your own salary sacrifice is crowded out to that extent and becomes more expensive. Economically it is a floor, not a lever: it does not change the marginal return on your next euro of contribution (∂²return/∂agz_fix ∂contribution ≤ 0 — through crowding out it can even lower it), but it raises the average a lot. On the sensitivity curve it is therefore approximately affine — until the 4 % or 8 % allowance is exhausted, where it kinks.', + 'f.I:agz_fix_dyn.label': 'Index the flat top-up', + 'f.I:agz_fix_dyn.hint': 'Flat amounts are usually written in nominal terms into collective agreements and raised only in bargaining rounds — hence the default „nominal, constant“. Over 27 years at 2 % inflation a factor of 1.02⁻²⁷ ≈ 0.59 remains; if you find that too pessimistic, switch to „with salary“ and you get a real top-up growing at rate gsteig − infl.', + 'f.I:agz_fix_dyn.o0': 'nominal, constant', + 'f.I:agz_fix_dyn.o1': 'with salary', + 'f.I:agzus_sv.label': 'Top-up mode', + 'f.I:agzus_sv.hint': '„Actual saving“ is the wording of the statute: § 1a Abs. 1a BetrAVG owes the top-up only INSOFAR AS the employer actually saves social-security contributions. If your pay is above both contribution ceilings, the employer saves nothing and owes nothing. That is why the slider saturates around 21 % in the base case. „Full contribution“ is a voluntary or collectively agreed promise, „exempt part only“ the widespread flat-rate practice. Confidence in the strict reading roughly 75 %.', + 'f.I:agzus_sv.o0': 'full contribution', + 'f.I:agzus_sv.o1': 'exempt part only', + 'f.I:agzus_sv.o2': 'actual saving', + 'f.I:dyn.label': 'Index the contribution', + 'f.I:dyn.hint': 'If the contribution rises with your salary, the use of the 8 % allowance stays roughly constant over the years. If it stays nominally constant, its real value falls with inflation — and so does the net outlay that the other two options get for comparison.', + 'f.I:dyn.o1': 'with salary', + 'f.I:dyn.o0': 'nominal, constant', + 'f.I:rendite.label': 'Gross ETF return p.a.', + 'f.I:rendite.hint': 'Before costs, before taxes. The same ETF in all three options — only then are you comparing subsidy architectures rather than investment decisions.', + 'f.I:alter.label': 'Age today', + 'f.I:alter.hint': 'Start of the accumulation phase. Calendar year 0 = 2026.', + 'f.I:alter.unit': 'years', + 'f.I:rentbeg.label': 'Start of payout', + 'f.I:rentbeg.hint': 'Start of the payout phase for all three options. The Altersvorsorgedepot cannot pay out before age 65 (2026 reform).', + 'f.I:rentbeg.unit': 'years', + 'f.I:kv_ruhe.label': 'Health insurance in retirement', + 'f.I:kv_ruhe.hint': 'The decisive lever. Under statutory health insurance (GKV), occupational pensions count as Versorgungsbezüge and carry the full contribution rate (§§ 229, 250 SGB V); private provision does not. This setting alone flips the result.', + 'f.I:kv_ruhe.o1': 'statutory', + 'f.I:kv_ruhe.o2': 'private', + 'f.I:auszform.label': 'bAV payout', + 'f.I:auszform.hint': 'Lump sum: fully taxable in the year of receipt, health-insurance contributions spread over 120 months (§ 229 Abs. 1 S. 3 SGB V). An annuity stretches the benefit over the remaining lifetime and therefore shelters a multiple of the capital under the same allowance. The model knows only all or nothing; a partial lump sum from the bAV is not modelled.', + 'f.I:auszform.o1': 'lump sum', + 'f.I:auszform.o2': 'lifelong annuity', + 'f.I:teilkap.label': 'Altersvorsorgedepot: lump sum at the start', + 'f.I:teilkap.hint': 'Up to 30 % of the accumulated capital can be taken as a lump sum at the start of the payout phase — in both payout forms. It costs you twice: full progression in the year of receipt, and afterwards only the rate net of withholding tax. The excess over the annual maximum (the „sleeve“) is annuitised without a lump sum; that is a model convention, not a rule of law.', + 'f.I:av_auszform.label': 'Altersvorsorgedepot: remainder', + 'f.I:av_auszform.hint': 'What happens to the capital beyond the lump sum — independent of the bAV decision above. Payout plan: the money stays invested and is drawn down with interest; the non-subsidised layer is taxed on half the difference amount. Lifelong annuity: the capital goes to the provider, in return the non-subsidised layer is taxed only on the income share (§ 22 Nr. 5 S. 2 Buchst. a EStG, 17 % from age 67 instead of roughly 26 % here). The subsidised layer is fully taxable on a deferred basis either way; that does not change.', + 'f.I:av_auszform.o1': 'payout plan', + 'f.I:av_auszform.o2': 'lifelong annuity', + 'f.I:agz_fix_min.label': 'Minimum own contribution for the flat top-up', + 'f.I:agz_fix_min.hint': 'Matching schemes tie the flat amount to a minimum own contribution („sacrifice at least X and you get Y“). 0 means unconditional — then the top-up flows in every active year, even with a zero contribution. Mind the model semantics: a genuinely unconditional top-up would not be a differential effect of the salary-sacrifice decision and would flatter the bAV in this comparison, although it would flow anyway. Only enter what actually depends on participating.', + 'f.I:rentfak_av.label': 'Annuity factor, Altersvorsorgedepot', + 'f.I:rentfak_av.hint': 'Only if the account is annuitised. Preset as for the bAV; an account contract does without an insurance wrapper and can therefore be cheaper — enter the value from the actual offer. At 0 there is no annuity.', + 'f.I:rentfak_av.unit': '€ per 10,000 €', + 'f.I:rentdyn_av.label': 'Escalation of the account annuity p.a.', + 'f.I:rentdyn_av.hint': 'Profit participation or fund performance during the payout phase of the account contract.', + 'f.I:rentfak.label': 'Annuity factor, bAV', + 'f.I:rentfak.hint': 'Only for payout form „annuity“. 26 € monthly pension per 10,000 € corresponds to a withdrawal rate of about 3.1 %. It is stated in the product information sheet.', + 'f.I:rentfak.unit': '€ per 10,000 €', + 'f.I:rentdyn.label': 'Escalation of the bAV annuity p.a.', + 'f.I:rentdyn.hint': 'Profit participation or fund performance during the payout phase.', + 'f.I:fuenftel.label': 'Test the one-fifth rule', + 'f.I:fuenftel.hint': '§ 34 EStG applies to a scheduled lump sum from § 3 Nr. 63 contracts only exceptionally, per BFH X R 23/15. Conservative: no.', + 'f.I:fuenftel.o0': 'no (conservative)', + 'f.I:fuenftel.o1': 'yes', + 'f.I:planende.label': 'Comparison horizon to age', + 'f.I:planende.hint': 'Payout plans run until here. Lifelong annuities are compared over the same horizon; whatever flows afterwards is shown as a memo item under „Diagnostics“. Living long pays off there.', + 'f.I:planende.unit': 'years', + 'f.I:ausscheid.label': 'Leaving the job', + 'f.I:ausscheid.hint': 'No salary and no contributions from here on. The capital keeps compounding until the start of payout.', + 'f.I:ausscheid.unit': 'years', + 'f.I:tzalter.label': 'Part-time from age', + 'f.I:tzalter.hint': 'Any value at or after the start of payout means: no part-time. The default is therefore 67.', + 'f.I:tzalter.unit': 'years', + 'f.I:tzfaktor.label': 'Part-time factor', + 'f.I:tzfaktor.hint': 'Share of the full-time salary. 60 % = a 60 % position.', + 'f.I:gsteig.label': 'Salary and wage growth p.a.', + 'f.I:gsteig.hint': 'Also rolls forward the contribution ceilings, the reference figure (Bezugsgröße), average earnings and the pension value.', + 'f.I:stkl.label': 'Assessment', + 'f.I:stkl.hint': 'What matters is the ASSESSMENT TYPE, not the wage-tax class: the class only controls the withholding during the year, the annual assessment applies income splitting under § 32a Abs. 5 EStG anyway. With „joint“, be sure to enter the partner’s taxable income.', + 'f.I:stkl.o1': 'single', + 'f.I:stkl.o3': 'joint (splitting)', + 'f.I:partner.label': 'Partner’s taxable income', + 'f.I:partner.hint': 'Only for joint assessment. The model computes 2·ESt((zvE + zvE_partner)/2).', + 'f.I:kist.label': 'Church tax rate', + 'f.I:kist.hint': '8 % in Bavaria and Baden-Württemberg, otherwise 9 %, 0 if you have left the church. It also affects withholding tax: 0.25/(1+0.25k)·(1+0.055+k).', + 'f.I:tarifanp.label': 'Indexation of the tariff break points p.a.', + 'f.I:tarifanp.hint': 'The whole tariff is stretched by the factor λ = (1+x)^t: ESt = s·λ·T(zvE/s/λ). This is exactly equivalent to shifting all break points proportionally. 0 % shows full bracket creep.', + 'f.I:kinder.label': 'Number of children', + 'f.I:kinder.hint': 'Child allowance in the Altersvorsorgedepot: 1 € per € paid in, up to 300 € per child.', + 'f.I:kind_jahre.label': 'Years with child allowance', + 'f.I:kind_jahre.hint': 'The child allowance is tied to the child-benefit entitlement (§ 85 EStG) and therefore does not run over the whole accumulation phase.', + 'f.I:kind_jahre.unit': 'years', + 'f.I:kinderlos.label': 'Childless (long-term care surcharge)', + 'f.I:kinderlos.hint': '0.6 percentage points on top of the long-term care contribution, borne by the employee alone (§ 55 Abs. 3 SGB XI).', + 'f.I:kinderlos.o0': 'no', + 'f.I:kinderlos.o1': 'yes', + 'f.I:k_bav.label': 'Cost of the bAV product p.a.', + 'f.I:k_bav.hint': 'Insurance wrapper plus TER. The effective costs are in the product information sheet — look them up, do not guess.', + 'f.I:k_av.label': 'Cost of the Altersvorsorgedepot p.a.', + 'f.I:k_av.hint': 'For the standard account the effective costs are capped by law at 1.0 % p.a.', + 'f.I:k_priv.label': 'Cost of the private ETF account p.a.', + 'f.I:k_priv.hint': 'Plain TER of a broad world ETF.', + 'f.I:infl.label': 'Inflation p.a.', + 'f.I:infl.hint': 'Discount rate for everything stated „in today’s purchasing power“. It barely changes the ranking, but it changes every level.', + 'f.I:sonst_zve.label': 'Other taxable income in retirement', + 'f.I:sonst_zve.hint': 'Above all the taxable part of the statutory pension, excluding the products compared here. It determines the marginal tax rate in the payout phase — and hence the tariff arbitrage t₀ − t₁.', + 'f.I:rente_br.label': 'Gross statutory pension p.a.', + 'f.I:rente_br.hint': 'Only for the health-insurance cap: the statutory pension and Versorgungsbezüge share ONE contribution ceiling (§ 223 Abs. 3 SGB V).', + 'f.P:bbg_rv.label': 'Contribution ceiling, pension insurance (annual)', + 'f.P:bbg_rv.hint': 'SVBezGrV 2026 (BGBl. 2025 I Nr. 116), uniform nationwide.', + 'f.P:bbg_kv.label': 'Contribution ceiling, health and care insurance', + 'f.P:bbg_kv.hint': 'SVBezGrV 2026; 5,812.50 € per month.', + 'f.P:bezug.label': 'Reference figure (monthly)', + 'f.P:bezug.hint': 'SVBezGrV 2026, 47,460 € per year. One twentieth of it is the health-insurance allowance for Versorgungsbezüge (§ 226 Abs. 2 S. 2 SGB V).', + 'f.P:de_rv.label': 'Average earnings, pension insurance', + 'f.P:de_rv.hint': 'Anlage 1 to SGB VI, 2026 value — the denominator for earnings points.', + 'f.P:rw.label': 'Current pension value', + 'f.P:rw.hint': 'Rentenwertbestimmungsverordnung 2026, from 1 July 2026: 42.52 € (+4.24 %).', + 'f.P:rw.unit': '€', + 'f.P:rv_an.label': 'Pension insurance, employee share', + 'f.P:rv_an.hint': '18.6 % split in half.', + 'f.P:av_an.label': 'Unemployment insurance, employee share', + 'f.P:av_an.hint': '2.6 % split in half.', + 'f.P:kv_allg.label': 'Health insurance, general rate', + 'f.P:kv_allg.hint': '§ 241 SGB V.', + 'f.P:kv_zus.label': 'Health insurance, average supplementary rate', + 'f.P:kv_zus.hint': 'Announced by the Federal Ministry of Health; differs by fund — enter your own fund’s rate.', + 'f.P:pv_ges.label': 'Long-term care, total rate', + 'f.P:pv_ges.hint': '§ 55 SGB XI. Reductions from the second child onwards are not modelled.', + 'f.P:pv_an.label': 'Long-term care, employee share', + 'f.P:pv_an.hint': 'Different in Saxony (2.3 %).', + 'f.P:pv_kl.label': 'Long-term care surcharge for the childless', + 'f.P:pv_kl.hint': '§ 55 Abs. 3 SGB XI, borne by the employee alone.', + 'f.P:gfb.label': 'Basic allowance (end of zone 1)', + 'f.P:gfb.hint': '§ 32a Abs. 1 EStG as amended by the Steuerfortentwicklungsgesetz.', + 'f.P:e2.label': 'End of zone 2', + 'f.P:e2.hint': 'Marginal rate rises from 14 % to 23.97 %.', + 'f.P:e3.label': 'End of zone 3', + 'f.P:e3.hint': 'Marginal rate rises from 23.97 % to 42 %.', + 'f.P:e4.label': 'Start of zone 5 (top rate)', + 'f.P:e4.hint': 'From 277,826 € the rate is 45 %.', + 'f.P:a2.label': 'Coefficient a₂', + 'f.P:a2.hint': 'ESt = (a₂·y + b₂)·y with y = (zvE − basic allowance)/10,000.', + 'f.P:b2.label': 'Coefficient b₂', + 'f.P:a3.label': 'Coefficient a₃', + 'f.P:a3.hint': 'ESt = (a₃·z + b₃)·z + c₃ with z = (zvE − 17,799)/10,000.', + 'f.P:b3.label': 'Coefficient b₃', + 'f.P:c3.label': 'Constant c₃', + 'f.P:c4.label': 'Constant c₄', + 'f.P:c4.hint': 'ESt = 0.42·zvE − c₄.', + 'f.P:c5.label': 'Constant c₅', + 'f.P:c5.hint': 'ESt = 0.45·zvE − c₅.', + 'f.P:soli.label': 'Solidarity surcharge', + 'f.P:soli.hint': 'SolzG 1995.', + 'f.P:soli_fg.label': 'Solidarity surcharge exemption limit', + 'f.P:soli_fg.hint': '2026, basic tariff, applied to the assessed income tax; doubled under splitting. Plus a phase-in zone.', + 'f.P:soli_mz.label': 'Phase-in zone, marginal rate', + 'f.P:soli_mz.hint': '§ 3 Abs. 2a SolzG.', + 'f.P:an_pausch.label': 'Employee lump-sum deduction', + 'f.P:an_pausch.hint': '§ 9a S. 1 Nr. 1a EStG.', + 'f.P:sa_pausch.label': 'Special-expenses lump sum', + 'f.P:sa_pausch.hint': '§ 10c EStG.', + 'f.P:kapst.label': 'Withholding tax rate', + 'f.P:kapst.hint': '§ 32d Abs. 1 EStG.', + 'f.P:tfs.label': 'Partial exemption, equity funds', + 'f.P:tfs.hint': '§ 20 Abs. 1 Nr. 3 in conjunction with § 20 InvStG, equity share above 50 %.', + 'f.P:sparerpb.label': 'Saver’s allowance', + 'f.P:sparerpb.hint': '§ 20 Abs. 9 EStG; doubled under splitting. It absorbs a large part of the advance lump sum (Vorabpauschale) — see the waterfall.', + 'f.P:basiszins.label': 'Base rate for the advance lump sum', + 'f.P:basiszins.hint': 'BMF letter of 13 January 2026, § 18 Abs. 4 InvStG (2025: 2.53 %). Setting it to 0 means: no advance lump sum.', + 'f.P:vp_faktor.label': 'Factor for the base yield', + 'f.P:vp_faktor.hint': '§ 18 Abs. 1 S. 1 InvStG.', + 'f.P:st_frei_q.label': 'Tax-free under § 3 Nr. 63 (share of BBG-RV)', + 'f.P:st_frei_q.hint': '8 % of the BBG-RV = 8,112 € in 2026.', + 'f.P:sv_frei_q.label': 'Free of social-security contributions (share of BBG-RV)', + 'f.P:sv_frei_q.hint': '§ 1 Abs. 1 S. 1 Nr. 9 SvEV; 4 % = 4,056 € in 2026. At the same time the limit of the statutory entitlement under § 1a BetrAVG.', + 'f.P:zul_g1.label': 'Allowance, tier 1 (per € of own contribution)', + 'f.P:zul_g1.hint': 'Altersvorsorgereformgesetz, in force since May 2026, applicable from 1 January 2027.', + 'f.P:zul_s1.label': 'Limit of tier 1', + 'f.P:zul_s1.hint': 'Up to 360 € of own contribution: 50 cents per €.', + 'f.P:zul_g2.label': 'Allowance, tier 2 (per € of own contribution)', + 'f.P:zul_g2.hint': 'From 361 to 1,800 €: 25 cents per €.', + 'f.P:zul_s2.label': 'Limit of tier 2', + 'f.P:zul_s2.hint': 'Maximum subsidised own contribution; yields a maximum basic allowance of 540 €.', + 'f.P:kind_zul.label': 'Child allowance per child', + 'f.P:kind_zul.hint': '1 € per € paid in, up to 300 € per child.', + 'f.P:av_max.label': 'Maximum annual contribution', + 'f.P:av_max.hint': 'BMF FAQ on the reform of subsidised private provision, as of 5 May 2026. Anything above runs into a side track taxed like a private account.', + 'f.P:av_sa_max.label': 'Maximum special-expenses deduction, § 10a', + 'f.P:av_sa_max.hint': 'MODEL ASSUMPTION, confidence roughly 70 %. Own contribution up to 1,800 € plus allowances. This single number decides whether a child raises or lowers the value of the AV account.', + 'f.P:ertragsq.label': 'Income share (age 67)', + 'f.P:ertragsq.hint': '§ 22 Nr. 1 S. 3 Buchst. a Doppelbuchst. bb EStG, for non-subsidised annuity components.', + 'f.P:halb.label': 'Taxable share of the difference amount', + 'f.P:halb.hint': '§ 20 Abs. 1 Nr. 6 S. 2 EStG by analogy (contract over 12 years, payout after 62): half the difference amount. Confidence roughly 80 %.', + + /* ---------------- Frame, window titles, header and footer ---------------- */ + 'app.title': 'Retirement provision comparison', + 'app.meta': 'Compare occupational pension (bAV), Altersvorsorgedepot and a private ETF account at the same net outlay. Law as of 2026.', + 'hdr.title': 'Retirement provision comparison', + 'hdr.sub': 'Occupational pension, Altersvorsorgedepot and a private ETF account — at the <b>same net outlay</b> and with the same ETF. German law as of 2026.', + 'win.header': 'Retirement provision comparison', + 'win.result': 'Result', + 'win.input': 'Your figures', + 'win.chart': 'Charts', + 'win.extra': 'More settings', + 'win.calc': 'How it is calculated', + 'win.share': 'Save and share', + 'win.min': 'Minimise', + 'win.restore': 'Restore', + 'win.n': function(p){ return 'Window ' + p.n; }, + 'input.sub': 'The figures recalculate on every change. Every field explains itself via <span class="qm" style="cursor:default">?</span>.', + 'extra.sub': 'Everything here has sensible defaults. Touch it only if it applies to you.', + 'calc.sub': 'For anyone who wants to follow the arithmetic.', + 'share.sub': 'Your entries stay in this browser only (<kbd>localStorage</kbd>). Nothing is transmitted, there is no server and there are no trackers.', + 'ui.share': 'Copy link with my figures', + 'ui.export': 'Save to file', + 'ui.import': 'Load file', + 'ui.reset': 'Reset everything', + 'ui.pin.set': 'Pin as reference', + 'ui.pin.clear': 'Unpin reference', + 'ui.lang': 'Language', + 'ui.lang.de': 'German', + 'ui.lang.en': 'English', + 'ui.lang.la': 'Latin', + 'ui.lang.ja': 'Japanese', + 'ui.lang.aria': 'Choose language', + 'sb.up': 'Back up to the result', + 'footer.1': '<b>Not tax or investment advice.</b> A model is not a contract. The 2026 legal position on reformed private retirement provision is not settled in several respects; the assumptions with the lowest confidence are listed under „How it is calculated → Robustness of the assumptions“ and can be changed there.', + 'footer.2': 'Calculation core ported one-to-one from the Excel model <span class="pill">bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx</span> and checked against three test cases to machine precision (largest relative deviation 1.3·10<sup>−14</sup>).', + + /* ---------------- The three options ---------------- */ + 'opt.bav.name': 'Occupational pension (bAV, salary sacrifice)', + 'opt.bav.short': 'bAV', + 'opt.bav.nom': 'the occupational pension', + 'opt.bav.satz': 'The occupational pension', + 'opt.bav.gen': 'the occupational pension', + 'opt.av.name': 'Altersvorsorgedepot', + 'opt.av.short': 'AV account', + 'opt.av.nom': 'the Altersvorsorgedepot', + 'opt.av.satz': 'The Altersvorsorgedepot', + 'opt.av.gen': 'the Altersvorsorgedepot', + 'opt.priv.name': 'private ETF account', + 'opt.priv.short': 'private', + 'opt.priv.nom': 'the private ETF account', + 'opt.priv.satz': 'The private ETF account', + 'opt.priv.gen': 'the private ETF account', + + /* ---------------- Metrics ---------------- */ + 'metric.monat.label': 'Monthly benefit', + 'metric.monat.unit': '€/month', + 'metric.monat.desc': 'A constant monthly payment of the same amount over the entire payout phase, in today’s purchasing power. The only figure directly comparable with your net income today.', + 'metric.ertrag.label': 'Net return (present value)', + 'metric.ertrag.unit': '€', + 'metric.ertrag.desc': 'What the option yields in today’s purchasing power beyond your own stake: present value of all net benefits minus present value of the net outlay.', + 'metric.irr.label': 'Internal rate of return', + 'metric.irr.unit': '% p.a.', + 'metric.irr.desc': 'Return on the net amount actually committed, in nominal terms. Directly comparable with the gross ETF return you set — the difference is exactly what subsidies and levies cost or yield in total.', + 'metric.kapital.label': 'Capital at start of payout', + 'metric.kapital.unit': '€', + 'metric.kapital.desc': 'Before tax and before social-security contributions, nominal. Deliberately NOT the decision variable: the bAV often leads here but loses in the payout phase.', + 'metric.pa': ' p.a.', + 'metric.pp': ' pp', + 'metric.short.monat': 'month', + 'metric.short.ertrag': 'return', + 'metric.short.irr': 'IRR', + 'metric.short.kapital': 'capital', + + /* ---------------- Input fields, framework ---------------- */ + 'unit.pct': '%', + 'unit.eur': '€', + 'unit.eurm': '€/month', + 'ui.hintFor': function(p){ return 'Explanation of: ' + p.label; }, + 'ui.couple': '<span>⇅</span> Two views of the same quantity. Change one and the other follows.', + 'ui.cnt.legal': function(p){ return p.n + ' statutory figures'; }, + 'ui.cnt.fields': function(p){ return p.n + ' fields'; }, + 'ui.legalNote': 'Statutory figures with their sources. Change them only if the law has changed or your insurer differs.', + 'group.bav': 'Top-up and payout forms in detail', + 'group.person': 'Timeline, leaving work, part-time', + 'group.steuer': 'Tax, assessment, children', + 'group.markt': 'Costs and inflation', + 'group.ruhe': 'Retirement', + 'group.p_sv': 'Statutory figures: social security 2026', + 'group.p_est': 'Statutory figures: income tax tariff 2026 (§ 32a EStG)', + 'group.p_kap': 'Statutory figures: capital gains and investment tax', + 'group.p_bav': 'Statutory figures: occupational and subsidised private provision', + + /* ---------------- Live notes on the fields ---------------- */ + 'n.beitrag.ueber8': function(p, f){ + return 'Above 8 % of the pension-insurance contribution ceiling (' + f.eur(p.v8) + ' per year). The part ' + + 'above is neither tax- nor contribution-privileged — it goes into the insurance wrapper and picks up ' + + 'nothing there but liability to health-insurance contributions.'; }, + 'n.beitrag.ueber4': function(p, f){ + return 'Above 4 % of the pension-insurance contribution ceiling (' + f.eur(p.v4) + ' per year). Up to here ' + + 'you have a statutory entitlement under § 1a BetrAVG; beyond it your employer has to agree.'; }, + 'n.agzus.gedeckelt': function(p, f){ + return 'Capped at ' + f.pct(p.quote, 1) + '. The employer actually saves only ' + f.eur2(p.ersparnis) + + ' per year — higher values change nothing here as long as the top-up mode stays „actual saving“.'; }, + 'n.agz_fix.fliesstNicht': function(p, f){ + return 'Does not flow: your monthly contribution of ' + f.eur2(p.beitrag) + ' is below the minimum ' + + 'threshold of ' + f.eur2(p.schwelle) + ' (field under „More settings“).'; }, + 'n.agz_fix.ueber4': function(p, f){ + return 'The flat top-up alone exceeds the 4 % allowance (' + f.eur(p.topf) + ' per year) by ' + + f.eur(p.ueber) + '. In your case that part would be pay subject to social-security contributions — ' + + 'the model does NOT net out the additional contribution burden and overstates the bAV to that extent.'; }, + 'n.agz_fix.verdraengung': function(p, f){ + return 'It takes priority in the 4 % allowance: only ' + f.eur(p.rest) + ' of your own contribution stays ' + + 'free of social-security contributions. Your net outlay rises accordingly — the top-up is still free ' + + 'money, it merely shifts the boundary of your own subsidised amount.'; }, + 'n.agz_fix.ueber8': 'Together with your own contribution above 8 % of the BBG-RV — the excess is neither tax- nor contribution-privileged.', + 'n.agz_fix_min.knapp': function(p, f){ + return 'You are only ' + f.eur2(p.d) + ' above the threshold. Below it the flat top-up disappears entirely: ' + + 'the marginal return on the last euro of contribution is infinite at that point, and the gross ↔ net ' + + 'coupling is discontinuous there.'; }, + 'n.agz_fix_min.verfehlt': function(p, f){ + return 'Threshold not met — ' + f.eur2(p.fehlt) + ' more per month triggers ' + f.eur2(p.zuschuss) + + ' of top-up. That is almost always the most profitable euro in the whole model.'; }, + 'n.teilkap.kosten': function(p, f){ + return 'It costs ' + (p.gewinn ? 'nothing, on the contrary: ' + p.dtxt : p.dtxt + ' ' + p.unit) + + ' compared with pure annuitisation. Reason: the lump sum is fully taxable in the year of receipt ' + + '(marginal burden there ' + f.pct(p.grenz, 1) + ') and afterwards compounds only at the rate net of ' + + 'withholding tax instead of at the product return.'; }, + 'n.av_auszform.horizont': function(p, f){ + return 'Beyond the comparison horizon this annuity still pays ' + f.eur(p.rest) + ', which is NOT counted ' + + 'here. A fixed horizon systematically understates every lifelong annuity — living long pays off here.'; }, + + /* ---------------- Result cards ---------------- */ + 'card.lead': 'ahead', + 'card.unchanged': 'unchanged', + 'base.pinned': 'Δ against the pinned state', + 'base.delta': function(p){ return 'Δ ' + p.desc; }, + 'base.none': 'Move a slider — the change appears here as Δ.', + 'base.desc.prev': 'previous state', + 'base.desc.pinned': 'pinned state', + 'base.desc.change': function(p){ return 'before changing „' + p.label + '“'; }, + 'rank.swap': function(p, f){ + return '<b>The metrics disagree.</b> By monthly benefit ' + p.a + ' is ahead of ' + p.b + + ', by present value of the net return it is the other way round. Reason: the present value discounts ' + + 'at inflation (' + f.pct(p.infl, 2) + '), the monthly benefit annuitises at the reinvestment rate net ' + + 'of tax (' + f.pct(p.wiederanl, 2) + '). The wider those two are apart, the better an early lump sum ' + + 'looks. Which metric applies depends on whether you would invest or spend the money — for „spend“, set ' + + 'the field „Cost of the private ETF account“ so that the reinvestment rate goes to zero.'; }, + 'warn.unreachable': 'This net outlay is unreachable — it exceeds your salary. The highest possible value has been set instead.', + 'warn.inactive': 'By your own figures you are no longer employed in the first year — there is no salary sacrifice, hence no net outlay from which a gross contribution could be back-solved. Check „Leaving the job“ and „Age today“.', + + /* ---------------- Plain-language verdict ---------------- */ + 'verdict.lead': function(p){ + return '<b>' + p.satz + '</b> is ahead with ' + p.wert + ' — ' + p.d2 + ' more than ' + p.nom2 + + (p.nom3 ? ' and ' + p.d3 + ' more than ' + p.nom3 : '') + '.'; }, + 'verdict.pp': function(p){ return p.v + ' percentage points'; }, + 'verdict.top': function(p, f){ + return ' The single largest item in the gap between ' + p.gen + ' and the private ETF account is ' + + '„' + p.label + '“ — ' + (p.pro ? 'in favour of ' : 'to the detriment of ') + p.gen + + ', worth ' + f.eur(Math.abs(p.wert)) + ' in present value. The full decomposition is under ' + + '„Value drivers“.'; }, + 'verdict.band': function(p, f){ + return '<span class="band">How robust is that? At a gross return of ' + f.pct(p.lo, 1) + ' instead of ' + + f.pct(p.mid, 1) + ' it would be ' + p.vlo + ', at ' + f.pct(p.hi, 1) + ' it would be ' + p.vhi + + ' — the ranking ' + + (p.stabil ? 'stays the same across that range.' + : '<b>reverses within that range</b>, so the result does not carry here.') + + '</span>'; }, + 'demo.text': '<b>These are still example figures</b> — 40 years old, 75,000 € gross per year, 300 € of salary sacrifice per month, statutory health insurance. Enter your own under „Your figures“; this notice then disappears by itself.', + + /* ---------------- Floating bar ---------------- */ + 'sb.cap': function(p){ return p.label + ' · ' + p.unit + (p.kaufkraft ? ', today’s purchasing power' : ''); }, + 'sb.aria': function(p){ return 'Result: ' + p.label; }, + + /* ---------------- Charts ---------------- */ + 'tab.sens': 'Sensitivity', + 'tab.wf': 'Value drivers', + 'tab.kap': 'Capital path', + 'tab.zer': 'Monthly breakdown', + 'chart.error': function(p){ return 'Chart cannot be drawn: ' + p.msg; }, + 'sens.readout': 'Dashed: your current value. Rings: intersections with the private account. Hover or tap over the curve for values.', + 'sens.legendY': function(p){ return 'y: ' + p.label + ' (' + p.unit + ')'; }, + 'sens.note': function(p){ + return 'The slider you moved last is computed across its whole range — ' + p.n + + ' complete model runs. Currently: „' + p.label + '“. Move another slider and the curve follows.'; }, + 'sens.noteNetto': function(p, f){ + return ' Careful: this slider also changes the net outlay (here ' + f.eur2(p.lo) + ' to ' + f.eur2(p.hi) + + ' per month) and hence the amount invested in all three options. The three curves are therefore fairly ' + + 'comparable at any single x, but the level between different x values is not. The kinks sit at the ' + + 'contribution ceilings (' + f.eur(p.bbgkv) + ' for health and care, ' + f.eur(p.bbgrv) + ' for pension ' + + 'and unemployment insurance) and at the tariff break points.'; }, + 'wf.up': 'raises the return', + 'wf.down': 'lowers the return', + 'wf.legend': 'present value of the net return, today’s purchasing power', + 'wf.check': function(p, f){ + return 'Check: ' + f.eur(p.start) + ' + ' + f.eur(p.summe) + ' = ' + f.eur(p.start + p.summe) + + ' (target ' + f.eur(p.ende) + ', deviation ' + f.nf2(p.start + p.summe - p.ende) + ' €).'; }, + 'wf.note': 'A telescoping decomposition, which is why it adds up exactly — but it is <b>order-dependent</b>: the effects are not additively separable, each bar measures its contribution <i>given</i> the ones above it. Tap a bar for the derivation.', + 'wf.barinfo': function(p){ return p.label + ': ' + p.note; }, + 'wfb.vp.label': 'No advance lump sum (Vorabpauschale)', + 'wfb.vp.note': 'Neither the bAV nor the Altersvorsorgedepot is subject to the advance lump sum (§ 18 InvStG). Measured as the private account without the advance lump sum minus with it. The bar can be negative: the advance lump sum largely runs into the saver’s allowance while raising the cost basis in full — in that case it is a net advantage for the private account.', + 'wfb.zul.label': 'Allowances and special-expenses deduction', + 'wfb.zul.note': 'Free money under the new allowance model plus the deduction under § 10a EStG. Measured by switching off both subsidy channels.', + 'wfb.arch.label': 'Taxation architecture (remainder)', + 'wfb.arch.note': 'Tariff arbitrage t₀ − t₁ and everything else. Determined as a residual so that the chain adds up exactly.', + 'wfb.agz.label': 'Employer top-up', + 'wfb.agz.note': 'Free money — the only structural advantage of the bAV that does not come from tax law. Measured by switching the top-up off.', + 'wfb.tarif.label': 'Tariff arbitrage and remainder', + 'wfb.tarif.note': 'Marginal tax rate when paying in minus when paying out, plus everything else. Determined as a residual so that the chain adds up exactly. Tax deferral alone is worthless: (1−t)(1+r)ⁿ = (1+r)ⁿ(1−t), multiplication commutes.', + 'wfb.kvpv.label': 'Health and care contributions on Versorgungsbezüge', + 'wfb.kvpv.note': '§§ 229, 250 SGB V: the pensioner bears the full contribution rate alone. Measured as the difference to the setting „private health insurance in retirement“.', + 'wfb.ep.label': 'Loss of statutory pension entitlement', + 'wfb.ep.note': 'Salary sacrifice reduces the pay subject to contributions and hence the statutory pension. Netted out here.', + 'kap.rentbeg': 'start of payout', + 'kap.swap': 'capital → annuity claim', + 'kap.legend': 'solid: total · dashed: freely available', + 'kap.readout': function(p, f){ + return 'Locked in at the start of payout: bAV ' + f.eur(p.bavGeb) + ' of ' + f.eur(p.bav) + + ' · AV account ' + f.eur(p.avGeb) + ' of ' + f.eur(p.av) + + ' · private account ' + f.eur(0) + ' of ' + f.eur(p.priv) + + '. The equivalent monthly benefit is consumed, which is why every option ends at zero.'; }, + 'kap.note': function(p, f){ + return 'Balance inside the product, plus — during the payout phase — the payouts reinvested net of tax (' + + f.pct(p.wiederanl, 2) + ' after tax) and minus the equivalent monthly benefit that is actually ' + + 'consumed. That is why every option runs down to exactly zero. Because consumption comes only out of ' + + 'the reinvested part while the product capital stays in the product, where it grows at the pre-tax ' + + 'product return, the curves are nevertheless not proportional. — The dashed line is the freely ' + + 'available part: the bAV is fully locked until the start of payout (§ 1b BetrAVG grants vesting, not ' + + 'availability); with the Altersvorsorgedepot a withdrawal before 65 forfeits the subsidy and only the ' + + 'excess over the annual maximum is free; the private account is free at any time. This is the only ' + + 'axis on which a lump sum looks good. ' + + (p.verrentet ? 'Because you annuitise the account, the capital goes to the provider there as well — ' + + 'the curve drops accordingly, although nothing is lost. ' : '') + + 'And once more, explicitly: the curves show the wealth arising from THIS one decision, not your total ' + + 'wealth.'; }, + 'zer.netto': 'net', + 'zer.tax': 'tax', + 'zer.kv': 'health/care', + 'zer.pension': 'pension', + 'zer.legend': '<span><i style="background:#808080"></i>tax</span><span><i style="background:#800000"></i>health/care</span><span><i style="background:#800080"></i>forgone statutory pension</span><span style="margin-left:auto">coloured base = what is left</span>', + 'zer.readout': 'Everything as a constant monthly benefit in today’s purchasing power. Because annuitisation is linear, the blocks add up exactly.', + 'zer.note': function(p, f){ + return 'The reference period is identical for all three options. A lump sum is not divided by twelve but ' + + 'annuitised over the same period at the reinvestment rate net of tax (' + f.pct(p.wiederanl, 2) + + ' p.a.). Anyone who consumes the capital instead of investing it ends up worse off.'; }, + + /* ---------------- How it is calculated ---------------- */ + 'det.h.bav': 'bAV', + 'det.h.av': 'AV account', + 'det.h.priv': 'private', + 'det.h.kennzahl': 'Figure', + 'det.h.wert': 'Value', + 'det.erg.netto': 'Total paid in out of net income (nominal)', + 'det.erg.fremd': 'Free money (top-up or allowances)', + 'det.erg.kapital': 'Capital at the start of payout', + 'det.erg.brutto': 'Total gross payouts', + 'det.erg.steuer': 'of which income and withholding tax', + 'det.erg.kvpv': 'of which health and care insurance', + 'det.erg.rentenverlust': 'Loss of statutory pension (cumulative)', + 'det.erg.nettoausz': 'Total net payouts (nominal)', + 'det.erg.bwausz': 'Present value of the net payouts', + 'det.erg.bwaufw': 'Present value of the net outlay', + 'det.erg.ertrag': 'Present value of the net return', + 'det.erg.verhaeltnis': 'Ratio of payout to outlay', + 'det.erg.irr': 'Internal rate of return (nominal)', + 'det.erg.vorsprung': 'Lead over the private account', + 'det.mon.brutto': 'Gross benefit per month', + 'det.mon.steuer': 'less income and withholding tax', + 'det.mon.kvpv': 'less health and care insurance', + 'det.mon.ep': 'less loss of statutory pension', + 'det.mon.netto': 'NET PER MONTH (today’s purchasing power)', + 'det.diag.beitrag': 'Gross bAV contribution per month', + 'det.diag.aufwand': 'Net outlay per month (first year)', + 'det.diag.grenzsteuer': 'Marginal tax rate during accumulation (first year)', + 'det.diag.ersparnis': 'Total saving rate (tax + social security), first year', + 'det.diag.svAG': 'Actual social-security saving of the employer', + 'det.diag.svAN': 'Actual social-security saving of the employee', + 'det.diag.ngef': 'Share of bAV contributions without subsidy (first year)', + 'det.diag.aussch': 'Use of the statutory entitlement (4 % of BBG-RV)', + 'det.diag.agzVar': 'of which employer top-up: percentage (first year)', + 'det.diag.agzFix': 'of which employer top-up: flat amount (first year)', + 'det.diag.agzFixUeber4': 'flat top-up above the 4 % allowance (contribution burden not captured)', + 'det.diag.vbMonat': 'Versorgungsbezug per month at the start of payout', + 'det.diag.freibetrag': 'Allowance for Versorgungsbezüge in the same year', + 'det.diag.vbQuote': 'Ratio of Versorgungsbezug to allowance', + 'det.diag.kapFreiKapital': 'Capital possible free of contributions (lump sum)', + 'det.diag.kapFreiRente': 'Capital possible free of contributions (lifelong annuity)', + 'det.diag.memoRest': 'MEMO: bAV annuity payments beyond the horizon', + 'det.diag.memoRestAv': 'MEMO: account annuity beyond the horizon', + 'det.diag.wiederanl': 'Reinvestment rate net of tax', + 'det.diag.kapstEff': 'Effective withholding tax incl. solidarity and church tax', + 'det.diag.bestAnteil': 'Taxable share of the statutory pension', + 'det.warn.lead': 'You are outside the privileged corridor: ', + 'det.warn.aussch': 'Above 100 % usage there is no longer a statutory entitlement to salary sacrifice. ', + 'det.warn.ngef': 'A noticeable part of the contributions is neither tax- nor contribution-privileged — that layer goes into an insurance wrapper and gets nothing for it except liability to health-insurance contributions. ', + 'det.warn.vb': 'The Versorgungsbezug clearly exceeds the allowance; the protective effect of an absolute allowance falls like 1/capital. ', + 'det.anspar.h.jahr': 'Year', + 'det.anspar.h.brutto': 'Gross', + 'det.anspar.h.beitrag': 'bAV contribution', + 'det.anspar.h.agzVar': 'Top-up %', + 'det.anspar.h.agzFix': 'Top-up flat', + 'det.anspar.h.sv': 'Soc. sec. saving', + 'det.anspar.h.st': 'Tax saving', + 'det.anspar.h.netto': 'Net outlay', + 'det.anspar.h.zul': 'Allowances', + 'det.anspar.h.kbav': 'Capital bAV', + 'det.anspar.h.kav': 'Capital AV', + 'det.anspar.h.kpriv': 'Capital private', + 'det.row.year': function(p){ return p.jahr + ' (age ' + p.alter + ')'; }, + 'det.ausz.h.jahr': 'Year', + 'det.ausz.h.bavBr': 'bAV gross', + 'det.ausz.h.kvpv': 'health/care', + 'det.ausz.h.steuer': 'Tax', + 'det.ausz.h.mind': 'Pension reduction', + 'det.ausz.h.bavNet': 'bAV net', + 'det.ausz.h.avBr': 'AV gross', + 'det.ausz.h.avNet': 'AV net', + 'det.ausz.h.privBr': 'private gross', + 'det.ausz.h.privNet': 'private net', + 'det.ann.intro': 'The ranking depends on legal questions that are not all settled in 2026. The figures in the column are subjective confidence levels.', + 'det.ann.h.annahme': 'Assumption', + 'det.ann.h.vertrauen': 'Confidence', + 'det.ann.h.anmerkung': 'Note', + 'det.ann.r1.a': 'Social-security figures 2026, § 32a tariff 2026', + 'det.ann.r1.c': 'SVBezGrV 2026, Steuerfortentwicklungsgesetz', + 'det.ann.r2.a': 'Health and care contributions on Versorgungsbezüge', + 'det.ann.r2.c': '§§ 229, 250 SGB V', + 'det.ann.r3.a': 'Allowance under § 226 Abs. 2 S. 2 SGB V (health insurance)', + 'det.ann.r3.c': 'undisputed', + 'det.ann.r4.a': 'Exemption limit rather than allowance in care insurance', + 'det.ann.r4.c': 'the most common error in guidebook texts', + 'det.ann.r5.a': 'AV account payout free of social-security contributions', + 'det.ann.r5.c': 'not Versorgungsbezüge', + 'det.ann.r6.a': 'Half the difference amount for non-subsidised layers', + 'det.ann.r6.c': '§ 22 Nr. 5 S. 2 Buchst. b in conjunction with § 20 Abs. 1 Nr. 6 S. 2 EStG', + 'det.ann.r7.a': 'Top-up only up to the employer’s actual saving', + 'det.ann.r7.c': 'the opposing view reads the 15 % as a pure flat rate', + 'det.ann.r8.a': 'Special-expenses maximum of 2,340 €', + 'det.ann.r8.c': 'MODEL ASSUMPTION. Decides whether a child raises or lowers the value of the AV account', + 'det.ann.r9.a': 'Compulsory KVdR insurance assumed in retirement', + 'det.ann.r9.c': 'voluntarily insured persons not examined', + 'det.ann.notmodelled': '<b>Not modelled:</b> deductions for early retirement, the effect of salary sacrifice on sickness, unemployment and parental benefits, survivors’ benefits, insolvency protection (PSVaG), portability when changing employer, care-insurance reductions for children, the comparison test under § 32d Abs. 6 EStG, protection against attachment and in basic income support, longevity risk beyond the horizon.', + 'det.prinzip': '<p>Comparing <i>gross contributions</i> would be unfair: salary sacrifice comes out of gross pay, private saving out of net pay. The <b>net outlay</b> is therefore held equal:</p>' + + '<p style="font-family:var(--mono);font-size:.82rem;background:var(--chip);padding:9px 11px;border-radius:9px">net outlay = gross contribution − wage-tax saving − employee social-security contributions saved</p>' + + '<p>Exactly this amount is invested in all three options. What remains is a comparison of <i>subsidy architectures</i>, not of contribution levels.</p>' + + '<p><b>Tax deferral in itself is worthless:</b> (1−t)·(1+r)ⁿ = (1+r)ⁿ·(1−t). Multiplication commutes. Only four value drivers remain:</p>' + + '<ol style="padding-left:1.2em;margin:.3em 0"><li>tariff arbitrage t₀ − t₁</li>' + + '<li>free money — employer top-up or allowances</li>' + + '<li>tax-free accumulation — no advance lump sum</li>' + + '<li>new levies that hit only one option — health and care contributions on Versorgungsbezüge</li></ol>' + + '<p>Point 4 is the dominant disadvantage of the bAV, point 2 its only structural advantage. The return on the bAV <b>falls strictly monotonically in the contribution level</b>, because the allowance is an absolute amount and its protective effect decays like 1/K — there is no interior optimum.</p>' + + '<p>If the net outlay is given, the gross contribution is back-solved by bisection: N(B) is continuous and strictly increasing (dN/dB = 1 − marginal social-security rate − marginal tax rate > 0), but has no closed-form inverse because of the kinks at 4 % and 8 % of the BBG-RV, at the contribution ceilings and at the tariff break points. 34 bisections get below a tenth of a cent.</p>', + 'det.t.prinzip': 'The comparison principle', + 'det.t.erg': 'Result in detail', + 'det.s.erg': 'All three options cost exactly the same amount out of net income during accumulation.', + 'det.t.mon': 'Monthly benefit, decomposed', + 'det.s.mon': 'All options annuitised over the same period, in today’s purchasing power. Because annuitisation is linear, the rows add up exactly.', + 'det.t.diag': 'Diagnostics', + 'det.s.diag': 'Where it comes from — and where you stand outside the privileged corridor.', + 'det.t.anspar': 'Accumulation phase, year by year', + 'det.t.ausz': 'Payout phase, year by year', + 'det.t.ann': 'Robustness of the assumptions', + + /* ---------------- Save and share ---------------- */ + 'msg.linkCopied': 'Link copied. Whoever opens it sees exactly these figures — none of it goes through a server.', + 'msg.linkFailed': 'Copying failed. The link is now in the address bar.', + 'msg.loaded': 'Loaded.', + 'msg.loadError': function(p){ return 'The file could not be read: ' + p.msg; }, + 'msg.reset': 'Reset to the base case.', + + /* ---------------- PWA ---------------- */ + 'pwa.offline': '<b>Works offline.</b> The app is now in the browser cache and runs without a connection. To install: on iPhone „Share“ → „Add to Home Screen“, on Android the menu → „Install app“.', + 'pwa.installAsk': '<b>Install as an app?</b> Then it sits on your home screen with its own icon and runs offline. ', + 'pwa.installBtn': 'Install', + 'pwa.installed': '<b>Installed.</b> You will find the app among your other apps now.', + 'pwa.declined': 'Fine — the app runs like this too, and offline at that.', + 'pwa.file': '<b>You opened the file directly</b> (<kbd>file://</kbd>). Everything computes, everything is saved. Only the two PWA properties are missing: no icon on the home screen and no offline cache — which you would not need here anyway, since the file is already on the device. For the full app experience, put the folder on any HTTPS web space and open it from there.' +}; diff --git a/app/i18n.ja.js b/app/i18n.ja.js new file mode 100644 index 0000000..914f3f2 --- /dev/null +++ b/app/i18n.ja.js @@ -0,0 +1,570 @@ +/* ===================================================================== + 日本語辞書。キーの集合は i18n.de.js と同一で、欠けているキーは + 実行時にドイツ語へフォールバックします。 + 法令の引用(§ 1a BetrAVG など)は意図的にドイツ語のまま残しています。 + 一次資料であり、公式の訳が存在しないためです。 + 文体は説明文が「ですます」、項目名は体言止めで統一しています。 + ===================================================================== */ +window.I18N_JA = { + 'f.I:gehalt.label': '現在の年間総支給額', + 'f.I:gehalt.hint': '一時金を除き、必要に応じて13か月目の按分を含めます。限界税率と、社会保険料算定上限(BBG)を超えるかどうかを決める値です。', + 'f.I:beitrag.label': '企業年金の月額拠出(税引前)', + 'f.I:beitrag.hint': '§ 1a BetrAVG による総額からの賃金転換です。法律上の請求権は年金保険の算定上限(BBG-RV)の4 %までで、それを超えるには事業主の同意が必要です。', + 'f.I:netto_ziel.label': '月あたりの手取り負担額', + 'f.I:netto_ziel.hint': '初年度に手取り収入が減る金額です。実際にあきらめる消費であり、経済的に正しい意思決定変数です。三つの選択肢すべてにこの同じ金額を投資します。それが公平な比較の条件です。上の総額拠出とは一対一に対応します。N(B) = B − 社会保険料の節約 − 税の節約 は狭義単調増加なので逆関数が存在します。算定上限と税率表の折れ点があるため閉じた式では解けず、34回の二分法で求めています(誤差は0.1セント未満)。', + 'f.I:agzus.label': '事業主補助', + 'f.I:agzus.hint': '法定の下限は15 %(§ 1a Abs. 1a BetrAVG)で、それ以上を出す事業主も多くあります。民間口座との損益分岐は概ね (1+z)·(1−0.211) ≈ 1、すなわち z ≈ 26.7 % のあたりで、感応度曲線で確かめられます。補助方式が「実際の節約分」の場合、先に頭打ちになるためこの値に届かないことがよくあります。', + 'f.I:agz_fix.label': '追加の定額補助', + 'f.I:agz_fix.hint': '拠出額に連動しない定額の事業主補助です。ドイツ鉄道や多くの労働協約で一般的で、そこでは § 1a Abs. 1a BetrAVG の15 %に上乗せされます。§ 3 Nr. 63 EStG の事業主拠出にあたり、8 %の非課税枠と4 %の社会保険料免除枠(§ 1 Abs. 1 S. 1 Nr. 9 SvEV)を優先的に消費します。つまり自分の賃金転換はその分だけ押し出され、割高になります。経済的には土台であってテコではありません。次の1ユーロの限界収益は変わらず(∂²収益/∂agz_fix ∂拠出 ≤ 0、押し出しによってむしろ下がることもあります)、平均だけが大きく上がります。感応度曲線ではおおむね一次関数に見え、4 %枠または8 %枠を使い切ったところで折れます。', + 'f.I:agz_fix_dyn.label': '定額補助の物価連動', + 'f.I:agz_fix_dyn.hint': '定額は労働協約に名目額で書かれ、改定交渉のときだけ引き上げられるのが普通です。そのため既定値は「名目一定」です。インフレ2 %で27年経つと 1.02⁻²⁷ ≈ 0.59 しか残りません。悲観的すぎると考えるなら「給与に連動」に切り替えてください。実質の伸び率は gsteig − infl になります。', + 'f.I:agz_fix_dyn.o0': '名目一定', + 'f.I:agz_fix_dyn.o1': '給与に連動', + 'f.I:agzus_sv.label': '補助の方式', + 'f.I:agzus_sv.hint': '「実際の節約分」が条文の文言です。§ 1a Abs. 1a BetrAVG は、事業主が現に社会保険料を節約した「限りにおいて」補助を義務づけます。給与が両方の算定上限を超えていれば事業主は何も節約せず、何も負いません。だから基準ケースではスライダーが21 %前後で頭打ちになります。「拠出全額」は任意または労働協約による約束、「免除部分のみ」は広く行われている概算実務です。厳格な読み方への信頼度は約75 %です。', + 'f.I:agzus_sv.o0': '拠出全額', + 'f.I:agzus_sv.o1': '免除部分のみ', + 'f.I:agzus_sv.o2': '実際の節約分', + 'f.I:dyn.label': '拠出額の物価連動', + 'f.I:dyn.hint': '拠出が給与の上昇に連動すれば、8 %枠の消化率は年を通じてほぼ一定に保たれます。名目一定のままなら実質価値はインフレの分だけ下がり、他の二つの選択肢が比較のために受け取る手取り負担額も一緒に下がります。', + 'f.I:dyn.o1': '給与に連動', + 'f.I:dyn.o0': '名目一定', + 'f.I:rendite.label': 'ETFの総収益率(年率)', + 'f.I:rendite.hint': 'コスト控除前、税引前です。三つの選択肢すべてで同じETFを用います。そうして初めて、運用判断ではなく優遇の仕組みを比べることになります。', + 'f.I:alter.label': '現在の年齢', + 'f.I:alter.hint': '積立期間の起点です。暦年0 = 2026年。', + 'f.I:alter.unit': '歳', + 'f.I:rentbeg.label': '受給開始', + 'f.I:rentbeg.hint': '三つの選択肢すべての受給開始時点です。老後資産口座は最短でも65歳からの受給になります(2026年改革)。', + 'f.I:rentbeg.unit': '歳', + 'f.I:kv_ruhe.label': '退職後の医療保険', + 'f.I:kv_ruhe.hint': '決定的なてこです。公的医療保険(GKV)では企業年金は Versorgungsbezüge(企業年金等の給付)にあたり、保険料率の全額を負担します(§§ 229, 250 SGB V)。私的な備えにはこれがありません。この設定だけで結論がひっくり返ります。', + 'f.I:kv_ruhe.o1': '公的', + 'f.I:kv_ruhe.o2': '民間', + 'f.I:auszform.label': '企業年金の受け取り方', + 'f.I:auszform.hint': '一時金は受け取った年に全額課税され、医療保険料は120か月に分けて賦課されます(§ 229 Abs. 1 S. 3 SGB V)。年金形式は給付を残りの生涯に引き伸ばすため、同じ非課税枠で何倍もの資産を覆えます。このモデルは全部か無かのみを扱い、企業年金の一部一時金化は表現していません。', + 'f.I:auszform.o1': '一時金', + 'f.I:auszform.o2': '終身年金', + 'f.I:teilkap.label': '老後資産口座:受給開始時の一時金', + 'f.I:teilkap.hint': '積み立てた資産の30 %までを受給開始時に一時金として受け取れます。これはどちらの受け取り方でも可能です。ただし二重に高くつきます。受け取った年に累進課税を受け、その後は商品の収益率ではなく源泉課税後の利回りでしか増えません。年間上限を超えた部分(「スリーブ」)は一時金なしで年金化されます。これはモデル上の約束であって法規範ではありません。', + 'f.I:av_auszform.label': '老後資産口座:残りの部分', + 'f.I:av_auszform.hint': '一時金を超える資産をどうするかで、上の企業年金の選択とは独立です。取崩しプラン:資産は運用したまま利息とともに引き出し、非優遇層は差額の半分に課税されます。終身年金:資産は事業者に移り、その代わり非優遇層には収益部分だけが課税されます(§ 22 Nr. 5 S. 2 Buchst. a EStG、67歳以降は17 %。ここでの約26 %に対して)。優遇層はどちらの場合も全額が後払い課税で、これは変わりません。', + 'f.I:av_auszform.o1': '取崩しプラン', + 'f.I:av_auszform.o2': '終身年金', + 'f.I:agz_fix_min.label': '定額補助を受けるための最低自己拠出', + 'f.I:agz_fix_min.hint': 'マッチング型は定額を最低自己拠出に結びつけます(「X以上を転換すればYがもらえる」)。0は無条件を意味し、その場合は拠出が0でも在職中の各年に補助が入ります。モデルの意味に注意してください。本当に無条件の補助は賃金転換という判断の差分効果ではないため、転換しなくても入るのにこの比較では企業年金に有利に働きます。実際に加入に依存する分だけを入力してください。', + 'f.I:rentfak_av.label': '年金換算率:老後資産口座', + 'f.I:rentfak_av.hint': '口座を年金化する場合のみ有効です。既定値は企業年金と同じにしてあります。口座契約は保険の外枠を必要としないため有利になり得ますので、実際の提案書の値を入れてください。0なら年金はありません。', + 'f.I:rentfak_av.unit': '€/1万€あたり', + 'f.I:rentdyn_av.label': '口座年金の増額率(年率)', + 'f.I:rentdyn_av.hint': '口座契約の受給期における配当または運用実績です。', + 'f.I:rentfak.label': '年金換算率:企業年金', + 'f.I:rentfak.hint': '受け取り方が「年金」の場合のみ有効です。1万€あたり月26 €は取崩し率およそ3.1 %に相当します。商品概要説明書に記載があります。', + 'f.I:rentfak.unit': '€/1万€あたり', + 'f.I:rentdyn.label': '企業年金の増額率(年率)', + 'f.I:rentdyn.hint': '受給期における配当または運用実績です。', + 'f.I:fuenftel.label': '5分の1課税の適用を見込む', + 'f.I:fuenftel.hint': '§ 34 EStG は § 3 Nr. 63 契約からの計画的な一時金には、BFH X R 23/15 によれば例外的にしか適用されません。保守的には「いいえ」です。', + 'f.I:fuenftel.o0': 'いいえ(保守的)', + 'f.I:fuenftel.o1': 'はい', + 'f.I:planende.label': '比較の終期(年齢)', + 'f.I:planende.hint': '取崩しプランはここまで続きます。終身年金も同じ期間で比較し、その後に支払われる分は「診断」の備考に示します。長生きすればそこで得をします。', + 'f.I:planende.unit': '歳', + 'f.I:ausscheid.label': '退職する年齢', + 'f.I:ausscheid.hint': 'これ以降は給与も拠出もありません。資産は受給開始まで運用され続けます。', + 'f.I:ausscheid.unit': '歳', + 'f.I:tzalter.label': '時短勤務の開始年齢', + 'f.I:tzalter.hint': '受給開始以降の値はすべて「時短なし」を意味します。そのため既定値は67です。', + 'f.I:tzalter.unit': '歳', + 'f.I:tzfaktor.label': '時短勤務の比率', + 'f.I:tzfaktor.hint': 'フルタイム給与に対する割合です。60 %は60 %勤務を指します。', + 'f.I:gsteig.label': '賃金上昇率(年率)', + 'f.I:gsteig.hint': '算定上限、基準額(Bezugsgröße)、平均賃金、年金額単価もこの率で先送りします。', + 'f.I:stkl.label': '課税方式', + 'f.I:stkl.hint': '重要なのは課税方式であって源泉徴収の税額表クラスではありません。クラスは年度途中の源泉徴収を決めるだけで、年次確定では § 32a Abs. 5 EStG の二分二乗方式で計算されます。「合算」を選ぶ場合は配偶者の課税所得を必ず入力してください。', + 'f.I:stkl.o1': '単独', + 'f.I:stkl.o3': '合算(二分二乗)', + 'f.I:partner.label': '配偶者の課税所得', + 'f.I:partner.hint': '合算課税の場合のみ有効です。2·ESt((課税所得 + 配偶者の課税所得)/2) として計算します。', + 'f.I:kist.label': '教会税率', + 'f.I:kist.hint': 'バイエルン州とバーデン=ヴュルテンベルク州は8 %、他は9 %、離脱していれば0です。源泉課税にも効きます:0.25/(1+0.25k)·(1+0.055+k)。', + 'f.I:tarifanp.label': '税率表の刻みの調整(年率)', + 'f.I:tarifanp.hint': '税率表全体を λ = (1+x)^t 倍に引き伸ばします:ESt = s·λ·T(課税所得/s/λ)。これはすべての刻みを比例的にずらすのと厳密に同値です。0 %にすると名目上昇による増税(ブラケットクリープ)がそのまま現れます。', + 'f.I:kinder.label': '子の人数', + 'f.I:kinder.hint': '老後資産口座の子ども加算です。払い込んだ1€につき1€、子1人あたり300€までです。', + 'f.I:kind_jahre.label': '子ども加算のある年数', + 'f.I:kind_jahre.hint': '子ども加算は児童手当の受給権に連動するため(§ 85 EStG)、積立期間全体には及びません。', + 'f.I:kind_jahre.unit': '年', + 'f.I:kinderlos.label': '子なし(介護保険の上乗せ)', + 'f.I:kinderlos.hint': '介護保険料に0.6ポイントが上乗せされ、被用者が単独で負担します(§ 55 Abs. 3 SGB XI)。', + 'f.I:kinderlos.o0': 'いいえ', + 'f.I:kinderlos.o1': 'はい', + 'f.I:k_bav.label': '企業年金商品のコスト(年率)', + 'f.I:k_bav.hint': '保険の外枠とTERの合計です。実効コストは商品概要説明書に載っています。推測せず、そこで確かめてください。', + 'f.I:k_av.label': '老後資産口座のコスト(年率)', + 'f.I:k_av.hint': '標準口座では実効コストが法律で年1.0 %に上限設定されています。', + 'f.I:k_priv.label': '民間ETF口座のコスト(年率)', + 'f.I:k_priv.hint': '広範な全世界ETFの純粋なTERです。', + 'f.I:infl.label': 'インフレ率(年率)', + 'f.I:infl.hint': '「現在の購買力で」と書かれたすべての値の割引率です。順位はほとんど変えませんが、水準はすべて変えます。', + 'f.I:sonst_zve.label': '退職後のその他の課税所得', + 'f.I:sonst_zve.hint': '主に公的年金の課税対象部分で、ここで比較している商品は含みません。受給期の限界税率、ひいては税率差 t₀ − t₁ を決めます。', + 'f.I:rente_br.label': '公的年金の年間総額', + 'f.I:rente_br.hint': '医療保険の上限判定にのみ使います。公的年金と Versorgungsbezüge は一つの算定上限を分け合います(§ 223 Abs. 3 SGB V)。', + 'f.P:bbg_rv.label': '算定上限:年金保険(年額)', + 'f.P:bbg_rv.hint': 'SVBezGrV 2026(BGBl. 2025 I Nr. 116)、全国一律です。', + 'f.P:bbg_kv.label': '算定上限:医療・介護保険', + 'f.P:bbg_kv.hint': 'SVBezGrV 2026、月額5,812.50 €。', + 'f.P:bezug.label': '基準額(月額)', + 'f.P:bezug.hint': 'SVBezGrV 2026、年額47,460 €。その20分の1が医療保険における Versorgungsbezüge の非課税枠です(§ 226 Abs. 2 S. 2 SGB V)。', + 'f.P:de_rv.label': '年金保険の平均賃金', + 'f.P:de_rv.hint': 'SGB VI 附則1の2026年値です。報酬点数の分母になります。', + 'f.P:rw.label': '現在の年金額単価', + 'f.P:rw.hint': 'Rentenwertbestimmungsverordnung 2026、2026年7月1日から42.52 €(+4.24 %)。', + 'f.P:rw.unit': '€', + 'f.P:rv_an.label': '年金保険料(被用者負担分)', + 'f.P:rv_an.hint': '18.6 %の折半です。', + 'f.P:av_an.label': '雇用保険料(被用者負担分)', + 'f.P:av_an.hint': '2.6 %の折半です。', + 'f.P:kv_allg.label': '医療保険の一般料率', + 'f.P:kv_allg.hint': '§ 241 SGB V。', + 'f.P:kv_zus.label': '医療保険の平均追加料率', + 'f.P:kv_zus.hint': '連邦保健省の告示によります。保険者ごとに異なるので、加入先の料率を入力してください。', + 'f.P:pv_ges.label': '介護保険の総料率', + 'f.P:pv_ges.hint': '§ 55 SGB XI。第2子以降の軽減は表現していません。', + 'f.P:pv_an.label': '介護保険料(被用者負担分)', + 'f.P:pv_an.hint': 'ザクセン州のみ異なります(2.3 %)。', + 'f.P:pv_kl.label': '子なしの介護保険上乗せ', + 'f.P:pv_kl.hint': '§ 55 Abs. 3 SGB XI、被用者が単独で負担します。', + 'f.P:gfb.label': '基礎控除(第1ゾーンの上限)', + 'f.P:gfb.hint': 'Steuerfortentwicklungsgesetz による改正後の § 32a Abs. 1 EStG。', + 'f.P:e2.label': '第2ゾーンの上限', + 'f.P:e2.hint': '限界税率が14 %から23.97 %へ上がります。', + 'f.P:e3.label': '第3ゾーンの上限', + 'f.P:e3.hint': '限界税率が23.97 %から42 %へ上がります。', + 'f.P:e4.label': '第5ゾーンの起点(最高税率)', + 'f.P:e4.hint': '277,826 €から45 %になります。', + 'f.P:a2.label': '係数 a₂', + 'f.P:a2.hint': 'ESt = (a₂·y + b₂)·y、y = (課税所得 − 基礎控除)/10,000。', + 'f.P:b2.label': '係数 b₂', + 'f.P:a3.label': '係数 a₃', + 'f.P:a3.hint': 'ESt = (a₃·z + b₃)·z + c₃、z = (課税所得 − 17,799)/10,000。', + 'f.P:b3.label': '係数 b₃', + 'f.P:c3.label': '定数 c₃', + 'f.P:c4.label': '定数 c₄', + 'f.P:c4.hint': 'ESt = 0.42·課税所得 − c₄。', + 'f.P:c5.label': '定数 c₅', + 'f.P:c5.hint': 'ESt = 0.45·課税所得 − c₅。', + 'f.P:soli.label': '連帯付加税', + 'f.P:soli.hint': 'SolzG 1995。', + 'f.P:soli_fg.label': '連帯付加税の免税限度額', + 'f.P:soli_fg.hint': '2026年、基本税率表、確定所得税額に対する額です。二分二乗では倍になります。ほかに緩和ゾーンがあります。', + 'f.P:soli_mz.label': '緩和ゾーンの限界負担率', + 'f.P:soli_mz.hint': '§ 3 Abs. 2a SolzG。', + 'f.P:an_pausch.label': '給与所得者概算控除', + 'f.P:an_pausch.hint': '§ 9a S. 1 Nr. 1a EStG。', + 'f.P:sa_pausch.label': '特別支出概算控除', + 'f.P:sa_pausch.hint': '§ 10c EStG。', + 'f.P:kapst.label': '源泉課税の税率', + 'f.P:kapst.hint': '§ 32d Abs. 1 EStG。', + 'f.P:tfs.label': '株式ファンドの部分非課税', + 'f.P:tfs.hint': '§ 20 Abs. 1 Nr. 3 と § 20 InvStG、株式比率50 %超の場合です。', + 'f.P:sparerpb.label': '貯蓄者概算控除', + 'f.P:sparerpb.hint': '§ 20 Abs. 9 EStG、二分二乗では倍になります。前払一括課税額(Vorabpauschale)の大部分を吸収します。ウォーターフォールを参照してください。', + 'f.P:basiszins.label': '前払一括課税の基準利率', + 'f.P:basiszins.hint': '2026年1月13日付BMF通達、§ 18 Abs. 4 InvStG(2025年は2.53 %)。0にすると前払一括課税なしになります。', + 'f.P:vp_faktor.label': '基準収益の係数', + 'f.P:vp_faktor.hint': '§ 18 Abs. 1 S. 1 InvStG。', + 'f.P:st_frei_q.label': '§ 3 Nr. 63 の非課税枠(BBG-RV比)', + 'f.P:st_frei_q.hint': 'BBG-RVの8 %=2026年は8,112 €。', + 'f.P:sv_frei_q.label': '社会保険料免除枠(BBG-RV比)', + 'f.P:sv_frei_q.hint': '§ 1 Abs. 1 S. 1 Nr. 9 SvEV、4 %=2026年は4,056 €。同時に § 1a BetrAVG の請求権の限界でもあります。', + 'f.P:zul_g1.label': '加算・第1段階(自己拠出1€あたり)', + 'f.P:zul_g1.hint': 'Altersvorsorgereformgesetz、2026年5月施行、2027年1月1日から適用。', + 'f.P:zul_s1.label': '第1段階の上限', + 'f.P:zul_s1.hint': '自己拠出360 €までは1€につき50セントです。', + 'f.P:zul_g2.label': '加算・第2段階(自己拠出1€あたり)', + 'f.P:zul_g2.hint': '361 €から1,800 €までは1€につき25セントです。', + 'f.P:zul_s2.label': '第2段階の上限', + 'f.P:zul_s2.hint': '優遇対象となる自己拠出の上限額です。基礎加算は最大540 €になります。', + 'f.P:kind_zul.label': '子1人あたりの加算', + 'f.P:kind_zul.hint': '払い込んだ1€につき1€、子1人あたり300€までです。', + 'f.P:av_max.label': '年間払込みの上限額', + 'f.P:av_max.hint': '優遇付き私的老後保障の改革に関するBMFのFAQ、2026年5月5日時点。これを超えた分は、民間口座と同様に課税される別枠に回ります。', + 'f.P:av_sa_max.label': '§ 10a 特別支出控除の上限額', + 'f.P:av_sa_max.hint': 'モデル上の仮定です。信頼度は約70 %。自己拠出1,800 €までと加算の合計です。この一つの数字が、子どもの存在が老後資産口座の価値を上げるか下げるかを決めます。', + 'f.P:ertragsq.label': '収益部分の割合(67歳)', + 'f.P:ertragsq.hint': '§ 22 Nr. 1 S. 3 Buchst. a Doppelbuchst. bb EStG、非優遇の年金部分について。', + 'f.P:halb.label': '差額のうち課税対象となる割合', + 'f.P:halb.hint': '§ 20 Abs. 1 Nr. 6 S. 2 EStG の類推適用(契約期間12年超、62歳以降の受け取り)で差額の半分です。信頼度は約80 %。', + + /* ---------------- 外枠・ウィンドウ名・ヘッダー・フッター ---------------- */ + 'app.title': '老後保障くらべ', + 'app.meta': '企業年金・老後資産口座・民間ETF口座を同じ手取り負担額で比較します。2026年のドイツ法に基づく試算。', + 'hdr.title': '老後保障くらべ', + 'hdr.sub': '企業年金、老後資産口座、民間ETF口座を — <b>同じ手取り負担額</b>で、同じETFを使って比べます。2026年のドイツ法に基づく試算。', + 'win.header': '老後保障くらべ', + 'win.result': '結果', + 'win.input': '入力', + 'win.chart': 'グラフ', + 'win.extra': '詳細設定', + 'win.calc': '計算の内訳', + 'win.share': '保存と共有', + 'win.min': '最小化', + 'win.restore': '元に戻す', + 'win.n': function(p){ return 'ウィンドウ ' + p.n; }, + 'input.sub': '変更のたびに即座に再計算します。各項目は <span class="qm" style="cursor:default">?</span> で説明が開きます。', + 'extra.sub': 'ここはすべて妥当な既定値です。ご自身に当てはまる場合だけ触ってください。', + 'calc.sub': '計算をたどりたい方へ。', + 'share.sub': '入力内容はこのブラウザの中だけに残ります(<kbd>localStorage</kbd>)。送信はせず、サーバーもトラッカーもありません。', + 'ui.share': '数値入りのリンクをコピー', + 'ui.export': 'ファイルに保存', + 'ui.import': 'ファイルを読み込む', + 'ui.reset': 'すべて初期化', + 'ui.pin.set': '基準として固定', + 'ui.pin.clear': '基準を解除', + 'ui.lang': '言語', + 'ui.lang.de': 'ドイツ語', + 'ui.lang.en': '英語', + 'ui.lang.la': 'ラテン語', + 'ui.lang.ja': '日本語', + 'ui.lang.aria': '言語を選ぶ', + 'sb.up': '結果に戻る', + 'footer.1': '<b>税務・投資の助言ではありません。</b>モデルは契約ではありません。改革後の私的老後保障をめぐる2026年の法状況にはまだ確定していない点がいくつもあります。信頼度のもっとも低い仮定は「計算の内訳 → 仮定の頑健性」にまとめてあり、そこで変更もできます。', + 'footer.2': '計算部分はExcelモデル <span class="pill">bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx</span> をそのまま移植し、3件のテストケースで機械精度まで一致を確認しています(相対誤差の最大は 1.3·10<sup>−14</sup>)。', + + /* ---------------- 三つの選択肢 ---------------- */ + 'opt.bav.name': '企業年金(賃金転換)', + 'opt.bav.short': '企業年金', + 'opt.bav.nom': '企業年金', + 'opt.bav.satz': '企業年金', + 'opt.bav.gen': '企業年金', + 'opt.av.name': '老後資産口座(Altersvorsorgedepot)', + 'opt.av.short': 'AV口座', + 'opt.av.nom': '老後資産口座', + 'opt.av.satz': '老後資産口座', + 'opt.av.gen': '老後資産口座', + 'opt.priv.name': '民間ETF口座', + 'opt.priv.short': '民間', + 'opt.priv.nom': '民間ETF口座', + 'opt.priv.satz': '民間ETF口座', + 'opt.priv.gen': '民間ETF口座', + + /* ---------------- 指標 ---------------- */ + 'metric.monat.label': '月額給付', + 'metric.monat.unit': '€/月', + 'metric.monat.desc': '受給期間を通じて同額を毎月受け取るとした場合の金額を、現在の購買力で表したものです。いまの手取り収入と直接くらべられる唯一の数字です。', + 'metric.ertrag.label': '正味収益(現在価値)', + 'metric.ertrag.unit': '€', + 'metric.ertrag.desc': '自分の負担を超えて手元に残る分を現在の購買力で示します。すべての正味給付の現在価値から、手取り負担額の現在価値を引いた額です。', + 'metric.irr.label': '内部収益率', + 'metric.irr.unit': '% 年率', + 'metric.irr.desc': '実際に投じた手取り額に対する名目の収益率です。設定したETFの総収益率と直接くらべられます。その差が、優遇と負担を合わせた正味の効果にあたります。', + 'metric.kapital.label': '受給開始時の資産額', + 'metric.kapital.unit': '€', + 'metric.kapital.desc': '税引前・社会保険料控除前の名目額です。あえて意思決定の指標にはしていません。企業年金はここで先行しても、受給段階で逆転されることが多いからです。', + 'metric.pa': ' 年率', + 'metric.pp': ' ポイント', + 'metric.short.monat': '月額', + 'metric.short.ertrag': '収益', + 'metric.short.irr': 'IRR', + 'metric.short.kapital': '資産', + + /* ---------------- 入力欄の枠組み ---------------- */ + 'unit.pct': '%', + 'unit.eur': '€', + 'unit.eurm': '€/月', + 'ui.hintFor': function(p){ return p.label + 'の説明'; }, + 'ui.couple': '<span>⇅</span> 同じ量を二つの見方で表しています。一方を変えると、もう一方も追随します。', + 'ui.cnt.legal': function(p){ return '法定値 ' + p.n + '件'; }, + 'ui.cnt.fields': function(p){ return p.n + '項目'; }, + 'ui.legalNote': '出典つきの法定値です。法が変わったとき、または加入先が異なるときだけ変更してください。', + 'group.bav': '補助と受け取り方の詳細', + 'group.person': '時間軸・退職・時短勤務', + 'group.steuer': '税・課税方式・子ども', + 'group.markt': 'コストとインフレ', + 'group.ruhe': '退職後', + 'group.p_sv': '法定値:社会保険 2026年', + 'group.p_est': '法定値:所得税率表 2026年(§ 32a EStG)', + 'group.p_kap': '法定値:金融所得課税・投資信託課税', + 'group.p_bav': '法定値:企業年金と優遇付き私的年金', + + /* ---------------- 入力欄のライブ注記 ---------------- */ + 'n.beitrag.ueber8': function(p, f){ + return '年金保険の算定上限の8 %(年 ' + f.eur(p.v8) + ')を超えています。超過分は税制上も社会保険上も' + + '優遇されません。保険の外枠に入るだけで、そこで得るのは医療保険料の負担義務だけです。'; }, + 'n.beitrag.ueber4': function(p, f){ + return '年金保険の算定上限の4 %(年 ' + f.eur(p.v4) + ')を超えています。ここまでは § 1a BetrAVG による' + + '請求権がありますが、これを超えるには事業主の同意が必要です。'; }, + 'n.agzus.gedeckelt': function(p, f){ + return f.pct(p.quote, 1) + 'で頭打ちです。事業主が実際に節約しているのは年 ' + f.eur2(p.ersparnis) + + 'だけなので、補助の方式が「実際の節約分」のままなら、これ以上の値を入れても結果は変わりません。'; }, + 'n.agz_fix.fliesstNicht': function(p, f){ + return '支給されません。月額拠出 ' + f.eur2(p.beitrag) + ' が最低ライン ' + f.eur2(p.schwelle) + + ' に届いていません(項目は「詳細設定」の中です)。'; }, + 'n.agz_fix.ueber4': function(p, f){ + return '定額補助だけで4 %枠(年 ' + f.eur(p.topf) + ')を ' + f.eur(p.ueber) + ' 超えています。' + + 'この部分はあなたにとって社会保険料の対象となる賃金にあたりますが、モデルはその追加負担を' + + '差し引いていないため、その分だけ企業年金を高く見積もっています。'; }, + 'n.agz_fix.verdraengung': function(p, f){ + return '4 %枠を優先的に使うため、自分の拠出のうち社会保険料が免除されるのは ' + f.eur(p.rest) + + ' だけになります。手取り負担額はその分だけ増えます。とはいえ補助はもらえるお金であり、' + + '自己優遇の境目を動かしているにすぎません。'; }, + 'n.agz_fix.ueber8': '自己拠出と合わせるとBBG-RVの8 %を超えます。超過分は税制上も社会保険上も優遇されません。', + 'n.agz_fix_min.knapp': function(p, f){ + return '最低ラインより ' + f.eur2(p.d) + ' 上回っているだけです。これを下回ると定額補助はまるごと' + + '消えます。この点で最後の1ユーロの限界収益は無限大になり、税引前と手取りの対応もそこで不連続です。'; }, + 'n.agz_fix_min.verfehlt': function(p, f){ + return '最低ラインに届いていません。月あと ' + f.eur2(p.fehlt) + ' 出せば ' + f.eur2(p.zuschuss) + + ' の補助が動き出します。これはたいていモデル全体でもっとも割のよい1ユーロです。'; }, + 'n.teilkap.kosten': function(p, f){ + return '全額年金化する場合とくらべて' + (p.gewinn ? '損はなく、むしろ ' + p.dtxt + ' 得になります' + : p.dtxt + ' ' + p.unit + 'の損になります') + + '。理由は二つです。一時金は受け取った年に全額課税され(その時点の限界負担は ' + + f.pct(p.grenz, 1) + ')、その後は商品の収益率ではなく源泉課税後の利回りでしか増えません。'; }, + 'n.av_auszform.horizont': function(p, f){ + return '比較の終期より後に、この年金はさらに ' + f.eur(p.rest) + ' 支払われますが、ここには' + + '算入していません。期間を固定すると終身年金は構造的に過小評価されます。長生きすればここで得をします。'; }, + + /* ---------------- 結果カード ---------------- */ + 'card.lead': '首位', + 'card.unchanged': '変化なし', + 'base.pinned': 'Δ(固定した状態との差)', + 'base.delta': function(p){ return 'Δ ' + p.desc; }, + 'base.none': 'スライダーを動かすと、変化がここにΔとして出ます。', + 'base.desc.prev': '直前の状態との差', + 'base.desc.pinned': '固定した状態との差', + 'base.desc.change': function(p){ return '「' + p.label + '」を変える前との差'; }, + 'rank.swap': function(p, f){ + return '<b>指標どうしが食い違っています。</b>月額給付では' + p.a + 'が' + p.b + + 'を上回りますが、正味収益の現在価値では逆になります。理由は割引率の違いです。現在価値は' + + 'インフレ率(' + f.pct(p.infl, 2) + ')で割り引き、月額給付は税引後の再投資利回り(' + + f.pct(p.wiederanl, 2) + ')で年金化します。この二つが離れているほど、早期の一時金が有利に' + + '見えます。どちらの指標を採るかは、そのお金を運用するか使うかで決まります。「使う」なら' + + '「民間ETF口座のコスト」を、再投資利回りがほぼ0になるように設定してください。'; }, + 'warn.unreachable': 'この手取り負担額は実現できません。給与を超えています。可能な最大値を設定しました。', + 'warn.inactive': '入力内容によれば初年度にはすでに就業していません。賃金転換がないので手取り負担額もなく、そこから総額拠出を逆算することもできません。「退職する年齢」と「現在の年齢」を確認してください。', + + /* ---------------- ひとことの結論 ---------------- */ + 'verdict.lead': function(p){ + return '<b>' + p.satz + '</b>が首位です(' + p.wert + ')。' + p.nom2 + 'を' + p.d2 + + (p.nom3 ? '、' + p.nom3 + 'を' + p.d3 : '') + '上回ります。'; }, + 'verdict.pp': function(p){ return p.v + 'ポイント'; }, + 'verdict.top': function(p, f){ + return p.gen + 'と民間ETF口座の差をもっとも大きく生んでいるのは「' + p.label + '」で、' + + p.gen + 'に' + (p.pro ? '有利' : '不利') + 'に働き、現在価値で ' + f.eur(Math.abs(p.wert)) + + ' にあたります。分解の全体は「価値の要因」にあります。'; }, + 'verdict.band': function(p, f){ + return '<span class="band">この結論はどこまで頑健でしょうか。総収益率が ' + f.pct(p.mid, 1) + + ' ではなく ' + f.pct(p.lo, 1) + ' なら ' + p.vlo + '、' + f.pct(p.hi, 1) + ' なら ' + p.vhi + + ' になります。順位は' + + (p.stabil ? 'この範囲では変わりません。' : '<b>この範囲で入れ替わります</b>。結論はここでは持ちこたえません。') + + '</span>'; }, + 'demo.text': '<b>これはまだ例示の数値です</b>(40歳、年収75,000 €、月300 €の賃金転換、公的医療保険)。「入力」にご自身の数値を入れてください。このお知らせは自動的に消えます。', + + /* ---------------- 追従バー ---------------- */ + 'sb.cap': function(p){ return p.label + ' · ' + p.unit + (p.kaufkraft ? '(現在の購買力)' : ''); }, + 'sb.aria': function(p){ return '結果:' + p.label; }, + + /* ---------------- グラフ ---------------- */ + 'tab.sens': '感応度', + 'tab.wf': '価値の要因', + 'tab.kap': '資産の推移', + 'tab.zer': '月額の内訳', + 'chart.error': function(p){ return 'グラフを描画できません:' + p.msg; }, + 'sens.readout': '破線は現在の値、丸印は民間口座との交点です。曲線の上をなぞるかタップすると値が出ます。', + 'sens.legendY': function(p){ return 'y:' + p.label + '(' + p.unit + ')'; }, + 'sens.note': function(p){ + return '最後に動かしたスライダーを可動域全体で計算しています(モデル実行 ' + p.n + '回)。現在は「' + + p.label + '」です。別のスライダーを動かせば曲線も切り替わります。'; }, + 'sens.noteNetto': function(p, f){ + return ' 注意:このスライダーは手取り負担額も変えます(ここでは月 ' + f.eur2(p.lo) + '〜' + + f.eur2(p.hi) + ')。つまり三つの選択肢に投資される金額そのものが動きます。したがって同じ x ' + + 'における三本の曲線の比較は公平ですが、異なる x のあいだで水準を比べることはできません。' + + '折れ目は算定上限(医療・介護 ' + f.eur(p.bbgkv) + '、年金・雇用 ' + f.eur(p.bbgrv) + + ')と税率表の刻みのところにあります。'; }, + 'wf.up': '収益を上げる', + 'wf.down': '収益を下げる', + 'wf.legend': '正味収益の現在価値(現在の購買力)', + 'wf.check': function(p, f){ + return '検算:' + f.eur(p.start) + ' + ' + f.eur(p.summe) + ' = ' + f.eur(p.start + p.summe) + + '(目標 ' + f.eur(p.ende) + '、差 ' + f.nf2(p.start + p.summe - p.ende) + ' €)。'; }, + 'wf.note': '入れ子式の分解なので合計はぴったり合いますが、<b>順序に依存します</b>。各効果は加法的に分離できず、それぞれの棒は上にある項目を<i>所与としたうえで</i>の寄与を測っています。棒をタップすると導出が出ます。', + 'wf.barinfo': function(p){ return p.label + ':' + p.note; }, + 'wfb.vp.label': '前払一括課税がないこと', + 'wfb.vp.note': '企業年金にも老後資産口座にも前払一括課税(Vorabpauschale、§ 18 InvStG)はありません。民間口座について前払一括課税なしの場合から、ありの場合を引いて測っています。この棒は負にもなり得ます。前払一括課税の多くは貯蓄者概算控除に吸収される一方、取得原価は満額だけ引き上げるため、民間口座にとって正味で有利になることがあるからです。', + 'wfb.zul.label': '加算と特別支出控除', + 'wfb.zul.note': '新しい加算制度による公的資金と、§ 10a EStG による控除です。両方の優遇経路を切って測っています。', + 'wfb.arch.label': '課税構造(残差)', + 'wfb.arch.note': '税率差 t₀ − t₁ とその他すべてです。連鎖がぴったり合うよう残差として求めています。', + 'wfb.agz.label': '事業主補助', + 'wfb.agz.note': '他人のお金であり、税法に由来しない企業年金の唯一の構造的な強みです。補助を切って測っています。', + 'wfb.tarif.label': '税率差とその他', + 'wfb.tarif.note': '払込時の限界税率から受取時の限界税率を引いたもの、およびその他すべてです。連鎖がぴったり合うよう残差として求めています。課税の繰延べそれ自体には価値がありません:(1−t)(1+r)ⁿ = (1+r)ⁿ(1−t)、掛け算は可換です。', + 'wfb.kvpv.label': 'Versorgungsbezüge への医療・介護保険料', + 'wfb.kvpv.note': '§§ 229, 250 SGB V:料率の全額を年金受給者が単独で負担します。「退職後は民間医療保険」に切り替えた場合との差として測っています。', + 'wfb.ep.label': '報酬点数の目減り', + 'wfb.ep.note': '賃金転換は社会保険料の対象賃金を減らし、その分だけ公的年金も減ります。ここでは正味で差し引いています。', + 'kap.rentbeg': '受給開始', + 'kap.swap': '資産 → 年金受給権', + 'kap.legend': '実線:合計 · 破線:自由に使える分', + 'kap.readout': function(p, f){ + return '受給開始時に拘束されている額:企業年金 ' + f.eur(p.bav) + ' のうち ' + f.eur(p.bavGeb) + + '、AV口座 ' + f.eur(p.av) + ' のうち ' + f.eur(p.avGeb) + + '、民間口座 ' + f.eur(p.priv) + ' のうち ' + f.eur(0) + + '。等価の月額給付は消費されるため、どの選択肢も最後は0になります。'; }, + 'kap.note': function(p, f){ + return '商品内の残高です。受給期にはそこへ、税引後に再投資した受取額(税引後 ' + + f.pct(p.wiederanl, 2) + ')を加え、実際に消費される等価の月額給付を差し引いています。' + + 'そのためどの選択肢もちょうど0まで下がります。消費は再投資分からのみ出ていき、商品内の資産は' + + '商品の中に残って税引前の収益率で増えるため、それでも曲線は比例しません。 — 破線は自由に' + + '使える分です。企業年金は受給開始まで全額が拘束され(§ 1b BetrAVG が定めるのは受給権の不喪失で' + + 'あって処分の自由ではありません)、老後資産口座では65歳前の引き出しは優遇を失い、自由なのは' + + '年間上限を超えた分だけ、民間口座はいつでも自由です。一時金がよく見える唯一の軸がこれです。' + + (p.verrentet ? '口座を年金化しているため、そちらでも資産は事業者に移ります。曲線はその分' + + '下がりますが、失われたわけではありません。' : '') + + 'そしてもう一度はっきりと。この曲線が示すのは「この一つの判断から生じる資産」であって、' + + 'あなたの総資産ではありません。'; }, + 'zer.netto': '手取り', + 'zer.tax': '税', + 'zer.kv': '医療・介護', + 'zer.pension': '年金減', + 'zer.legend': '<span><i style="background:#808080"></i>税</span><span><i style="background:#800000"></i>医療・介護</span><span><i style="background:#800080"></i>失う公的年金</span><span style="margin-left:auto">色の付いた土台=手元に残る分</span>', + 'zer.readout': 'いずれも現在の購買力による一定の月額給付として表しています。年金化は線形なので、各ブロックはぴったり足し合わさります。', + 'zer.note': function(p, f){ + return '対象期間は三つの選択肢で同一です。一時金は12で割るのではなく、税引後の再投資利回り(' + + f.pct(p.wiederanl, 2) + ' 年率)で同じ期間にわたって年金化しています。資産を運用せずに' + + '使ってしまう場合は、これより不利になります。'; }, + + /* ---------------- 計算の内訳 ---------------- */ + 'det.h.bav': '企業年金', + 'det.h.av': 'AV口座', + 'det.h.priv': '民間', + 'det.h.kennzahl': '項目', + 'det.h.wert': '値', + 'det.erg.netto': '手取りからの払込み合計(名目)', + 'det.erg.fremd': '他人のお金(補助・加算)', + 'det.erg.kapital': '受給開始時の資産額', + 'det.erg.brutto': '受取総額(税引前)の合計', + 'det.erg.steuer': 'うち所得税・源泉課税', + 'det.erg.kvpv': 'うち医療・介護保険料', + 'det.erg.rentenverlust': '公的年金の目減り(累計)', + 'det.erg.nettoausz': '正味受取額の合計(名目)', + 'det.erg.bwausz': '正味受取額の現在価値', + 'det.erg.bwaufw': '手取り負担額の現在価値', + 'det.erg.ertrag': '正味収益の現在価値', + 'det.erg.verhaeltnis': '受取と負担の比', + 'det.erg.irr': '内部収益率(名目)', + 'det.erg.vorsprung': '民間口座に対する差', + 'det.mon.brutto': '月あたりの給付(税引前)', + 'det.mon.steuer': '差引:所得税・源泉課税', + 'det.mon.kvpv': '差引:医療・介護保険料', + 'det.mon.ep': '差引:公的年金の目減り', + 'det.mon.netto': '月あたりの手取り(現在の購買力)', + 'det.diag.beitrag': '企業年金の月額拠出(税引前)', + 'det.diag.aufwand': '月あたりの手取り負担額(初年度)', + 'det.diag.grenzsteuer': '積立期の限界税率(初年度)', + 'det.diag.ersparnis': '節約率の合計(税+社会保険料、初年度)', + 'det.diag.svAG': '事業主の実際の社会保険料節約', + 'det.diag.svAN': '被用者の実際の社会保険料節約', + 'det.diag.ngef': '優遇のない企業年金拠出の割合(初年度)', + 'det.diag.aussch': '請求権の消化率(BBG-RVの4 %)', + 'det.diag.agzVar': 'うち事業主補助:定率分(初年度)', + 'det.diag.agzFix': 'うち事業主補助:定額分(初年度)', + 'det.diag.agzFixUeber4': '4 %枠を超える定額補助(負担として未計上)', + 'det.diag.vbMonat': '受給開始時の月あたり Versorgungsbezug', + 'det.diag.freibetrag': '同年の Versorgungsbezüge 非課税枠', + 'det.diag.vbQuote': 'Versorgungsbezug と非課税枠の比', + 'det.diag.kapFreiKapital': '保険料がかからない資産額(一時金の場合)', + 'det.diag.kapFreiRente': '保険料がかからない資産額(終身年金の場合)', + 'det.diag.memoRest': '備考:終期より後の企業年金支給額', + 'det.diag.memoRestAv': '備考:終期より後の口座年金', + 'det.diag.wiederanl': '税引後の再投資利回り', + 'det.diag.kapstEff': '実効源泉課税率(連帯付加税・教会税込み)', + 'det.diag.bestAnteil': '公的年金の課税対象割合', + 'det.warn.lead': '優遇が効く範囲から外れています:', + 'det.warn.aussch': '消化率が100 %を超えると、賃金転換の法律上の請求権はもうありません。', + 'det.warn.ngef': '拠出のうち無視できない部分が税制上も社会保険上も優遇されていません。この層は保険の外枠に入るだけで、見返りは医療保険料の負担義務のみです。', + 'det.warn.vb': 'Versorgungsbezug が非課税枠を大きく超えています。定額の非課税枠が守る力は 1/資産額 のように減っていきます。', + 'det.anspar.h.jahr': '年', + 'det.anspar.h.brutto': '税引前給与', + 'det.anspar.h.beitrag': '企業年金拠出', + 'det.anspar.h.agzVar': '補助(定率)', + 'det.anspar.h.agzFix': '補助(定額)', + 'det.anspar.h.sv': '社保の節約', + 'det.anspar.h.st': '税の節約', + 'det.anspar.h.netto': '手取り負担額', + 'det.anspar.h.zul': '加算', + 'det.anspar.h.kbav': '資産(企業年金)', + 'det.anspar.h.kav': '資産(AV口座)', + 'det.anspar.h.kpriv': '資産(民間)', + 'det.row.year': function(p){ return p.jahr + '年(' + p.alter + '歳)'; }, + 'det.ausz.h.jahr': '年', + 'det.ausz.h.bavBr': '企業年金(税引前)', + 'det.ausz.h.kvpv': '医療・介護', + 'det.ausz.h.steuer': '税', + 'det.ausz.h.mind': '年金の減少', + 'det.ausz.h.bavNet': '企業年金(手取り)', + 'det.ausz.h.avBr': 'AV(税引前)', + 'det.ausz.h.avNet': 'AV(手取り)', + 'det.ausz.h.privBr': '民間(税引前)', + 'det.ausz.h.privNet': '民間(手取り)', + 'det.ann.intro': '順位は、2026年時点でまだ決着していない法律問題に左右されます。表の数値は主観的な信頼度です。', + 'det.ann.h.annahme': '仮定', + 'det.ann.h.vertrauen': '信頼度', + 'det.ann.h.anmerkung': '備考', + 'det.ann.r1.a': '社会保険の2026年法定値、§ 32a の2026年税率表', + 'det.ann.r1.c': 'SVBezGrV 2026、Steuerfortentwicklungsgesetz', + 'det.ann.r2.a': 'Versorgungsbezüge への医療・介護保険料の賦課', + 'det.ann.r2.c': '§§ 229, 250 SGB V', + 'det.ann.r3.a': '§ 226 Abs. 2 S. 2 SGB V の非課税枠(医療保険)', + 'det.ann.r3.c': '争いなし', + 'det.ann.r4.a': '介護保険では控除枠ではなく免除限度額であること', + 'det.ann.r4.c': '解説記事でもっとも多い誤り', + 'det.ann.r5.a': 'AV口座からの受取に社会保険料がかからないこと', + 'det.ann.r5.c': 'Versorgungsbezüge にあたらないため', + 'det.ann.r6.a': '非優遇層は差額の半分が課税対象であること', + 'det.ann.r6.c': '§ 22 Nr. 5 S. 2 Buchst. b と § 20 Abs. 1 Nr. 6 S. 2 EStG', + 'det.ann.r7.a': '補助は事業主の実際の節約額までであること', + 'det.ann.r7.c': '反対説は15 %を単なる定率とみなす', + 'det.ann.r8.a': '特別支出控除の上限額 2,340 €', + 'det.ann.r8.c': 'モデル上の仮定。子どもの存在がAV口座の価値を上げるか下げるかを左右する', + 'det.ann.r9.a': '退職後はKVdRの強制加入を前提', + 'det.ann.r9.c': '任意継続被保険者は検討していない', + 'det.ann.notmodelled': '<b>モデル化していないもの:</b>繰上げ受給による減額、賃金転換が傷病手当・失業給付・育児給付に及ぼす影響、遺族保障、支払不能時の保護(PSVaG)、転職時のポータビリティ、介護保険の子ども数による軽減、§ 32d Abs. 6 EStG の有利判定、差押えおよび基礎的生活保障における保護、比較の終期を超える長生きのリスク。', + 'det.prinzip': '<p><i>税引前の拠出額</i>どうしを比べるのは不公平です。賃金転換は税引前から、私的な貯蓄は手取りから出ていくからです。そこで<b>手取り負担額</b>をそろえます。</p>' + + '<p style="font-family:var(--mono);font-size:.82rem;background:var(--chip);padding:9px 11px;border-radius:9px">手取り負担額 = 税引前拠出 − 賃金税の節約 − 節約できた社会保険料(被用者負担分)</p>' + + '<p>この同じ金額を三つの選択肢すべてに投資します。そうして残るのは<i>優遇の仕組み</i>どうしの比較であり、払込額の大小の比較ではありません。</p>' + + '<p><b>課税の繰延べそれ自体には価値がありません。</b>(1−t)·(1+r)ⁿ = (1+r)ⁿ·(1−t)、掛け算は可換だからです。残る価値の源泉は四つだけです。</p>' + + '<ol style="padding-left:1.2em;margin:.3em 0"><li>税率差 t₀ − t₁</li>' + + '<li>他人のお金 — 事業主補助または加算</li>' + + '<li>非課税での積み上がり — 前払一括課税がないこと</li>' + + '<li>一方の選択肢だけにかかる新たな負担 — Versorgungsbezüge への医療・介護保険料</li></ol>' + + '<p>4つめが企業年金の最大の弱点、2つめがその唯一の構造的な強みです。企業年金の収益率は<b>拠出額に対して狭義単調に減少します</b>。非課税枠が定額であり、その保護効果が 1/K のように薄れていくからです。内点の最適解は存在しません。</p>' + + '<p>手取り負担額を与えた場合、税引前の拠出額は二分法で逆算します。N(B) は連続かつ狭義単調増加(dN/dB = 1 − 限界社会保険料率 − 限界税率 > 0)ですが、BBG-RVの4 %と8 %、算定上限、税率表の刻みに折れ点があるため閉じた式では逆に解けません。34回の二分で誤差は0.1セントを下回ります。</p>', + 'det.t.prinzip': '比較の考え方', + 'det.t.erg': '結果の詳細', + 'det.s.erg': '三つの選択肢はいずれも、積立期に手取りからまったく同じ額を負担します。', + 'det.t.mon': '月額給付の内訳', + 'det.s.mon': 'すべての選択肢を同じ期間で年金化し、現在の購買力で表しています。年金化は線形なので、各行はぴったり足し合わさります。', + 'det.t.diag': '診断', + 'det.s.diag': '何が効いているのか、そしてどこで優遇の範囲から外れているのか。', + 'det.t.anspar': '積立期(年ごと)', + 'det.t.ausz': '受給期(年ごと)', + 'det.t.ann': '仮定の頑健性', + + /* ---------------- 保存と共有 ---------------- */ + 'msg.linkCopied': 'リンクをコピーしました。開いた人にはこの数値がそのまま見えます。どれもサーバーを経由しません。', + 'msg.linkFailed': 'コピーできませんでした。リンクはアドレス欄に入っています。', + 'msg.loaded': '読み込みました。', + 'msg.loadError': function(p){ return 'ファイルを読めませんでした:' + p.msg; }, + 'msg.reset': '基準ケースに戻しました。', + + /* ---------------- PWA ---------------- */ + 'pwa.offline': '<b>オフラインでも使えます。</b>アプリはブラウザのキャッシュに入り、接続がなくても動きます。インストールするには、iPhoneでは「共有」→「ホーム画面に追加」、Androidではメニュー →「アプリをインストール」を選んでください。', + 'pwa.installAsk': '<b>アプリとしてインストールしますか。</b>ホーム画面に専用のアイコンで置かれ、オフラインでも動きます。', + 'pwa.installBtn': 'インストール', + 'pwa.installed': '<b>インストールしました。</b>ほかのアプリと並んで見つかります。', + 'pwa.declined': '了解しました。このままでも動きますし、オフラインでも使えます。', + 'pwa.file': '<b>ファイルを直接開いています</b>(<kbd>file://</kbd>)。計算も保存もすべて動きます。欠けているのはPWAの二つの性質だけです。ホーム画面のアイコンと、オフラインキャッシュがありません。もっともキャッシュはここでは不要です。ファイルはすでに端末上にあるからです。アプリとして完全に使うには、フォルダを任意のHTTPSサーバーに置いて、そこから開いてください。' +}; diff --git a/app/i18n.la.js b/app/i18n.la.js new file mode 100644 index 0000000..ca113ad --- /dev/null +++ b/app/i18n.la.js @@ -0,0 +1,574 @@ +/* ===================================================================== + Lexicon Latinum. Iidem clavium tituli ac in i18n.de.js; quod hic deest, + ex lexico Germanico suppletur. + Locorum legalium citationes Germanicae manent de industria: fontes + primarii sunt neque versionem publicam Latinam habent. + ===================================================================== */ +window.I18N_LA = { + 'f.I:gehalt.label': 'Merces annua bruta hodierna', + 'f.I:gehalt.hint': 'Sine solutionibus singularibus, si opus est cum parte mercedis tertiae decimae. Definit quotam tributi marginalem tuam atque utrum limites contributionum socialium superes.', + 'f.I:beitrag.label': 'Collatio menstrua bruta in pensionem operativam', + 'f.I:beitrag.hint': 'Conversio mercedis brutae secundum § 1a BetrAVG. Ius petendi tibi est solum usque ad 4 % limitis contributionis pensionariae (BBG-RV); ultra id consensus conductoris requiritur.', + 'f.I:netto_ziel.label': 'Impensa pura menstrua', + 'f.I:netto_ziel.hint': 'Summa qua reditus tuus in usum paratus primo anno minuitur — consumptio re vera omissa, ideoque variabilis oeconomice recta. Haec ipsa summa in omnibus tribus rationibus collocatur; ea est condicio comparationis aequae. Cum collatione bruta supra posita uno ad unum cohaeret: N(B) = B − compendium contributionum − compendium tributi crescit stricte, ergo invertibilis est. Formula clausa non datur propter flexus ad limites contributionum et ad terminos tabulae tributariae; ideo 34 bisectiones — error infra decimam centesimae partis nummi.', + 'f.I:agzus.label': 'Subsidium conductoris', + 'f.I:agzus.hint': 'Minimum legitimum 15 % (§ 1a Abs. 1a BetrAVG); multi conductores plus solvunt. Aequilibrium adversus depositum privatum fere ibi est ubi (1+z)·(1−0,211) ≈ 1, id est z ≈ 26,7 % — id curva sensibilitatis probare licet. In modo „compendium verum“ hic numerus saepe attingi non potest, quia subsidium prius saturatur.', + 'f.I:agz_fix.label': 'Subsidium fixum additicium', + 'f.I:agz_fix.hint': 'Subsidium certae summae, a collatione tua non pendens — apud Deutsche Bahn et in multis pactis collectivis usitatum, ibi praeter 15 % secundum § 1a Abs. 1a BetrAVG. Est donum a conductore datum secundum § 3 Nr. 63 EStG et occupat PRIORE loco et immunitatem 8 % et immunitatem contributionum 4 % (§ 1 Abs. 1 S. 1 Nr. 9 SvEV): conversio tua propria eatenus expellitur et carior fit. Oeconomice fundamentum est, non vectis: fructum marginalem nummi proximi non mutat (∂²fructus/∂agz_fix ∂collatio ≤ 0, per expulsionem eum etiam minuere potest), mediocritatem autem valde tollit. In curva sensibilitatis ideo fere affinis apparet — donec immunitas 4 % vel 8 % exhausta est, ubi flectitur.', + 'f.I:agz_fix_dyn.label': 'Subsidium fixum indice augere', + 'f.I:agz_fix_dyn.hint': 'Summae fixae plerumque nominaliter in pactis collectivis scribuntur et solum in novis pactionibus tolluntur — ideo praescriptum est „nominaliter constans“. Per 27 annos inflatione 2 % manet factor 1,02⁻²⁷ ≈ 0,59; cui id nimis triste videtur, „cum mercede“ eligat et subsidium verum accipiat cuius incrementum est gsteig − infl.', + 'f.I:agz_fix_dyn.o0': 'nominaliter constans', + 'f.I:agz_fix_dyn.o1': 'cum mercede', + 'f.I:agzus_sv.label': 'Modus subsidii', + 'f.I:agzus_sv.hint': '„Compendium verum“ verba legis sequitur: § 1a Abs. 1a BetrAVG subsidium debet solum QUATENUS conductor contributiones sociales re vera lucratur. Si merces tua utrumque limitem superat, nihil lucratur et nihil debet. Ideo regula supra id in casu fundamentali circa 21 % saturatur. „Collatio tota“ promissio voluntaria vel pactionis est, „pars immunis“ consuetudo late usitata. Fides huius lectionis strictae circa 75 %.', + 'f.I:agzus_sv.o0': 'collatio tota', + 'f.I:agzus_sv.o1': 'pars immunis', + 'f.I:agzus_sv.o2': 'compendium verum', + 'f.I:dyn.label': 'Collationem indice augere', + 'f.I:dyn.hint': 'Si collatio cum mercede crescit, usus immunitatis 8 % per annos fere constans manet. Si nominaliter constans manet, valor eius verus cum inflatione decrescit — et cum eo impensa pura quam duae ceterae rationes ad comparationem accipiunt.', + 'f.I:dyn.o1': 'cum mercede', + 'f.I:dyn.o0': 'nominaliter constans', + 'f.I:rendite.label': 'Fructus brutus ETF in annum', + 'f.I:rendite.hint': 'Ante sumptus, ante tributa. Idem ETF in omnibus tribus rationibus — ita demum architecturae subsidiorum comparantur, non consilia collocandi.', + 'f.I:alter.label': 'Aetas hodierna', + 'f.I:alter.hint': 'Initium temporis congerendi. Annus 0 = 2026.', + 'f.I:alter.unit': 'anni', + 'f.I:rentbeg.label': 'Initium solutionum', + 'f.I:rentbeg.hint': 'Initium temporis solvendi in omnibus tribus rationibus. Depositum senectutis non ante aetatem 65 solvi potest (reformatio anni 2026).', + 'f.I:rentbeg.unit': 'anni', + 'f.I:kv_ruhe.label': 'Assecuratio valetudinaria in senectute', + 'f.I:kv_ruhe.hint': 'Vectis decretorius. In assecuratione publica (GKV) pensiones operativae inter Versorgungsbezüge numerantur et totam quotam contributionis ferunt (§§ 229, 250 SGB V); provisio privata non. Haec sola optio exitum invertit.', + 'f.I:kv_ruhe.o1': 'publica', + 'f.I:kv_ruhe.o2': 'privata', + 'f.I:auszform.label': 'Solutio pensionis operativae', + 'f.I:auszform.hint': 'Summa una: anno accepto tota tributo obnoxia, contributiones valetudinariae per 120 menses distributae (§ 229 Abs. 1 S. 3 SGB V). Annuitas praestationem per reliquam vitam extendit et ideo multiplex capitale sub eadem summa immuni tegit. Exemplar aut totum aut nihil novit; solutio partim capitalis ex pensione operativa non exprimitur.', + 'f.I:auszform.o1': 'summa una', + 'f.I:auszform.o2': 'annuitas perpetua', + 'f.I:teilkap.label': 'Depositum senectutis: summa una initio', + 'f.I:teilkap.hint': 'Usque ad 30 % capitalis congesti initio temporis solvendi ut summa una accipi possunt — in utraque forma solvendi. Bis constat: progressio tota anno accepto, deinde solum usura post tributum retentum. Excessus supra summam annuam maximam („manica“) sine summa una in annuitatem vertitur; ea res exempli consuetudo est, non regula iuris.', + 'f.I:av_auszform.label': 'Depositum senectutis: reliquum', + 'f.I:av_auszform.hint': 'Quid fiat de capitali ultra summam unam — a consilio de pensione operativa supra posito seiunctum. Ratio solvendi: pecunia collocata manet et cum usuris extrahitur; stratum non subsidiatum dimidio discrimine tributatur. Annuitas perpetua: capitale ad institutum transit, pro quo stratum non subsidiatum solum parte fructus tributatur (§ 22 Nr. 5 S. 2 Buchst. a EStG, 17 % ab aetate 67 loco circa 26 % hic). Stratum subsidiatum utroque modo totum postea tributatur; id non mutatur.', + 'f.I:av_auszform.o1': 'ratio solvendi', + 'f.I:av_auszform.o2': 'annuitas perpetua', + 'f.I:agz_fix_min.label': 'Collatio propria minima pro subsidio fixo', + 'f.I:agz_fix_min.hint': 'Rationes „matching“ summam fixam collationi propriae minimae alligant („qui saltem X convertit, Y accipit“). 0 significat sine condicione — tum subsidium omni anno activo fluit, etiam collatione nulla. Cave semanticam exemplaris: subsidium vere incondicionatum non esset effectus differentialis consilii de conversione et pensioni operativae in hac comparatione faveret, quamquam etiam sine conversione flueret. Solum id inscribe quod re vera a participatione pendet.', + 'f.I:rentfak_av.label': 'Factor annuitatis, depositum senectutis', + 'f.I:rentfak_av.hint': 'Solum si depositum in annuitatem vertitur. Praescriptum ut in pensione operativa; contractus depositi involucro assecurationis caret et ideo vilior esse potest — valorem ex oblatione vera inscribe. Ad 0 nulla annuitas est.', + 'f.I:rentfak_av.unit': '€ pro 10.000 €', + 'f.I:rentdyn_av.label': 'Incrementum annuitatis depositi in annum', + 'f.I:rentdyn_av.hint': 'Participatio lucri vel progressus fundi tempore solvendi contractus depositi.', + 'f.I:rentfak.label': 'Factor annuitatis, pensio operativa', + 'f.I:rentfak.hint': 'Solum in forma „annuitas“. 26 € menstrui pro 10.000 € quotae extractionis circa 3,1 % respondent. In libello informationis de producto invenitur.', + 'f.I:rentfak.unit': '€ pro 10.000 €', + 'f.I:rentdyn.label': 'Incrementum annuitatis operativae in annum', + 'f.I:rentdyn.hint': 'Participatio lucri vel progressus fundi tempore solvendi.', + 'f.I:fuenftel.label': 'Regulam quintae partis probare', + 'f.I:fuenftel.hint': '§ 34 EStG ad summam unam ex contractibus § 3 Nr. 63 ex consilio solutam solum per exceptionem pertinet, secundum BFH X R 23/15. Caute: non.', + 'f.I:fuenftel.o0': 'non (caute)', + 'f.I:fuenftel.o1': 'ita', + 'f.I:planende.label': 'Terminus comparationis usque ad aetatem', + 'f.I:planende.hint': 'Rationes solvendi huc usque currunt. Annuitates perpetuae eodem termino comparantur; quod postea fluit, sub „Diagnosis“ ut memoria ponitur. Qui diu vivit, ibi vincit.', + 'f.I:planende.unit': 'anni', + 'f.I:ausscheid.label': 'Discessus ex opere', + 'f.I:ausscheid.hint': 'Hinc nulla merces et nullae collationes. Capitale usque ad initium solutionum crescere pergit.', + 'f.I:ausscheid.unit': 'anni', + 'f.I:tzalter.label': 'Labor imminutus ab aetate', + 'f.I:tzalter.hint': 'Quilibet valor ab initio solutionum significat: nullus labor imminutus. Ideo 67 praescriptum est.', + 'f.I:tzalter.unit': 'anni', + 'f.I:tzfaktor.label': 'Factor laboris imminuti', + 'f.I:tzfaktor.hint': 'Pars mercedis plenae. 60 % = munus 60 %.', + 'f.I:gsteig.label': 'Incrementum mercedis in annum', + 'f.I:gsteig.hint': 'Provehit etiam limites contributionum, summam referendi (Bezugsgröße), mercedem mediam et valorem pensionis.', + 'f.I:stkl.label': 'Modus aestimandi', + 'f.I:stkl.hint': 'Refert MODUS AESTIMANDI, non classis tributi salarii: classis solum retentionem intra annum regit, aestimatio annua tamen divisionem coniugalem secundum § 32a Abs. 5 EStG adhibet. Si „coniunctim“, reditum coniugis omnino inscribe.', + 'f.I:stkl.o1': 'singillatim', + 'f.I:stkl.o3': 'coniunctim (divisio)', + 'f.I:partner.label': 'Reditus coniugis tributo obnoxius', + 'f.I:partner.hint': 'Solum in aestimatione coniuncta. Computatur 2·ESt((zvE + zvE_coniugis)/2).', + 'f.I:kist.label': 'Quota tributi ecclesiastici', + 'f.I:kist.hint': '8 % in Bavaria et Baden-Württemberg, alibi 9 %, 0 si ex ecclesia exiisti. Valet etiam ad tributum retentum: 0,25/(1+0,25k)·(1+0,055+k).', + 'f.I:tarifanp.label': 'Accommodatio terminorum tabulae in annum', + 'f.I:tarifanp.hint': 'Tota tabula factore λ = (1+x)^t extenditur: ESt = s·λ·T(zvE/s/λ). Id prorsus idem est ac omnes terminos proportione movere. 0 % progressionem frigidam integram ostendit.', + 'f.I:kinder.label': 'Numerus liberorum', + 'f.I:kinder.hint': 'Additamentum liberorum in deposito senectutis: 1 € pro quoque € illato usque ad 300 € in singulos liberos.', + 'f.I:kind_jahre.label': 'Anni cum additamento liberorum', + 'f.I:kind_jahre.hint': 'Additamentum liberorum a iure ad Kindergeld pendet (§ 85 EStG), ideo per totum tempus congerendi non currit.', + 'f.I:kind_jahre.unit': 'anni', + 'f.I:kinderlos.label': 'Sine liberis (accessio curationis)', + 'f.I:kinderlos.hint': '0,6 puncta centesima accedunt ad contributionem curationis, ab operario solo ferenda (§ 55 Abs. 3 SGB XI).', + 'f.I:kinderlos.o0': 'non', + 'f.I:kinderlos.o1': 'ita', + 'f.I:k_bav.label': 'Sumptus producti operativi in annum', + 'f.I:k_bav.hint': 'Involucrum assecurationis cum TER. Sumptus effectivi in libello informationis stant — ibi lege, noli coniectare.', + 'f.I:k_av.label': 'Sumptus depositi senectutis in annum', + 'f.I:k_av.hint': 'In deposito normali sumptus effectivi lege ad 1,0 % in annum finiuntur.', + 'f.I:k_priv.label': 'Sumptus depositi privati ETF in annum', + 'f.I:k_priv.hint': 'Merum TER fundi indicis mundani lati.', + 'f.I:infl.label': 'Inflatio in annum', + 'f.I:infl.hint': 'Quota deducendi pro omnibus quae „in vi emendi hodierna“ dicuntur. Ordinem vix mutat, omnia autem plana movet.', + 'f.I:sonst_zve.label': 'Ceteri reditus tributo obnoxii in senectute', + 'f.I:sonst_zve.hint': 'Praecipue pars pensionis publicae tributo obnoxia, sine productis hic comparatis. Definit quotam marginalem tempore solvendi — atque ita arbitrium tabularum t₀ − t₁.', + 'f.I:rente_br.label': 'Pensio publica bruta in annum', + 'f.I:rente_br.hint': 'Solum propter finem in assecuratione valetudinaria: pensio publica et Versorgungsbezüge UNUM limitem contributionis participant (§ 223 Abs. 3 SGB V).', + 'f.P:bbg_rv.label': 'Limes contributionis, assecuratio pensionaria (in annum)', + 'f.P:bbg_rv.hint': 'SVBezGrV 2026 (BGBl. 2025 I Nr. 116), per totam rem publicam idem.', + 'f.P:bbg_kv.label': 'Limes contributionis, assecuratio valetudinaria et curationis', + 'f.P:bbg_kv.hint': 'SVBezGrV 2026; 5.812,50 € in mensem.', + 'f.P:bezug.label': 'Summa referendi (in mensem)', + 'f.P:bezug.hint': 'SVBezGrV 2026, 47.460 € in annum. Vicesima pars eius est summa immunis pro Versorgungsbezüge in assecuratione valetudinaria (§ 226 Abs. 2 S. 2 SGB V).', + 'f.P:de_rv.label': 'Merces media, assecuratio pensionaria', + 'f.P:de_rv.hint': 'Anlage 1 ad SGB VI, valor anni 2026 — denominator punctorum mercedis.', + 'f.P:rw.label': 'Valor pensionis currens', + 'f.P:rw.hint': 'Rentenwertbestimmungsverordnung 2026, a die 1 Iulii 2026: 42,52 € (+4,24 %).', + 'f.P:rw.unit': '€', + 'f.P:rv_an.label': 'Assecuratio pensionaria, pars operarii', + 'f.P:rv_an.hint': '18,6 % dimidiata.', + 'f.P:av_an.label': 'Assecuratio contra inopiam operis, pars operarii', + 'f.P:av_an.hint': '2,6 % dimidiata.', + 'f.P:kv_allg.label': 'Assecuratio valetudinaria, quota generalis', + 'f.P:kv_allg.hint': '§ 241 SGB V.', + 'f.P:kv_zus.label': 'Assecuratio valetudinaria, quota addita media', + 'f.P:kv_zus.hint': 'A ministerio sanitatis promulgata; pro singulis societatibus differt — quotam societatis tuae inscribe.', + 'f.P:pv_ges.label': 'Assecuratio curationis, quota tota', + 'f.P:pv_ges.hint': '§ 55 SGB XI. Deminutiones a secundo libero non exprimuntur.', + 'f.P:pv_an.label': 'Assecuratio curationis, pars operarii', + 'f.P:pv_an.hint': 'In Saxonia aliter (2,3 %).', + 'f.P:pv_kl.label': 'Accessio curationis pro sine liberis', + 'f.P:pv_kl.hint': '§ 55 Abs. 3 SGB XI, ab operario solo.', + 'f.P:gfb.label': 'Summa immunis fundamentalis (finis zonae 1)', + 'f.P:gfb.hint': '§ 32a Abs. 1 EStG in forma legis Steuerfortentwicklungsgesetz.', + 'f.P:e2.label': 'Finis zonae 2', + 'f.P:e2.hint': 'Quota marginalis a 14 % ad 23,97 % crescit.', + 'f.P:e3.label': 'Finis zonae 3', + 'f.P:e3.hint': 'Quota marginalis a 23,97 % ad 42 % crescit.', + 'f.P:e4.label': 'Initium zonae 5 (quota summa)', + 'f.P:e4.hint': 'A 277.826 € valet 45 %.', + 'f.P:a2.label': 'Coefficiens a₂', + 'f.P:a2.hint': 'ESt = (a₂·y + b₂)·y ubi y = (zvE − summa immunis)/10.000.', + 'f.P:b2.label': 'Coefficiens b₂', + 'f.P:a3.label': 'Coefficiens a₃', + 'f.P:a3.hint': 'ESt = (a₃·z + b₃)·z + c₃ ubi z = (zvE − 17.799)/10.000.', + 'f.P:b3.label': 'Coefficiens b₃', + 'f.P:c3.label': 'Constans c₃', + 'f.P:c4.label': 'Constans c₄', + 'f.P:c4.hint': 'ESt = 0,42·zvE − c₄.', + 'f.P:c5.label': 'Constans c₅', + 'f.P:c5.hint': 'ESt = 0,45·zvE − c₅.', + 'f.P:soli.label': 'Accessio solidaritatis', + 'f.P:soli.hint': 'SolzG 1995.', + 'f.P:soli_fg.label': 'Limes immunitatis accessionis solidaritatis', + 'f.P:soli_fg.hint': '2026, tabula fundamentalis, ad tributum constitutum relatus; in divisione duplex. Praeterea zona mitigationis.', + 'f.P:soli_mz.label': 'Zona mitigationis, onus marginale', + 'f.P:soli_mz.hint': '§ 3 Abs. 2a SolzG.', + 'f.P:an_pausch.label': 'Deductio forfaitaria operarii', + 'f.P:an_pausch.hint': '§ 9a S. 1 Nr. 1a EStG.', + 'f.P:sa_pausch.label': 'Deductio forfaitaria impensarum specialium', + 'f.P:sa_pausch.hint': '§ 10c EStG.', + 'f.P:kapst.label': 'Quota tributi retenti', + 'f.P:kapst.hint': '§ 32d Abs. 1 EStG.', + 'f.P:tfs.label': 'Immunitas partialis, fundi actionum', + 'f.P:tfs.hint': '§ 20 Abs. 1 Nr. 3 cum § 20 InvStG, pars actionum supra 50 %.', + 'f.P:sparerpb.label': 'Summa immunis parcentis', + 'f.P:sparerpb.hint': '§ 20 Abs. 9 EStG; in divisione duplex. Magnam partem summae anticipatae (Vorabpauschale) absorbet — vide cataractam.', + 'f.P:basiszins.label': 'Usura fundamentalis summae anticipatae', + 'f.P:basiszins.hint': 'Epistula BMF diei 13 Ianuarii 2026, § 18 Abs. 4 InvStG (2025: 2,53 %). Ad 0 posita significat: nulla summa anticipata.', + 'f.P:vp_faktor.label': 'Factor fructus fundamentalis', + 'f.P:vp_faktor.hint': '§ 18 Abs. 1 S. 1 InvStG.', + 'f.P:st_frei_q.label': 'Immune secundum § 3 Nr. 63 (pars BBG-RV)', + 'f.P:st_frei_q.hint': '8 % limitis BBG-RV = 8.112 € anno 2026.', + 'f.P:sv_frei_q.label': 'Immune a contributionibus (pars BBG-RV)', + 'f.P:sv_frei_q.hint': '§ 1 Abs. 1 S. 1 Nr. 9 SvEV; 4 % = 4.056 € anno 2026. Simul finis iuris petendi secundum § 1a BetrAVG.', + 'f.P:zul_g1.label': 'Additamentum gradus 1 (pro € collationis propriae)', + 'f.P:zul_g1.hint': 'Altersvorsorgereformgesetz, in vigore a mense Maio 2026, adhibendum a die 1 Ianuarii 2027.', + 'f.P:zul_s1.label': 'Finis gradus 1', + 'f.P:zul_s1.hint': 'Usque ad 360 € collationis propriae: 50 centesimae pro €.', + 'f.P:zul_g2.label': 'Additamentum gradus 2 (pro € collationis propriae)', + 'f.P:zul_g2.hint': 'A 361 ad 1.800 €: 25 centesimae pro €.', + 'f.P:zul_s2.label': 'Finis gradus 2', + 'f.P:zul_s2.hint': 'Summa maxima collationis subsidiatae; efficit additamentum fundamentale maximum 540 €.', + 'f.P:kind_zul.label': 'Additamentum in singulos liberos', + 'f.P:kind_zul.hint': '1 € pro quoque € illato usque ad 300 € in singulos liberos.', + 'f.P:av_max.label': 'Summa maxima illationis in annum', + 'f.P:av_max.hint': 'BMF-FAQ de reformatione provisionis privatae subsidiatae, status diei 5 Maii 2026. Quod supra est, in tramitem secundarium currit qui ut depositum privatum tributatur.', + 'f.P:av_sa_max.label': 'Summa maxima deductionis specialis § 10a', + 'f.P:av_sa_max.hint': 'SUPPOSITIO EXEMPLARIS, fides circa 70 %. Collatio propria usque ad 1.800 € cum additamentis. Hic unus numerus decernit utrum liberi valorem depositi senectutis tollant an minuant.', + 'f.P:ertragsq.label': 'Pars fructus (aetate 67)', + 'f.P:ertragsq.hint': '§ 22 Nr. 1 S. 3 Buchst. a Doppelbuchst. bb EStG, pro partibus annuitatis non subsidiatis.', + 'f.P:halb.label': 'Pars discriminis tributo obnoxia', + 'f.P:halb.hint': '§ 20 Abs. 1 Nr. 6 S. 2 EStG per analogiam (contractus ultra 12 annos, solutio post 62): dimidium discrimen. Fides circa 80 %.', + + /* ---------------- Compages, tituli fenestrarum, caput et calx ---------------- */ + 'app.title': 'Comparatio provisionis senectutis', + 'app.meta': 'Pensionem operativam, depositum senectutis et depositum privatum ETF eadem impensa pura comparare. Ius anni 2026.', + 'hdr.title': 'Comparatio provisionis senectutis', + 'hdr.sub': 'Pensio operativa, depositum senectutis et depositum privatum ETF — <b>eadem impensa pura</b> et eodem ETF. Ius Germanicum anni 2026.', + 'win.header': 'Comparatio provisionis senectutis', + 'win.result': 'Exitus', + 'win.input': 'Numeri tui', + 'win.chart': 'Diagrammata', + 'win.extra': 'Plura', + 'win.calc': 'Ratio computandi', + 'win.share': 'Servare et communicare', + 'win.min': 'Minuere', + 'win.restore': 'Restituere', + 'win.n': function(p){ return 'Fenestra ' + p.n; }, + 'input.sub': 'Numeri ad omnem mutationem statim denuo computantur. Omne spatium se ipsum explicat per <span class="qm" style="cursor:default">?</span>.', + 'extra.sub': 'Omnia hic praescripta sunt rationabiliter. Tange solum quod ad te pertinet.', + 'calc.sub': 'Iis qui rationem sequi volunt.', + 'share.sub': 'Quae inscribis in hoc navigatro sola manent (<kbd>localStorage</kbd>). Nihil transmittitur, nullus servus est, nulla vestigia.', + 'ui.share': 'Nexum cum numeris meis exscribere', + 'ui.export': 'In tabellam servare', + 'ui.import': 'Tabellam legere', + 'ui.reset': 'Omnia restituere', + 'ui.pin.set': 'Ut normam figere', + 'ui.pin.clear': 'Normam solvere', + 'ui.lang': 'Lingua', + 'ui.lang.de': 'Germanice', + 'ui.lang.en': 'Anglice', + 'ui.lang.la': 'Latine', + 'ui.lang.ja': 'Iaponice', + 'ui.lang.aria': 'Linguam eligere', + 'sb.up': 'Sursum ad exitum', + 'footer.1': '<b>Non est consilium tributarium neque de collocando.</b> Exemplar non est contractus. Ius anni 2026 de provisione privata reformata pluribus locis nondum certum est; suppositiones minimae fidei sub „Ratio computandi → Firmitas suppositionum“ stant et ibi mutari possunt.', + 'footer.2': 'Nucleus computandi ad litteram ex exemplari Excel <span class="pill">bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx</span> translatus et tribus casibus ad praecisionem machinae probatus (maxima discrepantia relativa 1,3·10<sup>−14</sup>).', + + /* ---------------- Tres rationes ---------------- */ + 'opt.bav.name': 'Pensio operativa (conversio mercedis)', + 'opt.bav.short': 'operativa', + 'opt.bav.nom': 'pensio operativa', + 'opt.bav.satz': 'Pensio operativa', + 'opt.bav.gen': 'pensionis operativae', + 'opt.av.name': 'Depositum senectutis', + 'opt.av.short': 'depositum', + 'opt.av.nom': 'depositum senectutis', + 'opt.av.satz': 'Depositum senectutis', + 'opt.av.gen': 'depositi senectutis', + 'opt.priv.name': 'Depositum privatum ETF', + 'opt.priv.short': 'privatum', + 'opt.priv.nom': 'depositum privatum ETF', + 'opt.priv.satz': 'Depositum privatum ETF', + 'opt.priv.gen': 'depositi privati ETF', + + /* ---------------- Mensurae ---------------- */ + 'metric.monat.label': 'Praestatio menstrua', + 'metric.monat.unit': '€/mens.', + 'metric.monat.desc': 'Solutio menstrua eiusdem summae per totum tempus solvendi, in vi emendi hodierna. Unicus numerus qui cum reditu tuo puro hodierno statim comparari potest.', + 'metric.ertrag.label': 'Fructus purus (valor praesens)', + 'metric.ertrag.unit': '€', + 'metric.ertrag.desc': 'Quantum ratio in vi emendi hodierna ultra impensam tuam reddat: valor praesens omnium praestationum purarum minus valor praesens impensae purae.', + 'metric.irr.label': 'Usura interna', + 'metric.irr.unit': '% p.a.', + 'metric.irr.desc': 'Fructus summae purae re vera impensae, nominaliter. Cum fructu bruto ETF quem posuisti recta comparabilis — discrimen est prorsus id quod subsidia et onera simul constant aut afferunt.', + 'metric.kapital.label': 'Capitale initio solutionum', + 'metric.kapital.unit': '€', + 'metric.kapital.desc': 'Ante tributa et ante contributiones, nominaliter. Consulto NON est variabilis decernens: pensio operativa hic saepe praecedit, in solvendo autem perdit.', + 'metric.pa': ' p.a.', + 'metric.pp': ' punct.', + 'metric.short.monat': 'mens.', + 'metric.short.ertrag': 'fructus', + 'metric.short.irr': 'IRR', + 'metric.short.kapital': 'capitale', + + /* ---------------- Spatia inscribenda ---------------- */ + 'unit.pct': '%', + 'unit.eur': '€', + 'unit.eurm': '€/mens.', + 'ui.hintFor': function(p){ return 'Explicatio ad: ' + p.label; }, + 'ui.couple': '<span>⇅</span> Duo aspectus eiusdem rei. Alterum muta, alterum sequitur.', + 'ui.cnt.legal': function(p){ return p.n + ' numeri legitimi'; }, + 'ui.cnt.fields': function(p){ return p.n + ' spatia'; }, + 'ui.legalNote': 'Numeri legitimi cum fontibus. Muta solum si ius mutatum est aut si societas tua differt.', + 'group.bav': 'Subsidium et formae solvendi accuratius', + 'group.person': 'Tempora, discessus, labor imminutus', + 'group.steuer': 'Tributum, aestimatio, liberi', + 'group.markt': 'Sumptus et inflatio', + 'group.ruhe': 'Senectus', + 'group.p_sv': 'Numeri legitimi: assecuratio socialis 2026', + 'group.p_est': 'Numeri legitimi: tabula tributi in reditus 2026 (§ 32a EStG)', + 'group.p_kap': 'Numeri legitimi: tributum in fructus capitalis et in fundos', + 'group.p_bav': 'Numeri legitimi: provisio operativa et privata subsidiata', + + /* ---------------- Notae vivae ---------------- */ + 'n.beitrag.ueber8': function(p, f){ + return 'Supra 8 % limitis contributionis pensionariae (' + f.eur(p.v8) + ' in annum). Pars superior ' + + 'nec tributo nec contributionibus favetur — in involucrum assecurationis migrat et ibi nihil ' + + 'nisi obligationem contributionis valetudinariae accipit.'; }, + 'n.beitrag.ueber4': function(p, f){ + return 'Supra 4 % limitis contributionis pensionariae (' + f.eur(p.v4) + ' in annum). Huc usque ius ' + + 'petendi habes secundum § 1a BetrAVG; ultra id consensus conductoris requiritur.'; }, + 'n.agzus.gedeckelt': function(p, f){ + return 'Finitum ad ' + f.pct(p.quote, 1) + '. Conductor re vera solum ' + f.eur2(p.ersparnis) + + ' in annum lucratur — valores maiores hic nihil mutant, dum modus subsidii „compendium verum“ manet.'; }, + 'n.agz_fix.fliesstNicht': function(p, f){ + return 'Non fluit: collatio tua menstrua ' + f.eur2(p.beitrag) + ' infra limen minimum ' + + f.eur2(p.schwelle) + ' iacet (spatium sub „Plura“).'; }, + 'n.agz_fix.ueber4': function(p, f){ + return 'Subsidium fixum solum immunitatem 4 % (' + f.eur(p.topf) + ' in annum) excedit summa ' + + f.eur(p.ueber) + '. Ea pars apud te merces contributionibus obnoxia esset — exemplar onus ' + + 'additicium NON compensat et pensionem operativam eatenus nimis aestimat.'; }, + 'n.agz_fix.verdraengung': function(p, f){ + return 'Immunitatem 4 % priore loco occupat: ex collatione tua propria solum ' + f.eur(p.rest) + + ' a contributionibus immunia manent. Impensa tua pura eo crescit — subsidium tamen donum est, ' + + 'finem tantum subsidii proprii movet.'; }, + 'n.agz_fix.ueber8': 'Cum collatione propria simul supra 8 % limitis BBG-RV — excessus nec tributo nec contributionibus favetur.', + 'n.agz_fix_min.knapp': function(p, f){ + return 'Solum ' + f.eur2(p.d) + ' supra limen es. Infra id subsidium fixum totum cadit: fructus ' + + 'marginalis ultimi nummi hoc loco infinitus est, et nexus inter brutum et purum ibi non continuus.'; }, + 'n.agz_fix_min.verfehlt': function(p, f){ + return 'Limen non attigisti — ' + f.eur2(p.fehlt) + ' plus in mensem subsidium ' + f.eur2(p.zuschuss) + + ' excitant. Is fere semper est nummus utilissimus totius exemplaris.'; }, + 'n.teilkap.kosten': function(p, f){ + return 'Constat ' + (p.gewinn ? 'nihil, immo contra: ' + p.dtxt : p.dtxt + ' ' + p.unit) + + ' prae mera annuitate. Causa: summa una anno accepto tota tributo obnoxia est (onus marginale ' + + 'ibi ' + f.pct(p.grenz, 1) + ') et postea solum usura post tributum retentum crescit, non fructu ' + + 'producti.'; }, + 'n.av_auszform.horizont': function(p, f){ + return 'Ultra terminum comparationis haec annuitas adhuc ' + f.eur(p.rest) + ' solvit, quae hic NON ' + + 'numerantur. Terminus fixus omnem annuitatem perpetuam ex ratione minoris aestimat — qui diu ' + + 'vivit, hic vincit.'; }, + + /* ---------------- Chartae exitus ---------------- */ + 'card.lead': 'prior', + 'card.unchanged': 'immutatum', + 'base.pinned': 'Δ prae statu fixo', + 'base.delta': function(p){ return 'Δ ' + p.desc; }, + 'base.none': 'Move regulam — mutatio hic ut Δ apparebit.', + 'base.desc.prev': 'status prior', + 'base.desc.pinned': 'status fixus', + 'base.desc.change': function(p){ return 'ante mutationem spatii „' + p.label + '“'; }, + 'rank.swap': function(p, f){ + return '<b>Mensurae inter se dissentiunt.</b> Secundum praestationem menstruam ' + p.a + ' ante ' + + p.b + ' est, secundum valorem praesentem fructus puri contra. Causa: valor praesens inflatione ' + + 'deducitur (' + f.pct(p.infl, 2) + '), praestatio menstrua usura reinvestiendi post tributa ' + + 'annuitatem facit (' + f.pct(p.wiederanl, 2) + '). Quo latius hae duae distant, eo melior summa ' + + 'una mature accepta videtur. Quae mensura valeat pendet ex eo utrum pecuniam collocaturus an ' + + 'consumpturus sis — si „consumpturus“, spatium „Sumptus depositi privati ETF“ ita pone ut usura ' + + 'reinvestiendi ad nihilum tendat.'; }, + 'warn.unreachable': 'Haec impensa pura attingi non potest — mercedem tuam superat. Positus est valor maximus possibilis.', + 'warn.inactive': 'Primo anno, ut ipse indicasti, iam non laboras — nulla est conversio mercedis, ergo nulla impensa pura ex qua collatio bruta retro computetur. Vide „Discessus ex opere“ et „Aetas hodierna“.', + + /* ---------------- Sententia aperta ---------------- */ + 'verdict.lead': function(p){ + return '<b>' + p.satz + '</b> praestat cum ' + p.wert + ' — ' + p.d2 + ' plus quam ' + p.nom2 + + (p.nom3 ? ' et ' + p.d3 + ' plus quam ' + p.nom3 : '') + '.'; }, + 'verdict.pp': function(p){ return p.v + ' puncta centesima'; }, + 'verdict.top': function(p, f){ + return ' Maximam partem discriminis ' + p.gen + ' a deposito privato ETF efficit „' + p.label + '“ — ' + + (p.pro ? 'idque in favorem ' : 'idque in damnum ') + p.gen + ', valore praesenti ' + + f.eur(Math.abs(p.wert)) + '. Tota resolutio sub „Causae valoris“ stat.'; }, + 'verdict.band': function(p, f){ + return '<span class="band">Quam firmum id est? Fructu bruto ' + f.pct(p.lo, 1) + ' loco ' + + f.pct(p.mid, 1) + ' esset ' + p.vlo + ', fructu ' + f.pct(p.hi, 1) + ' esset ' + p.vhi + + ' — ordo ' + + (p.stabil ? 'per hoc intervallum idem manet.' + : '<b>in hoc intervallo invertitur</b>, ergo exitus hic non sustinet.') + + '</span>'; }, + 'demo.text': '<b>Hi adhuc numeri exempli sunt</b> — annos 40 natus, 75.000 € mercedis brutae in annum, 300 € conversionis in mensem, assecuratione publica valetudinaria. Sub „Numeri tui“ tuos inscribe; tum haec admonitio per se evanescet.', + + /* ---------------- Regula currens ---------------- */ + 'sb.cap': function(p){ return p.label + ' · ' + p.unit + (p.kaufkraft ? ', vi emendi hodierna' : ''); }, + 'sb.aria': function(p){ return 'Exitus: ' + p.label; }, + + /* ---------------- Diagrammata ---------------- */ + 'tab.sens': 'Sensibilitas', + 'tab.wf': 'Causae valoris', + 'tab.kap': 'Cursus capitalis', + 'tab.zer': 'Resolutio menstrua', + 'chart.error': function(p){ return 'Diagramma pingi non potest: ' + p.msg; }, + 'sens.readout': 'Linea intermissa: valor tuus currens. Anuli: puncta intersectionis cum deposito privato. Supra curvam move vel tange ut valores videas.', + 'sens.legendY': function(p){ return 'y: ' + p.label + ' (' + p.unit + ')'; }, + 'sens.note': function(p){ + return 'Regula quam ultimam movisti per totum spatium suum computatur — ' + p.n + + ' cursus exemplaris pleni. Nunc: „' + p.label + '“. Move aliam regulam, et curva sequetur.'; }, + 'sens.noteNetto': function(p, f){ + return ' Cave: haec regula etiam impensam puram mutat (hic ' + f.eur2(p.lo) + ' ad ' + f.eur2(p.hi) + + ' in mensem) atque ita summam quae in omnibus tribus rationibus collocatur. Ideo tres curvae ad ' + + 'unumquodque x aeque comparabiles sunt, planum autem inter diversa x non. Flexus ad limites ' + + 'contributionum sedent (' + f.eur(p.bbgkv) + ' pro valetudine et curatione, ' + f.eur(p.bbgrv) + + ' pro pensione et inopia operis) et ad terminos tabulae tributariae.'; }, + 'wf.up': 'fructum tollit', + 'wf.down': 'fructum minuit', + 'wf.legend': 'valor praesens fructus puri, vi emendi hodierna', + 'wf.check': function(p, f){ + return 'Probatio: ' + f.eur(p.start) + ' + ' + f.eur(p.summe) + ' = ' + f.eur(p.start + p.summe) + + ' (debitum ' + f.eur(p.ende) + ', discrepantia ' + f.nf2(p.start + p.summe - p.ende) + ' €).'; }, + 'wf.note': 'Resolutio telescopica, ideo prorsus convenit — sed <b>ab ordine pendet</b>: effectus additive separari non possunt, quaeque columna partem suam metitur <i>datis</i> iis quae supra sunt. Tange columnam ut derivationem videas.', + 'wf.barinfo': function(p){ return p.label + ': ' + p.note; }, + 'wfb.vp.label': 'Summa anticipata abest', + 'wfb.vp.note': 'Neque pensio operativa neque depositum senectutis summam anticipatam (§ 18 InvStG) novit. Mensum ut depositum privatum sine summa anticipata minus cum ea. Columna negativa esse potest: summa anticipata magna ex parte in summam immunem parcentis currit, basim autem sumptuum tota mensura tollit — tum deposito privato pure prodest.', + 'wfb.zul.label': 'Additamenta et deductio specialis', + 'wfb.zul.note': 'Pecunia aliena secundum novam rationem additamentorum cum deductione secundum § 10a EStG. Mensum utroque tramite subsidii exstincto.', + 'wfb.arch.label': 'Architectura tributaria (reliquum)', + 'wfb.arch.note': 'Arbitrium tabularum t₀ − t₁ et cetera omnia. Ut residuum definitum, ut catena prorsus conveniat.', + 'wfb.agz.label': 'Subsidium conductoris', + 'wfb.agz.note': 'Pecunia aliena — unicum commodum structurale pensionis operativae quod non ex iure tributario venit. Mensum subsidio exstincto.', + 'wfb.tarif.label': 'Arbitrium tabularum et reliquum', + 'wfb.tarif.note': 'Quota marginalis inferendi minus quota efferendi, cum ceteris omnibus. Ut residuum definitum, ut catena prorsus conveniat. Dilatio tributi per se nihil valet: (1−t)(1+r)ⁿ = (1+r)ⁿ(1−t), multiplicatio commutat.', + 'wfb.kvpv.label': 'Contributiones valetudinariae in Versorgungsbezüge', + 'wfb.kvpv.note': '§§ 229, 250 SGB V: totam quotam solus senex fert. Mensum ut discrimen ad optionem „assecuratio privata in senectute“.', + 'wfb.ep.label': 'Iactura punctorum mercedis', + 'wfb.ep.note': 'Conversio mercedis mercedem contributionibus obnoxiam minuit atque ita pensionem publicam. Hic pure compensatum.', + 'kap.rentbeg': 'initium solutionum', + 'kap.swap': 'capitale → ius annuitatis', + 'kap.legend': 'linea continua: summa · intermissa: in promptu', + 'kap.readout': function(p, f){ + return 'Initio solutionum vinctum: operativa ' + f.eur(p.bavGeb) + ' ex ' + f.eur(p.bav) + + ' · depositum ' + f.eur(p.avGeb) + ' ex ' + f.eur(p.av) + + ' · privatum ' + f.eur(0) + ' ex ' + f.eur(p.priv) + + '. Praestatio menstrua aequivalens consumitur, ideo omnis ratio ad nihilum desinit.'; }, + 'kap.note': function(p, f){ + return 'Ratio in producto, tempore solvendi cum solutionibus pure reinvestitis (' + + f.pct(p.wiederanl, 2) + ' post tributa) et minus praestatione menstrua aequivalenti quae re vera ' + + 'consumitur. Ideo omnis ratio ad nihilum exacte decurrit. Quia consumptio sola ex parte ' + + 'reinvestita abit, capitale autem in producto manet ubi fructu ante tributa crescit, curvae tamen ' + + 'non proportionaliter currunt. — Linea intermissa est pars in promptu: pensio operativa usque ad ' + + 'initium solutionum tota vincta est (§ 1b BetrAVG novit ius non amittendum, non facultatem ' + + 'utendi); in deposito senectutis extractio ante 65 subsidium perdit et liber est solum excessus ' + + 'supra summam annuam maximam; depositum privatum semper liberum est. Haec sola est axis in qua ' + + 'summa una bene apparet. ' + + (p.verrentet ? 'Quia depositum in annuitatem vertis, etiam ibi capitale ad institutum transit — ' + + 'curva pro eo cadit, quamquam nihil periit. ' : '') + + 'Et iterum aperte: curvae opes ex HOC uno consilio ostendunt, non totas opes tuas.'; }, + 'zer.netto': 'purum', + 'zer.tax': 'tributum', + 'zer.kv': 'valet./cur.', + 'zer.pension': 'pensio', + 'zer.legend': '<span><i style="background:#808080"></i>tributum</span><span><i style="background:#800000"></i>valetudo et curatio</span><span><i style="background:#800080"></i>pensio publica amissa</span><span style="margin-left:auto">fundamentum coloratum = quod restat</span>', + 'zer.readout': 'Omnia ut praestatio menstrua constans in vi emendi hodierna. Quia annuitas linearis est, partes prorsus se addunt.', + 'zer.note': function(p, f){ + return 'Tempus referendi in omnibus tribus rationibus idem est. Summa una non per duodecim dividitur, ' + + 'sed usura reinvestiendi post tributa (' + f.pct(p.wiederanl, 2) + ' p.a.) per idem tempus in ' + + 'annuitatem vertitur. Qui capitale consumit potius quam collocat, peius stat.'; }, + + /* ---------------- Ratio computandi ---------------- */ + 'det.h.bav': 'operativa', + 'det.h.av': 'depositum', + 'det.h.priv': 'privatum', + 'det.h.kennzahl': 'Mensura', + 'det.h.wert': 'Valor', + 'det.erg.netto': 'Summa ex puro illata (nominaliter)', + 'det.erg.fremd': 'Pecunia aliena (subsidium vel additamenta)', + 'det.erg.kapital': 'Capitale initio solutionum', + 'det.erg.brutto': 'Summa solutionum brutarum', + 'det.erg.steuer': 'ex eo tributum in reditus et retentum', + 'det.erg.kvpv': 'ex eo assecuratio valetudinaria et curationis', + 'det.erg.rentenverlust': 'Iactura pensionis publicae (cumulata)', + 'det.erg.nettoausz': 'Summa solutionum purarum (nominaliter)', + 'det.erg.bwausz': 'Valor praesens solutionum purarum', + 'det.erg.bwaufw': 'Valor praesens impensae purae', + 'det.erg.ertrag': 'Valor praesens fructus puri', + 'det.erg.verhaeltnis': 'Proportio solutionis ad impensam', + 'det.erg.irr': 'Usura interna (nominaliter)', + 'det.erg.vorsprung': 'Praestantia prae deposito privato', + 'det.mon.brutto': 'Praestatio bruta in mensem', + 'det.mon.steuer': 'minus tributum in reditus et retentum', + 'det.mon.kvpv': 'minus assecuratio valetudinaria et curationis', + 'det.mon.ep': 'minus iactura pensionis publicae', + 'det.mon.netto': 'PURUM IN MENSEM (vi emendi hodierna)', + 'det.diag.beitrag': 'Collatio bruta operativa in mensem', + 'det.diag.aufwand': 'Impensa pura in mensem (primo anno)', + 'det.diag.grenzsteuer': 'Quota marginalis tempore congerendi (primo anno)', + 'det.diag.ersparnis': 'Quota compendii tota (tributum et contributiones), primo anno', + 'det.diag.svAG': 'Compendium contributionum verum conductoris', + 'det.diag.svAN': 'Compendium contributionum verum operarii', + 'det.diag.ngef': 'Pars collationum operativarum sine subsidio (primo anno)', + 'det.diag.aussch': 'Usus iuris petendi (4 % BBG-RV)', + 'det.diag.agzVar': 'ex eo subsidium conductoris: pro centesimis (primo anno)', + 'det.diag.agzFix': 'ex eo subsidium conductoris: summa fixa (primo anno)', + 'det.diag.agzFixUeber4': 'subsidium fixum supra immunitatem 4 % (onus non comprehensum)', + 'det.diag.vbMonat': 'Versorgungsbezug in mensem initio solutionum', + 'det.diag.freibetrag': 'Summa immunis pro Versorgungsbezüge eodem anno', + 'det.diag.vbQuote': 'Proportio Versorgungsbezug ad summam immunem', + 'det.diag.kapFreiKapital': 'Capitale sine contributionibus possibile (summa una)', + 'det.diag.kapFreiRente': 'Capitale sine contributionibus possibile (annuitas perpetua)', + 'det.diag.memoRest': 'MEMORIA: solutiones operativae post terminum', + 'det.diag.memoRestAv': 'MEMORIA: annuitas depositi post terminum', + 'det.diag.wiederanl': 'Usura reinvestiendi post tributa', + 'det.diag.kapstEff': 'Tributum retentum effectivum cum solidaritate et ecclesia', + 'det.diag.bestAnteil': 'Pars pensionis publicae tributo obnoxia', + 'det.warn.lead': 'Extra corridorem faventem stas: ', + 'det.warn.aussch': 'Supra 100 % usus ius petendi conversionem mercedis non amplius est. ', + 'det.warn.ngef': 'Pars collationum notabilis nec tributo nec contributionibus favetur — id stratum in involucrum assecurationis migrat nihilque pro eo accipit nisi obligationem contributionis valetudinariae. ', + 'det.warn.vb': 'Versorgungsbezug summam immunem multum superat; vis tuendi summae absolutae ut 1/capitale decrescit. ', + 'det.anspar.h.jahr': 'Annus', + 'det.anspar.h.brutto': 'Brutum', + 'det.anspar.h.beitrag': 'Collatio oper.', + 'det.anspar.h.agzVar': 'Subsid. %', + 'det.anspar.h.agzFix': 'Subsid. fix.', + 'det.anspar.h.sv': 'Comp. contrib.', + 'det.anspar.h.st': 'Comp. tributi', + 'det.anspar.h.netto': 'Impensa pura', + 'det.anspar.h.zul': 'Additamenta', + 'det.anspar.h.kbav': 'Capitale oper.', + 'det.anspar.h.kav': 'Capitale depos.', + 'det.anspar.h.kpriv': 'Capitale priv.', + 'det.row.year': function(p){ return p.jahr + ' (aetas ' + p.alter + ')'; }, + 'det.ausz.h.jahr': 'Annus', + 'det.ausz.h.bavBr': 'oper. brutum', + 'det.ausz.h.kvpv': 'valet./cur.', + 'det.ausz.h.steuer': 'Tributum', + 'det.ausz.h.mind': 'Deminutio pensionis', + 'det.ausz.h.bavNet': 'oper. purum', + 'det.ausz.h.avBr': 'depos. brutum', + 'det.ausz.h.avNet': 'depos. purum', + 'det.ausz.h.privBr': 'priv. brutum', + 'det.ausz.h.privNet': 'priv. purum', + 'det.ann.intro': 'Ordo ex quaestionibus iuris pendet quae anno 2026 nondum omnes solutae sunt. Numeri in columna sunt gradus fidei subiectivi.', + 'det.ann.h.annahme': 'Suppositio', + 'det.ann.h.vertrauen': 'Fides', + 'det.ann.h.anmerkung': 'Adnotatio', + 'det.ann.r1.a': 'Numeri assecurationis socialis 2026, tabula § 32a 2026', + 'det.ann.r1.c': 'SVBezGrV 2026, Steuerfortentwicklungsgesetz', + 'det.ann.r2.a': 'Contributiones valetudinariae in Versorgungsbezüge', + 'det.ann.r2.c': '§§ 229, 250 SGB V', + 'det.ann.r3.a': 'Summa immunis secundum § 226 Abs. 2 S. 2 SGB V (valetudo)', + 'det.ann.r3.c': 'sine controversia', + 'det.ann.r4.a': 'Limes immunitatis loco summae immunis in curatione', + 'det.ann.r4.c': 'error frequentissimus in libellis consultoriis', + 'det.ann.r5.a': 'Solutio depositi senectutis a contributionibus immunis', + 'det.ann.r5.c': 'non sunt Versorgungsbezüge', + 'det.ann.r6.a': 'Dimidium discrimen pro stratis non subsidiatis', + 'det.ann.r6.c': '§ 22 Nr. 5 S. 2 Buchst. b cum § 20 Abs. 1 Nr. 6 S. 2 EStG', + 'det.ann.r7.a': 'Subsidium solum usque ad compendium verum conductoris', + 'det.ann.r7.c': 'sententia contraria 15 % ut meram forfaitariam legit', + 'det.ann.r8.a': 'Summa maxima impensarum specialium 2.340 €', + 'det.ann.r8.c': 'SUPPOSITIO EXEMPLARIS. Decernit utrum liberi valorem depositi tollant an minuant', + 'det.ann.r9.a': 'Assecuratio KVdR necessaria in senectute supposita', + 'det.ann.r9.c': 'de voluntarie assecuratis non quaesitum', + 'det.ann.notmodelled': '<b>Non expressa:</b> deminutiones pensionis maturius acceptae, effectus conversionis mercedis in subsidia morbi, inopiae operis et parentum, provisio superstitum, tutela contra insolventiam (PSVaG), portabilitas mutato conductore, deminutiones curationis pro liberis, comparatio secundum § 32d Abs. 6 EStG, tutela contra pignerationem et in subsidio fundamentali, periculum longaevitatis ultra terminum.', + 'det.prinzip': '<p>Comparatio <i>collationum brutarum</i> iniqua esset: conversio mercedis ex bruto venit, parsimonia privata ex puro. Ideo <b>impensa pura</b> aequatur:</p>' + + '<p style="font-family:var(--mono);font-size:.82rem;background:var(--chip);padding:9px 11px;border-radius:9px">impensa pura = collatio bruta − compendium tributi salarii − contributiones operarii compensatae</p>' + + '<p>Haec ipsa summa in omnibus tribus rationibus collocatur. Quod restat, comparatio <i>architecturarum subsidii</i> est, non magnitudinum illationis.</p>' + + '<p><b>Dilatio tributi per se nihil valet:</b> (1−t)·(1+r)ⁿ = (1+r)ⁿ·(1−t). Multiplicatio commutat. Quattuor tantum causae valoris manent:</p>' + + '<ol style="padding-left:1.2em;margin:.3em 0"><li>arbitrium tabularum t₀ − t₁</li>' + + '<li>pecunia aliena — subsidium conductoris vel additamenta</li>' + + '<li>congestio sine tributo — nulla summa anticipata</li>' + + '<li>onera nova quae unam solam rationem feriunt — contributiones valetudinariae in Versorgungsbezüge</li></ol>' + + '<p>Punctum quartum est incommodum praecipuum pensionis operativae, punctum secundum unicum eius commodum structurale. Fructus pensionis operativae <b>stricte decrescit cum magnitudine collationis</b>, quia summa immunis absoluta est et vis eius tuendi ut 1/K minuitur — nullum est optimum internum.</p>' + + '<p>Data impensa pura, collatio bruta bisectione retro computatur: N(B) continua est et stricte crescens (dN/dB = 1 − quota contributionum marginalis − quota tributi marginalis > 0), formula autem clausa non invertitur propter flexus ad 4 % et 8 % limitis BBG-RV, ad limites contributionum et ad terminos tabulae. 34 bisectiones infra decimam centesimae partis nummi perveniunt.</p>', + 'det.t.prinzip': 'Principium comparationis', + 'det.t.erg': 'Exitus accuratius', + 'det.s.erg': 'Omnes tres rationes tempore congerendi prorsus eandem summam ex puro constant.', + 'det.t.mon': 'Praestatio menstrua resoluta', + 'det.s.mon': 'Omnes rationes per idem tempus in annuitatem versae, in vi emendi hodierna. Quia annuitas linearis est, versus prorsus se addunt.', + 'det.t.diag': 'Diagnosis', + 'det.s.diag': 'Unde id veniat — et ubi extra corridorem faventem stes.', + 'det.t.anspar': 'Tempus congerendi, annus post annum', + 'det.t.ausz': 'Tempus solvendi, annus post annum', + 'det.t.ann': 'Firmitas suppositionum', + + /* ---------------- Servare et communicare ---------------- */ + 'msg.linkCopied': 'Nexus exscriptus. Qui eum aperit, hos ipsos numeros videt — nihil eorum per servum transit.', + 'msg.linkFailed': 'Exscribere non licuit. Nexus nunc in linea inscriptionis stat.', + 'msg.loaded': 'Lectum.', + 'msg.loadError': function(p){ return 'Tabella legi non potuit: ' + p.msg; }, + 'msg.reset': 'Ad casum fundamentalem restitutum.', + + /* ---------------- PWA ---------------- */ + 'pwa.offline': '<b>Sine rete utibilis.</b> Applicatio nunc in navigatri promptuario iacet et sine coniunctione currit. Ut installes: in iPhone „Communicare“ → „Ad initialem tabulam“, in Android per menu → „Applicationem installare“.', + 'pwa.installAsk': '<b>Ut applicationem installare?</b> Tum cum proprio signo in tabula initiali iacet et sine rete currit. ', + 'pwa.installBtn': 'Installare', + 'pwa.installed': '<b>Installata.</b> Applicationem nunc inter ceteras tuas invenies.', + 'pwa.declined': 'Bene — applicatio etiam sic currit, et quidem sine rete.', + 'pwa.file': '<b>Tabellam recta aperuisti</b> (<kbd>file://</kbd>). Omnia computantur, omnia servantur. Duae solae proprietates PWA desunt: nullum signum in tabula initiali et nullum promptuarium sine rete — quo hic nec opus esset, cum tabella iam in machina iaceat. Ad plenum usum plicam in quodlibet spatium interretiale HTTPS pone et inde aperi.' +}; diff --git a/app/index.html b/app/index.html index 25614e8..0406d8c 100644 --- a/app/index.html +++ b/app/index.html @@ -5,6 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <meta name="theme-color" content="#000080"> <meta name="description" content="Betriebliche Altersvorsorge, Altersvorsorgedepot und privates ETF-Depot bei gleichem Nettoaufwand vergleichen. Rechtsstand 2026."> +<!-- Der Inhalt beider Marken wird beim Start aus dem Woerterbuch nachgezogen. --> <title>Altersvorsorge-Vergleich</title> <link rel="manifest" href="manifest.json"> <link rel="apple-touch-icon" href="icon-192.png"> @@ -39,8 +40,15 @@ --bav:#800000; --av:#000080; --priv:#008000; --good:#008000; --bad:#800000; - --ui:"MS Sans Serif","Microsoft Sans Serif",Tahoma,Geneva,Verdana,Arial,sans-serif; - --mono:"Fixedsys","Lucida Console","Courier New",monospace; + /* Die japanische Fassung braucht einen CJK-Schnitt. MS Gothic bzw. Meiryo + stehen bewusst vorn: eine Rasterschrift mit harten Kanten passt zum Rest + des Themas, und beide sind auf japanischen Systemen vorhanden. Für die + lateinischen Schriftzeichen greift weiterhin zuerst MS Sans Serif. */ + --ui:"MS Sans Serif","Microsoft Sans Serif",Tahoma,Geneva,Verdana,Arial, + "MS PGothic","Hiragino Kaku Gothic ProN","Yu Gothic UI",Meiryo, + "Noto Sans JP",sans-serif; + --mono:"Fixedsys","Lucida Console","Courier New", + "MS Gothic","Hiragino Kaku Gothic ProN","Noto Sans Mono CJK JP",monospace; } *{box-sizing:border-box} html{-webkit-text-size-adjust:100%} @@ -362,21 +370,24 @@ a{color:var(--navy)} <div class="sb-cap" id="sb-cap"></div> <div id="sb-vals"></div> </div> - <button class="sb-up" id="sb-up" type="button" aria-label="Nach oben zum Ergebnis">↑</button> + <button class="sb-up" id="sb-up" type="button" data-i18n-aria="sb.up" aria-label="Nach oben zum Ergebnis">↑</button> </div> </div> <div class="wrap"> -<header> - <h1>Altersvorsorge‑Vergleich</h1> - <p class="sub">Betriebliche Altersvorsorge, Altersvorsorgedepot und privates ETF‑Depot — bei +<header data-title-key="win.header"> + <h1 data-i18n="hdr.title">Altersvorsorge‑Vergleich</h1> + <p class="sub" data-i18n-html="hdr.sub">Betriebliche Altersvorsorge, Altersvorsorgedepot und privates ETF‑Depot — bei <b>gleichem Nettoaufwand</b> und mit demselben ETF. Rechtsstand 2026.</p> + <!-- Sprachwahl. Deutsch ist die Hauptsprache; Englisch ist eine Uebersetzung, + kein zweiter Rechtsstand - die Normzitate bleiben deutsch. --> + <div class="row" id="langrow" style="margin-top:7px"></div> </header> <!-- ======================= ERGEBNIS ======================= --> -<section class="win" id="w-result"> - <h2>Ergebnis</h2> +<section class="win" id="w-result" data-title-key="win.result"> + <h2 data-i18n="win.result">Ergebnis</h2> <div id="metricbar" class="seg" role="group" aria-label="Kennzahl"></div> <p class="sub" id="metricdesc" style="margin:2px 0 10px"></p> <p id="demo" class="msg demo" data-ico="?" hidden></p> @@ -391,15 +402,15 @@ a{color:var(--navy)} </section> <!-- ======================= KERNANGABEN ======================= --> -<section class="win" id="w-input"> - <h2>Ihre Angaben</h2> - <p class="sub" style="margin-bottom:6px">Die Zahlen rechnen sich bei jeder Änderung sofort neu. +<section class="win" id="w-input" data-title-key="win.input"> + <h2 data-i18n="win.input">Ihre Angaben</h2> + <p class="sub" style="margin-bottom:6px" data-i18n-html="input.sub">Die Zahlen rechnen sich bei jeder Änderung sofort neu. Mit <span class="qm" style="cursor:default">?</span> erklärt sich jedes Feld selbst.</p> <div id="core"></div> </section> <!-- ======================= DIAGRAMME ======================= --> -<section class="win" id="w-chart" data-title="Diagramme"> +<section class="win" id="w-chart" data-title-key="win.chart"> <div class="tabs" id="tabs" role="tablist"></div> <div id="chartwrap"> <canvas id="chart" height="260"></canvas> @@ -410,29 +421,29 @@ a{color:var(--navy)} </section> <!-- ======================= WEITERE ANGABEN ======================= --> -<section class="win" id="w-extra"> - <h2 style="margin-bottom:6px">Weitere Angaben</h2> - <p class="sub" style="margin-bottom:2px">Alles hier hat sinnvolle Voreinstellungen. Nur anfassen, wenn es auf Sie zutrifft.</p> +<section class="win" id="w-extra" data-title-key="win.extra"> + <h2 style="margin-bottom:6px" data-i18n="win.extra">Weitere Angaben</h2> + <p class="sub" style="margin-bottom:2px" data-i18n="extra.sub">Alles hier hat sinnvolle Voreinstellungen. Nur anfassen, wenn es auf Sie zutrifft.</p> <div id="groups"></div> </section> <!-- ======================= RECHENWEG ======================= --> -<section class="win" id="w-calc"> - <h2 style="margin-bottom:6px">Rechenweg</h2> - <p class="sub" style="margin-bottom:2px">Für alle, die es nachvollziehen wollen.</p> +<section class="win" id="w-calc" data-title-key="win.calc"> + <h2 style="margin-bottom:6px" data-i18n="win.calc">Rechenweg</h2> + <p class="sub" style="margin-bottom:2px" data-i18n="calc.sub">Für alle, die es nachvollziehen wollen.</p> <div id="details"></div> </section> <!-- ======================= WEITERGEBEN ======================= --> -<section class="win" id="w-share"> - <h2>Speichern und weitergeben</h2> - <p class="sub">Ihre Eingaben liegen ausschließlich in diesem Browser (<kbd>localStorage</kbd>). Nichts wird übertragen, +<section class="win" id="w-share" data-title-key="win.share"> + <h2 data-i18n="win.share">Speichern und weitergeben</h2> + <p class="sub" data-i18n-html="share.sub">Ihre Eingaben liegen ausschließlich in diesem Browser (<kbd>localStorage</kbd>). Nichts wird übertragen, es gibt keinen Server und keine Tracker.</p> <div class="row" style="margin-top:10px"> - <button class="btn pri" id="share">Link mit meinen Zahlen kopieren</button> - <button class="btn" id="export">Als Datei sichern</button> - <button class="btn" id="import">Datei laden</button> - <button class="btn" id="reset">Alles zurücksetzen</button> + <button class="btn pri" id="share" data-i18n="ui.share">Link mit meinen Zahlen kopieren</button> + <button class="btn" id="export" data-i18n="ui.export">Als Datei sichern</button> + <button class="btn" id="import" data-i18n="ui.import">Datei laden</button> + <button class="btn" id="reset" data-i18n="ui.reset">Alles zurücksetzen</button> </div> <input type="file" id="file" accept=".json,application/json" hidden> <p class="sub" id="sharemsg" style="margin-top:8px"></p> @@ -440,18 +451,70 @@ a{color:var(--navy)} </section> <footer> - <p><b>Keine Steuer- oder Anlageberatung.</b> Ein Modell ist kein Vertrag. Die Rechtslage 2026 zur reformierten + <p data-i18n-html="footer.1"><b>Keine Steuer- oder Anlageberatung.</b> Ein Modell ist kein Vertrag. Die Rechtslage 2026 zur reformierten privaten Altersvorsorge ist an mehreren Stellen noch nicht abschließend geklärt; die Annahmen mit dem geringsten Vertrauensgrad stehen unter „Rechenweg → Belastbarkeit der Annahmen“ und sind dort auch änderbar.</p> - <p>Rechenkern eins zu eins portiert aus dem Excel-Modell + <p data-i18n-html="footer.2">Rechenkern eins zu eins portiert aus dem Excel-Modell <span class="pill">bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx</span> und gegen drei Testfälle auf Maschinengenauigkeit geprüft (größte relative Abweichung 1,3·10<sup>−14</sup>).</p> </footer> </div> +<script src="i18n.de.js"></script> +<script src="i18n.en.js"></script> +<script src="i18n.la.js"></script> +<script src="i18n.ja.js"></script> <script> /* ===================================================================== + TEIL 0 — SPRACHEN + Deutsch ist die Hauptsprache und zugleich die Rueckfallebene: fehlt ein + Schluessel in der gewaehlten Sprache, greift der deutsche Eintrag; fehlt + auch der, erscheint der Schluessel selbst. Ein stiller Leerstring waere + die schlechtere Wahl - er verschwindet unbemerkt aus der Oberflaeche. + + Ein Woerterbucheintrag ist entweder eine Zeichenkette oder eine Funktion + (p, f) -> string. Die Funktionsform ist noetig, weil ein Satz nicht die + Verkettung seiner Teile ist: Stellung, Numerus und Rektion haengen an der + Sprache. Deshalb bekommen die Funktionen ROHWERTE (Zahlen) und formatieren + selbst ueber f, statt fertige Textstuecke einzusetzen. + ===================================================================== */ +var LANGS = ['de','en','la','ja'], LANGKEY = 'altersvorsorge.lang'; +var DICT = { de: window.I18N_DE || {}, en: window.I18N_EN || {}, + la: window.I18N_LA || {}, ja: window.I18N_JA || {} }; +var FMT = {}; /* wird in applyLocale() gefuellt */ + +/* Vorrang: ?lang= in der Adresse (teilbar) > gemerkte Wahl > Browsersprache. + Latein wird nie automatisch gewählt — ein Browser, der la meldet, meint es + fast sicher nicht als Anzeigesprache. */ +function pickLang(){ + var q = /[?&]lang=([a-z]{2})/i.exec(location.search); + if (q && LANGS.indexOf(q[1].toLowerCase()) >= 0) return q[1].toLowerCase(); + try { var s = localStorage.getItem(LANGKEY); if (LANGS.indexOf(s) >= 0) return s; } catch(e){} + var n = ((navigator.languages && navigator.languages[0]) || navigator.language || 'de').toLowerCase(); + if (/^ja/.test(n)) return 'ja'; + if (/^en/.test(n)) return 'en'; + return 'de'; +} +var LANG = pickLang(); + +function tRaw(key){ + var v = DICT[LANG] ? DICT[LANG][key] : undefined; + return v === undefined ? DICT.de[key] : v; +} +function tHas(key){ var v = tRaw(key); return v !== undefined && v !== ''; } +function t(key, p){ + var v = tRaw(key); + if (v === undefined) return key; + return typeof v === 'function' ? v(p || {}, FMT) : v; +} +/* Nur-Text-Fassung: fuer Beschriftungen im Canvas und in aria-Attributen, + wo Auszeichnung nicht dargestellt wird. */ +function tPlain(key, p){ + return String(t(key, p)).replace(/<[^>]*>/g, ''); +} + +/* ===================================================================== TEIL 1 — RECHENKERN Portierung von build_bav.py. Reine Funktionen, kein DOM. ===================================================================== */ @@ -466,7 +529,8 @@ var DEFAULT_INPUT = { tarifanp: 0.02, kinder: 0, kind_jahre: 18, kinderlos: 0, modus: 1, beitrag: 300, netto_ziel: 165, dyn: 1, /* modus bleibt 1: die Oberfläche kennt keinen Umschalter mehr, sondern koppelt Brutto und Netto über bruttoVonNetto() */ - agzus: 0.15, agzus_sv: 2, auszform: 1, rentfak: 26, rentdyn: 0.01, fuenftel: 0, + agzus: 0.15, agzus_sv: 2, agz_fix: 0, agz_fix_dyn: 0, agz_fix_min: 0, + auszform: 1, rentfak: 26, rentdyn: 0.01, fuenftel: 0, rendite: 0.07, k_bav: 0.006, k_av: 0.005, k_priv: 0.002, infl: 0.02, kv_ruhe: 1, sonst_zve: 22000, rente_br: 24000, teilkap: 0.0, av_auszform: 1, rentfak_av: 26, rentdyn_av: 0.01 @@ -528,9 +592,21 @@ function derive(I, P) { D.sv_kv_ag = (P.kv_allg + P.kv_zus) / 2 + P.pv_an; return D; } -/* N(B) = B − SV-Ersparnis(B) − Steuerersparnis(B) ist stetig und streng monoton - wachsend, wegen der Knicke bei 4 %/8 % BBG-RV, an den BBG und an den Tarif- - eckwerten aber nicht geschlossen invertierbar → 34 Halbierungen. */ +/* Fester, betragsunabhängiger Arbeitgeberzuschuss (z. B. Deutsche Bahn). + Jahresbetrag im Jahr t bei Bruttobeitrag `bav_br` (Jahreswert) und Lohnindex. + Bedingung: der eigene Monatsbeitrag erreicht die vereinbarte Mindestschwelle. + Bei Schwelle 0 fließt er in jedem aktiven Jahr, auch ohne Eigenbeitrag. */ +function festZuschuss(I, bav_br, lohnidx, aktiv) { + if (!(I.agz_fix > 0) || !aktiv) return 0; + if (bav_br / 12 < (I.agz_fix_min || 0) - 1e-9) return 0; + return 12 * I.agz_fix * (I.agz_fix_dyn === 1 ? lohnidx : 1); +} +/* N(B) = B − SV-Ersparnis(B) − Steuerersparnis(B) ist streng monoton wachsend, + wegen der Knicke bei 4 %/8 % BBG-RV, an den BBG und an den Tarifeckwerten + aber nicht geschlossen invertierbar → 34 Halbierungen. Ein fester Zuschuss + mit Mindestschwelle macht N zusätzlich unstetig (Sprung nach oben an der + Schwelle, weil der Zuschuss dort die Freibeträge belegt): monoton bleibt es, + aber Zielwerte im Sprungintervall sind nicht erreichbar — `resid` zeigt das. */ function bisektion(I, P, D, steuer) { var tz0 = I.alter >= I.tzalter ? I.tzfaktor : 1; var g0 = I.gehalt * tz0, bbgrv0 = P.bbg_rv, bbgkv0 = P.bbg_kv; @@ -541,12 +617,13 @@ function bisektion(I, P, D, steuer) { for (var t = 0; t < N_BISEKT; t++) { if (t > 0) { if (nb < ziel) lo = mid; else hi = mid; } mid = (lo + hi) / 2; - var svf = min(mid, P.sv_frei_q * bbgrv0); + var agfx = festZuschuss(I, mid, 1, 1); + var svf = min(mid, max(0, P.sv_frei_q * bbgrv0 - agfx)); var drv = min(g0, bbgrv0) - min(g0 - svf, bbgrv0); var dkv = min(g0, bbgkv0) - min(g0 - svf, bbgkv0); var svan = drv * D.sv_an + dkv * D.sv_kv_an; var svag = drv * D.sv_an + dkv * D.sv_kv_ag; - var agz = I.agzus_sv === 2 ? min(I.agzus * mid, svag) : I.agzus * (I.agzus_sv === 1 ? svf : mid); + var agz = (I.agzus_sv === 2 ? min(I.agzus * mid, svag) : I.agzus * (I.agzus_sv === 1 ? svf : mid)) + agfx; var stf = min(mid, max(0, P.st_frei_q * bbgrv0 - agz)); var vorsm = min(g0 - svf, bbgrv0) * P.rv_an + min(g0 - svf, bbgkv0) * D.vorsq_kv; var stgm = steuer(max(0, g0 - stf - P.an_pausch - P.sa_pausch - vorsm), 1); @@ -578,13 +655,19 @@ function rechne(input, param, flags) { a.brutto = I.gehalt * a.lohnidx * a.tz * a.aktiv; a.bbgrv = P.bbg_rv * a.lohnidx; a.bbgkv = P.bbg_kv * a.lohnidx; a.bav_br = min(a.brutto, 12 * BEITRAG * (I.dyn === 1 ? a.lohnidx : 1)) * a.aktiv; - a.bav_svf = min(a.bav_br, P.sv_frei_q * a.bbgrv); + /* Der feste Arbeitgeberzuschuss ist eine arbeitgeberfinanzierte Zuwendung + nach § 3 Nr. 63 EStG und belegt deshalb den 4-%-Topf des § 1 Abs. 1 S. 1 + Nr. 9 SvEV und den 8-%-Topf des § 3 Nr. 63 EStG VORRANGIG; die eigene + Entgeltumwandlung bekommt nur noch den Rest steuer- und beitragsfrei. */ + a.agz_fx = festZuschuss(I, a.bav_br, a.lohnidx, a.aktiv); + a.bav_svf = min(a.bav_br, max(0, P.sv_frei_q * a.bbgrv - a.agz_fx)); a.drv = min(a.brutto, a.bbgrv) - min(a.brutto - a.bav_svf, a.bbgrv); a.dkv = min(a.brutto, a.bbgkv) - min(a.brutto - a.bav_svf, a.bbgkv); a.sverp = a.drv * D.sv_an + a.dkv * D.sv_kv_an; a.sverp_ag = a.drv * D.sv_an + a.dkv * D.sv_kv_ag; - a.agz = I.agzus_sv === 2 ? min(I.agzus * a.bav_br, a.sverp_ag) - : I.agzus * (I.agzus_sv === 1 ? a.bav_svf : a.bav_br); + a.agz_var = I.agzus_sv === 2 ? min(I.agzus * a.bav_br, a.sverp_ag) + : I.agzus * (I.agzus_sv === 1 ? a.bav_svf : a.bav_br); + a.agz = a.agz_var + a.agz_fx; a.bav_stf = min(a.bav_br, max(0, P.st_frei_q * a.bbgrv - a.agz)); a.bav_ein = a.bav_br + a.agz; a.bav_gef = min(a.bav_ein, P.st_frei_q * a.bbgrv); @@ -844,6 +927,11 @@ function rechne(input, param, flags) { svErspAG: A[0].sverp_ag, svErspAN: A[0].sverp, ngefAnteil: A[0].bav_ein === 0 ? 0 : A[0].bav_ngef / A[0].bav_ein, ausschoepfung: A[0].bbgrv === 0 ? 0 : A[0].bav_br / (P.sv_frei_q * A[0].bbgrv), + agzFix: A[0].agz_fx, agzVar: A[0].agz_var, + /* Anteil des festen Zuschusses, der über den 4-%-Topf hinausragt und damit + beim Arbeitnehmer beitragspflichtiges Arbeitsentgelt wäre — vom Modell + NICHT als Beitragslast erfasst, deshalb als Diagnosewert ausgewiesen. */ + agzFixUeber4: max(0, A[0].agz_fx - P.sv_frei_q * A[0].bbgrv), vbMonat: Z[0].bav_vb, freibetrag: D.frei_vb * Z[0].lohn, vbQuote: (D.frei_vb * Z[0].lohn) === 0 ? 0 : Z[0].bav_vb / (D.frei_vb * Z[0].lohn), kapFreiKapital: 120 * D.frei_vb * Z[0].lohn, @@ -884,37 +972,28 @@ function waterfall(input, param, ziel) { var P0 = Object.assign({}, DEFAULT_PARAM, param || {}); var e_priv = base.ertrag.priv; var vp = rechne(input, Object.assign({}, P0, { basiszins: 0 })).ertrag.priv - e_priv; - var vpNote = 'Weder bAV noch Altersvorsorgedepot kennen die Vorabpauschale (§ 18 InvStG). Gemessen als ' - + 'Privatdepot ohne Vorabpauschale minus mit. Der Balken kann negativ sein: die Vorabpauschale läuft ' - + 'zu großen Teilen in den Sparer-Pauschbetrag, erhöht aber in voller Höhe die Kostenbasis — dann ist ' - + 'sie für das Privatdepot netto ein Vorteil.'; + var vpNote = t('wfb.vp.note'); if (ziel === 'av') { var e_av = base.ertrag.av; var ohne = rechne(input, Object.assign({}, P0, { zul_g1:0, zul_g2:0, kind_zul:0, av_sa_max:0 })).ertrag.av; var f = e_av - ohne; return { start:e_priv, ende:e_av, ziel:'av', bars:[ - { label:'Wegfall der Vorabpauschale', wert:vp, note:vpNote }, - { label:'Zulagen und Sonderausgabenabzug', wert:f, - note:'Fremdgeld nach dem neuen Zulagenmodell plus der Abzug nach § 10a EStG. Gemessen durch Abschalten beider Förderwege.' }, - { label:'Besteuerungsarchitektur (Rest)', wert:e_av-e_priv-vp-f, - note:'Tarifarbitrage t₀ − t₁ und alles Übrige. Residual bestimmt, damit die Kette exakt aufgeht.' } + { label:t('wfb.vp.label'), wert:vp, note:vpNote }, + { label:t('wfb.zul.label'), wert:f, note:t('wfb.zul.note') }, + { label:t('wfb.arch.label'), wert:e_av-e_priv-vp-f, note:t('wfb.arch.note') } ]}; } var e_bav = base.ertrag.bav; var noKV = rechne(Object.assign({}, input, { kv_ruhe:2 }), param).ertrag.bav; var noKVEP = rechne(Object.assign({}, input, { kv_ruhe:2 }), param, { noEP:1 }).ertrag.bav; - var noAll = rechne(Object.assign({}, input, { kv_ruhe:2, agzus:0 }), param, { noEP:1 }).ertrag.bav; + var noAll = rechne(Object.assign({}, input, { kv_ruhe:2, agzus:0, agz_fix:0 }), param, { noEP:1 }).ertrag.bav; var kvpv = e_bav - noKV, epv = noKV - noKVEP, fremd = noKVEP - noAll; return { start:e_priv, ende:e_bav, ziel:'bav', bars:[ - { label:'Wegfall der Vorabpauschale', wert:vp, note:vpNote }, - { label:'Arbeitgeberzuschuss', wert:fremd, - note:'Fremdgeld — der einzige strukturelle Vorteil der bAV, der nicht aus dem Steuerrecht kommt. Gemessen durch Abschalten des Zuschusses.' }, - { label:'Tarifarbitrage und Rest', wert:e_bav-e_priv-vp-fremd-kvpv-epv, - note:'Grenzsteuersatz beim Einzahlen minus beim Auszahlen, plus alles Übrige. Residual bestimmt, damit die Kette exakt aufgeht. Steueraufschub allein ist wertlos: (1−t)(1+r)ⁿ = (1+r)ⁿ(1−t), Multiplikation kommutiert.' }, - { label:'KV/PV auf Versorgungsbezüge', wert:kvpv, - note:'§§ 229, 250 SGB V: den vollen Beitragssatz trägt der Rentner allein. Gemessen als Differenz zur Einstellung „PKV im Ruhestand“.' }, - { label:'Verlust an Entgeltpunkten', wert:epv, - note:'Entgeltumwandlung mindert das beitragspflichtige Entgelt und damit die gesetzliche Rente. Netto gegengerechnet.' } + { label:t('wfb.vp.label'), wert:vp, note:vpNote }, + { label:t('wfb.agz.label'), wert:fremd, note:t('wfb.agz.note') }, + { label:t('wfb.tarif.label'), wert:e_bav-e_priv-vp-fremd-kvpv-epv, note:t('wfb.tarif.note') }, + { label:t('wfb.kvpv.label'), wert:kvpv, note:t('wfb.kvpv.note') }, + { label:t('wfb.ep.label'), wert:epv, note:t('wfb.ep.note') } ]}; } /* --------------------------------------------------------------------- @@ -952,41 +1031,43 @@ root.AVModell = { rechne:rechne, waterfall:waterfall, bruttoVonNetto:bruttoVonNe /* ===================================================================== TEIL 2 — FELDDEFINITIONEN ===================================================================== */ -/* nom/satz/gen: das Fazit ist ein deutscher Satz, kein Etikettensalat. - „mehr als privates ETF-Depot“ liest sich wie eine Tabellenzeile. */ +/* nom/satz/gen: das Fazit ist ein Satz, kein Etikettensalat. „mehr als + privates ETF-Depot“ liest sich wie eine Tabellenzeile. Die Formen stehen + im Wörterbuch, weil sie an der Grammatik der jeweiligen Sprache hängen — + das Deutsche braucht hier einen Genitiv, das Englische keinen. + + Die Textformen sind Zugriffsfunktionen und keine Felder: dann bleibt + `o.name` an jeder Aufrufstelle stehen und liefert nach einem Sprach- + wechsel von selbst den neuen Wert. */ var OPTS = [ - { k:'bav', name:'bAV (Entgeltumwandlung)', short:'bAV', color:'var(--bav)', raw:'#800000', - nom:'die betriebliche Altersvorsorge', satz:'Die betriebliche Altersvorsorge', gen:'der bAV' }, - { k:'av', name:'Altersvorsorgedepot', short:'AV-Depot', color:'var(--av)', raw:'#000080', - nom:'das Altersvorsorgedepot', satz:'Das Altersvorsorgedepot', gen:'des Altersvorsorgedepots' }, - { k:'priv', name:'privates ETF-Depot', short:'privat', color:'var(--priv)', raw:'#008000', - nom:'das private ETF-Depot', satz:'Das private ETF-Depot', gen:'des privaten ETF-Depots' } + { k:'bav', color:'var(--bav)', raw:'#800000' }, + { k:'av', color:'var(--av)', raw:'#000080' }, + { k:'priv', color:'var(--priv)', raw:'#008000' } ]; +OPTS.forEach(function(o){ + ['name','short','nom','satz','gen'].forEach(function(prop){ + Object.defineProperty(o, prop, { enumerable:true, + get: function(){ return t('opt.' + o.k + '.' + prop); } }); + }); +}); function opt(k){ return OPTS.filter(function(o){return o.k===k;})[0]; } /* kind: eur | eurm (€/Monat) | pct | num | seg src : 'I' Eingabe | 'P' Rechengröße */ var FIELDS = [ /* ---------------- Kern ---------------- */ - { k:'gehalt', src:'I', g:'kern', kind:'eur', label:'Jahresbrutto heute', min:15000, max:250000, step:1000, - hint:'Ohne Einmalzahlungen, gegebenenfalls einschließlich anteiligem 13. Gehalt. Bestimmt Ihren Grenzsteuersatz und ob Sie über den Beitragsbemessungsgrenzen liegen.' }, - { k:'beitrag', src:'I', g:'kern', kind:'eurm', label:'Monatsbeitrag bAV brutto', min:0, max:1500, step:10, + { k:'gehalt', src:'I', g:'kern', kind:'eur', min:15000, max:250000, step:1000 }, + { k:'beitrag', src:'I', g:'kern', kind:'eurm', min:0, max:1500, step:10, pairTop:true, liveNote:function(R){ var a = R.A[0]; if (!(a.bav_br > 0)) return null; var v4 = S.P.sv_frei_q * a.bbgrv, v8 = S.P.st_frei_q * a.bbgrv; - if (a.bav_br > v8 + 0.005) - return 'Über 8 % der Beitragsbemessungsgrenze RV (' + eur(v8) + ' im Jahr). Der Teil darüber ist ' - + 'weder steuer- noch sozialabgabenbegünstigt — er wandert in den Versicherungsmantel und holt ' - + 'sich dort nur die Beitragspflicht in der Krankenversicherung ab.'; - if (a.bav_br > v4 + 0.005) - return 'Über 4 % der Beitragsbemessungsgrenze RV (' + eur(v4) + ' im Jahr). Bis hierhin haben Sie ' - + 'einen Rechtsanspruch nach § 1a BetrAVG, darüber braucht es die Zustimmung des Arbeitgebers.'; + if (a.bav_br > v8 + 0.005) return t('n.beitrag.ueber8', { v8: v8 }); + if (a.bav_br > v4 + 0.005) return t('n.beitrag.ueber4', { v4: v4 }); return null; - }, - hint:'Bruttoentgeltumwandlung nach § 1a BetrAVG. Ein Rechtsanspruch besteht nur bis 4 % der Beitragsbemessungsgrenze RV, darüber braucht es die Zustimmung des Arbeitgebers.' }, - { k:'netto_ziel', src:'I', g:'kern', kind:'eurm', label:'Monatlicher Nettoaufwand', min:0, max:1200, step:5, + } }, + { k:'netto_ziel', src:'I', g:'kern', kind:'eurm', min:0, max:1200, step:5, pairBottom:true, srcKey:'beitrag', /* Abgeleitetes Feld: die Wahrheit steht in `beitrag`, hier steht dieselbe Größe in der anderen Währung. Lesen = Vorwärtsrechnung, Schreiben = @@ -997,9 +1078,8 @@ var FIELDS = [ var b = AVModell.bruttoVonNetto(S.I, S.P, v); S.I.beitrag = b.mtl; koppelWarn = b.erreichbar ? null : 'unerreichbar'; - }, - hint:'Der Betrag, um den Ihr verfügbares Einkommen im ersten Jahr sinkt — der tatsächlich aufgegebene Konsum und damit die ökonomisch richtige Entscheidungsvariable. Genau dieser Betrag wird in allen drei Optionen investiert; das ist die Bedingung für einen fairen Vergleich. Er hängt eineindeutig am Bruttobeitrag darüber: N(B) = B − SV-Ersparnis − Steuerersparnis ist streng monoton wachsend, also umkehrbar. Geschlossen geht das wegen der Knicke an den Beitragsbemessungsgrenzen und den Tarifeckwerten nicht, deshalb 34 Halbierungen — Genauigkeit unter einem Zehntelcent.' }, - { k:'agzus', src:'I', g:'kern', kind:'pct', label:'Arbeitgeberzuschuss', min:0, max:1, step:0.01, + } }, + { k:'agzus', src:'I', g:'kern', kind:'pct', min:0, max:1, step:0.01, /* Der wichtigste Live-Befund der ganzen App: oberhalb der KV-Beitrags- bemessungsgrenze spart der Arbeitgeber nur noch RV/AV, im spitzen Modus sättigt der Zuschuss deshalb oft schon unter dem gesetzlichen Minimum. @@ -1009,26 +1089,38 @@ var FIELDS = [ var a = R.A[0]; if (!(a.bav_br > 0)) return null; if (S.I.agzus * a.bav_br <= a.sverp_ag + 0.005) return null; - return 'Gedeckelt bei ' + pct(a.sverp_ag / a.bav_br, 1) + '. Der Arbeitgeber spart tatsächlich nur ' - + eur2(a.sverp_ag) + ' im Jahr — höhere Werte ändern hier nichts, solange der Zuschussmodus ' - + '„spitz gerechnet“ bleibt.'; - }, - hint:'Gesetzliches Minimum 15 % (§ 1a Abs. 1a BetrAVG); viele Arbeitgeber zahlen mehr. Break-even gegenüber dem Privatdepot grob dort, wo (1+z)·(1−0,211) ≈ 1, also z ≈ 26,7 % — nachprüfbar mit der Sensitivitätskurve. Im Zuschussmodus „spitz“ ist dieser Wert oft gar nicht erreichbar, weil der Zuschuss vorher sättigt.' }, - { k:'agzus_sv', src:'I', g:'kern', kind:'seg', label:'Zuschussmodus', opts:[[0,'voller Beitrag'],[1,'sv-freier Anteil'],[2,'spitz gerechnet']], - hint:'„Spitz“ ist der Gesetzeswortlaut: § 1a Abs. 1a BetrAVG schuldet den Zuschuss nur, SOWEIT der Arbeitgeber tatsächlich Sozialabgaben spart. Liegt Ihr Entgelt über beiden Beitragsbemessungsgrenzen, spart er nichts und schuldet nichts. Deshalb sättigt der Regler darüber im Basisfall bei rund 21 %. „Voller Beitrag“ ist eine freiwillige oder tarifvertragliche Zusage, „sv-freier Anteil“ die verbreitete pauschale Praxis. Vertrauensgrad der spitzen Lesart ca. 75 %.' }, - { k:'dyn', src:'I', g:'kern', kind:'seg', label:'Beitrag dynamisieren', opts:[[1,'mit dem Gehalt'],[0,'nominal konstant']], - hint:'Steigt der Beitrag mit der Gehaltssteigerung, bleibt die Ausschöpfung des 8-%-Topfes über die Jahre ungefähr konstant. Bleibt er nominal konstant, sinkt sein realer Wert mit der Inflation — und der Nettoaufwand, den die beiden anderen Optionen zum Vergleich bekommen, sinkt mit.' }, - { k:'rendite', src:'I', g:'kern', kind:'pct', label:'Bruttorendite ETF p.a.', min:0, max:0.12, step:0.001, - hint:'Vor Kosten, vor Steuern. In allen drei Optionen derselbe ETF — nur so vergleicht man Förderarchitekturen statt Anlageentscheidungen.' }, - { k:'alter', src:'I', g:'kern', kind:'num', label:'Alter heute', min:18, max:66, step:1, unit:'Jahre', - hint:'Startpunkt der Ansparphase. Kalenderjahr 0 = 2026.' }, - { k:'rentbeg', src:'I', g:'kern', kind:'num', label:'Auszahlungsbeginn', min:60, max:75, step:1, unit:'Jahre', - hint:'Beginn der Auszahlungsphase aller drei Optionen. Das Altersvorsorgedepot ist frühestens ab 65 auszahlbar (Reform 2026).' }, - { k:'kv_ruhe', src:'I', g:'kern', kind:'seg', label:'Krankenversicherung im Ruhestand', opts:[[1,'gesetzlich'],[2,'privat']], - hint:'Der entscheidende Hebel. In der GKV sind Betriebsrenten Versorgungsbezüge und tragen den vollen Beitragssatz (§§ 229, 250 SGB V); private Altersvorsorge nicht. Allein diese Einstellung dreht das Ergebnis.' }, - { k:'auszform', src:'I', g:'kern', kind:'seg', label:'bAV-Auszahlung', opts:[[1,'Kapital'],[2,'lebenslange Rente']], - hint:'Kapital: im Zuflussjahr voll steuerpflichtig, Beiträge zur KV auf 120 Monate verteilt (§ 229 Abs. 1 S. 3 SGB V). Die Rente streckt den Bezug auf die Restlebenszeit und schirmt deshalb ein Vielfaches an Kapital unter demselben Freibetrag ab. Das Modell kennt nur ganz oder gar nicht; eine Teilkapitalisierung der bAV ist nicht abgebildet.' }, - { k:'teilkap', src:'I', g:'kern', kind:'pct', label:'Altersvorsorgedepot: Einmalbetrag zu Beginn', min:0, max:0.3, step:0.05, + return t('n.agzus.gedeckelt', { quote: a.sverp_ag / a.bav_br, ersparnis: a.sverp_ag }); + } }, + { k:'agz_fix', src:'I', g:'kern', kind:'eurm', min:0, max:400, step:5, + /* Manche Arbeitgeber (Deutsche Bahn, Teile des öffentlichen Dienstes, viele + Tarifwerke) zahlen einen Festbetrag, der nicht am Umwandlungsbetrag hängt. + Er ist die einzige Größe im Modell mit konstanter Grenzrendite: ein Euro + Eigenbeitrag mehr bringt hier nichts, aber der Sockel liegt schon da. */ + liveNote:function(R){ + if (!(S.I.agz_fix > 0)) return null; + var a = R.A[0], d = R.diagnose; + if (!(a.aktiv)) return null; + if (a.agz_fx === 0) + return t('n.agz_fix.fliesstNicht', { beitrag: a.bav_br/12, schwelle: S.I.agz_fix_min }); + var txt = []; + if (d.agzFixUeber4 > 0.005) + txt.push(t('n.agz_fix.ueber4', { topf: S.P.sv_frei_q*a.bbgrv, ueber: d.agzFixUeber4 })); + else if (a.bav_svf < Math.min(a.bav_br, S.P.sv_frei_q*a.bbgrv) - 0.005) + txt.push(t('n.agz_fix.verdraengung', { rest: a.bav_svf })); + if (a.bav_ein > S.P.st_frei_q*a.bbgrv + 0.005) + txt.push(t('n.agz_fix.ueber8')); + return txt.length ? txt.join(' ') : null; + } }, + { k:'agz_fix_dyn', src:'I', g:'kern', kind:'seg', opts:[0,1], + showIf:function(I){return I.agz_fix > 0;} }, + { k:'agzus_sv', src:'I', g:'kern', kind:'seg', opts:[0,1,2] }, + { k:'dyn', src:'I', g:'kern', kind:'seg', opts:[1,0] }, + { k:'rendite', src:'I', g:'kern', kind:'pct', min:0, max:0.12, step:0.001 }, + { k:'alter', src:'I', g:'kern', kind:'num', min:18, max:66, step:1 }, + { k:'rentbeg', src:'I', g:'kern', kind:'num', min:60, max:75, step:1 }, + { k:'kv_ruhe', src:'I', g:'kern', kind:'seg', opts:[1,2] }, + { k:'auszform', src:'I', g:'kern', kind:'seg', opts:[1,2] }, + { k:'teilkap', src:'I', g:'kern', kind:'pct', min:0, max:0.3, step:0.05, /* Dieselbe Entscheidung wie eine Zeile darüber, nur für das andere Produkt — deshalb steht sie hier und nicht im Abschnitt „Ruhestand“. */ liveNote:function(R){ @@ -1037,154 +1129,143 @@ var FIELDS = [ var ohne = AVModell.rechne(Object.assign({}, S.I, { teilkap: 0 }), S.P); var d = M.get(R,'av') - M.get(ohne,'av'); var z = R.Z[0], gr = z.av_stpfl > 0 ? (z.stg_av - z.stg_b) / z.av_stpfl : 0; - return 'Kostet ' + (d >= 0 ? 'nichts, im Gegenteil: ' + M.dfmt(d) - : M.dfmt(-d).replace(/^[+−]/,'') + ' ' + M.unit) - + ' gegenüber reiner Verrentung. Grund: der Einmalbetrag ist im Zuflussjahr voll ' - + 'steuerpflichtig (Grenzbelastung dort ' + pct(gr,1) + ') und verzinst sich danach nur ' - + 'noch mit dem Satz nach Abgeltungsteuer statt mit der Produktrendite.'; - }, - hint:'Bis 30 % des angesparten Kapitals sind zu Beginn der Auszahlungsphase als Einmalbetrag möglich — in beiden Auszahlungsformen. Er kostet aus zwei Gründen: volle Progression im Zuflussjahr und danach nur noch der Zinssatz nach Abgeltungsteuer. Der Überschuss über den Jahreshöchstbetrag („Sleeve“) wird ohne Einmalbetrag verrentet; das ist eine Modellkonvention, kein Rechtssatz.' }, - { k:'av_auszform', src:'I', g:'kern', kind:'seg', label:'Altersvorsorgedepot: Rest', - opts:[[1,'Auszahlungsplan'],[2,'lebenslange Rente']], + /* dfmt und unit sind kennzahlabhängig, nicht sprachabhängig — sie werden + hier fertig übergeben, alles Übrige formatiert das Wörterbuch. */ + return t('n.teilkap.kosten', { gewinn: d >= 0, grenz: gr, unit: M.unit, + dtxt: d >= 0 ? M.dfmt(d) : M.dfmt(-d).replace(/^[+−]/, '') }); + } }, + { k:'av_auszform', src:'I', g:'kern', kind:'seg', + opts:[1,2], /* Bewusst ein eigener Schalter: wer die bAV als Kapital nimmt, kann das Depot trotzdem verrenten. Eine gemeinsame Umschaltung erzwänge eine Kopplung, die es rechtlich nicht gibt. */ - hint:'Was mit dem Kapital jenseits des Einmalbetrags geschieht — unabhängig von der bAV-Entscheidung darüber. Auszahlungsplan: das Geld bleibt investiert und wird verzinst entnommen; die nicht geförderte Schicht wird mit dem halben Unterschiedsbetrag besteuert. Lebenslange Rente: das Kapital geht an den Anbieter, dafür wird die nicht geförderte Schicht nur mit dem Ertragsanteil besteuert (§ 22 Nr. 5 S. 2 Buchst. a EStG, 17 % ab Alter 67 statt hier rund 26 %). Die geförderte Schicht ist in beiden Fällen voll nachgelagert steuerpflichtig, das ändert sich nicht.', liveNote:function(R){ if (S.I.av_auszform !== 2) return null; if (!(R.diagnose.memoRestAv > 1)) return null; - return 'Über den Vergleichshorizont hinaus zahlt diese Rente noch ' + eur(R.diagnose.memoRestAv) - + ', die hier NICHT mitgezählt werden. Ein fester Horizont unterschätzt jede lebenslange ' - + 'Rente systematisch — wer alt wird, gewinnt hier.'; + return t('n.av_auszform.horizont', { rest: R.diagnose.memoRestAv }); } }, - /* ---------------- Auszahlungsformen im Detail ---------------- */ - { k:'rentfak_av', src:'I', g:'bav', kind:'num', label:'Rentenfaktor Altersvorsorgedepot', min:0, max:60, step:0.5, unit:'€ je 10.000 €', - showIf:function(I){return I.av_auszform===2;}, - hint:'Nur bei Verrentung des Depots. Voreingestellt wie bei der bAV; ein Depotvertrag kommt ohne Versicherungsmantel aus und kann deshalb günstiger sein — den Wert aus dem konkreten Angebot eintragen. Bei 0 gibt es keine Rente.' }, - { k:'rentdyn_av', src:'I', g:'bav', kind:'pct', label:'Dynamik der Depotrente p.a.', min:0, max:0.05, step:0.0025, - showIf:function(I){return I.av_auszform===2;}, - hint:'Überschussbeteiligung bzw. Fondsentwicklung in der Rentenphase des Depotvertrags.' }, - { k:'rentfak', src:'I', g:'bav', kind:'num', label:'Rentenfaktor bAV', min:0, max:60, step:0.5, unit:'€ je 10.000 €', - showIf:function(I){return I.auszform===2;}, - hint:'Nur bei Auszahlungsform „Rente“. 26 € Monatsrente je 10.000 € entspricht etwa 3,1 % Entnahmequote. Steht im Produktinformationsblatt.' }, - { k:'rentdyn', src:'I', g:'bav', kind:'pct', label:'Dynamik der bAV-Rente p.a.', min:0, max:0.05, step:0.0025, - showIf:function(I){return I.auszform===2;}, - hint:'Überschussbeteiligung bzw. Fondsentwicklung in der Rentenphase.' }, - { k:'fuenftel', src:'I', g:'bav', kind:'seg', label:'Fünftelregelung prüfen', opts:[[0,'nein (konservativ)'],[1,'ja']], - hint:'§ 34 EStG ist bei planmäßiger Kapitalabfindung aus § 3 Nr. 63-Verträgen nach BFH X R 23/15 nur ausnahmsweise anwendbar. Konservativ: nein.' }, + /* ---------------- Zuschuss und Auszahlungsformen im Detail ---------------- */ + { k:'agz_fix_min', src:'I', g:'bav', kind:'eurm', min:0, max:600, step:5, + showIf:function(I){return I.agz_fix > 0;}, + liveNote:function(R){ + if (!(S.I.agz_fix_min > 0)) return null; + var a = R.A[0]; + if (!a.aktiv) return null; + var d = a.bav_br/12 - S.I.agz_fix_min; + if (d >= 0 && d < 25) return t('n.agz_fix_min.knapp', { d: d }); + if (d < 0) return t('n.agz_fix_min.verfehlt', { fehlt: -d, zuschuss: S.I.agz_fix }); + return null; + } }, + { k:'rentfak_av', src:'I', g:'bav', kind:'num', min:0, max:60, step:0.5, + showIf:function(I){return I.av_auszform===2;} }, + { k:'rentdyn_av', src:'I', g:'bav', kind:'pct', min:0, max:0.05, step:0.0025, + showIf:function(I){return I.av_auszform===2;} }, + { k:'rentfak', src:'I', g:'bav', kind:'num', min:0, max:60, step:0.5, + showIf:function(I){return I.auszform===2;} }, + { k:'rentdyn', src:'I', g:'bav', kind:'pct', min:0, max:0.05, step:0.0025, + showIf:function(I){return I.auszform===2;} }, + { k:'fuenftel', src:'I', g:'bav', kind:'seg', opts:[0,1] }, /* ---------------- Person und Zeitachse ---------------- */ - { k:'planende', src:'I', g:'person', kind:'num', label:'Vergleichshorizont bis Alter', min:70, max:100, step:1, unit:'Jahre', - hint:'Auszahlungspläne laufen bis hierhin. Lebenslange Renten werden über denselben Horizont verglichen; was danach noch fließt, steht als Memo unter „Diagnose“. Wer alt wird, gewinnt dort.' }, - { k:'ausscheid', src:'I', g:'person', kind:'num', label:'Ausscheiden aus dem Job', min:40, max:75, step:1, unit:'Jahre', - hint:'Ab hier kein Gehalt und keine Beiträge mehr. Das Kapital verzinst sich weiter bis zum Auszahlungsbeginn.' }, - { k:'tzalter', src:'I', g:'person', kind:'num', label:'Teilzeit ab Alter', min:20, max:999, step:1, unit:'Jahre', - hint:'Jeder Wert ab dem Auszahlungsbeginn bedeutet: keine Teilzeit. Voreingestellt ist deshalb 67.' }, - { k:'tzfaktor', src:'I', g:'person', kind:'pct', label:'Teilzeitfaktor', min:0.1, max:1, step:0.05, - hint:'Anteil des Vollzeitgehalts. 60 % = 60-%-Stelle.' }, + { k:'planende', src:'I', g:'person', kind:'num', min:70, max:100, step:1 }, + { k:'ausscheid', src:'I', g:'person', kind:'num', min:40, max:75, step:1 }, + { k:'tzalter', src:'I', g:'person', kind:'num', min:20, max:999, step:1 }, + { k:'tzfaktor', src:'I', g:'person', kind:'pct', min:0.1, max:1, step:0.05 }, /* ---------------- Steuer und Familie ---------------- */ - { k:'gsteig', src:'I', g:'steuer', kind:'pct', label:'Gehalts- und Lohnsteigerung p.a.', min:0, max:0.06, step:0.0025, - hint:'Schreibt auch Beitragsbemessungsgrenzen, Bezugsgröße, Durchschnittsentgelt und Rentenwert fort.' }, - { k:'stkl', src:'I', g:'steuer', kind:'seg', label:'Veranlagung', opts:[[1,'einzeln'],[3,'zusammen (Splitting)']], - hint:'Maßgeblich ist die VERANLAGUNGSART, nicht die Lohnsteuerklasse: die Klasse steuert nur den unterjährigen Abzug, der Jahresbescheid rechnet ohnehin mit Splitting nach § 32a Abs. 5 EStG. Bei „zusammen“ unbedingt das zvE des Partners eintragen.' }, - { k:'partner', src:'I', g:'steuer', kind:'eur', label:'zvE des Partners', min:0, max:250000, step:1000, - showIf:function(I){return I.stkl===3;}, - hint:'Nur bei Zusammenveranlagung. Gerechnet wird 2·ESt((zvE + zvE_Partner)/2).' }, - { k:'kist', src:'I', g:'steuer', kind:'pct', label:'Kirchensteuersatz', min:0, max:0.09, step:0.01, - hint:'8 % in Bayern und Baden-Württemberg, sonst 9 %, 0 bei Kirchenaustritt. Wirkt auch auf die Abgeltungsteuer: 0,25/(1+0,25k)·(1+0,055+k).' }, - { k:'tarifanp', src:'I', g:'steuer', kind:'pct', label:'Anpassung der Tarifeckwerte p.a.', min:0, max:0.05, step:0.0025, - hint:'Der ganze Tarif wird um den Faktor λ = (1+x)^t gestreckt: ESt = s·λ·T(zvE/s/λ). Das ist exakt äquivalent zu einer proportionalen Verschiebung aller Eckwerte. 0 % zeigt volle kalte Progression.' }, - { k:'kinder', src:'I', g:'steuer', kind:'num', label:'Anzahl Kinder', min:0, max:8, step:1, - hint:'Kinderzulage im Altersvorsorgedepot: 1 € je eingezahltem € bis 300 € je Kind.' }, - { k:'kind_jahre', src:'I', g:'steuer', kind:'num', label:'Jahre mit Kinderzulage', min:0, max:25, step:1, unit:'Jahre', - showIf:function(I){return I.kinder>0;}, - hint:'Die Kinderzulage hängt am Kindergeldanspruch (§ 85 EStG), läuft also nicht über die ganze Ansparphase.' }, - { k:'kinderlos', src:'I', g:'steuer', kind:'seg', label:'Kinderlos (PV-Zuschlag)', opts:[[0,'nein'],[1,'ja']], - hint:'0,6 Prozentpunkte Zuschlag zur Pflegeversicherung, allein vom Arbeitnehmer zu tragen (§ 55 Abs. 3 SGB XI).' }, + { k:'gsteig', src:'I', g:'steuer', kind:'pct', min:0, max:0.06, step:0.0025 }, + { k:'stkl', src:'I', g:'steuer', kind:'seg', opts:[1,3] }, + { k:'partner', src:'I', g:'steuer', kind:'eur', min:0, max:250000, step:1000, + showIf:function(I){return I.stkl===3;} }, + { k:'kist', src:'I', g:'steuer', kind:'pct', min:0, max:0.09, step:0.01 }, + { k:'tarifanp', src:'I', g:'steuer', kind:'pct', min:0, max:0.05, step:0.0025 }, + { k:'kinder', src:'I', g:'steuer', kind:'num', min:0, max:8, step:1 }, + { k:'kind_jahre', src:'I', g:'steuer', kind:'num', min:0, max:25, step:1, + showIf:function(I){return I.kinder>0;} }, + { k:'kinderlos', src:'I', g:'steuer', kind:'seg', opts:[0,1] }, /* ---------------- Kapitalmarkt ---------------- */ - { k:'k_bav', src:'I', g:'markt', kind:'pct', label:'Kosten bAV-Produkt p.a.', min:0, max:0.03, step:0.0005, - hint:'Versicherungsmantel plus TER. Die Effektivkosten stehen im Produktinformationsblatt — dort nachsehen, nicht schätzen.' }, - { k:'k_av', src:'I', g:'markt', kind:'pct', label:'Kosten Altersvorsorgedepot p.a.', min:0, max:0.03, step:0.0005, - hint:'Beim Standarddepot sind die Effektivkosten gesetzlich auf 1,0 % p.a. gedeckelt.' }, - { k:'k_priv', src:'I', g:'markt', kind:'pct', label:'Kosten privates ETF-Depot p.a.', min:0, max:0.03, step:0.0005, - hint:'Reine TER eines breiten Welt-ETF.' }, - { k:'infl', src:'I', g:'markt', kind:'pct', label:'Inflation p.a.', min:0, max:0.06, step:0.0025, - hint:'Diskontierungssatz für alle Angaben „in heutiger Kaufkraft“. Ändert die Rangfolge kaum, aber alle Niveaus.' }, + { k:'k_bav', src:'I', g:'markt', kind:'pct', min:0, max:0.03, step:0.0005 }, + { k:'k_av', src:'I', g:'markt', kind:'pct', min:0, max:0.03, step:0.0005 }, + { k:'k_priv', src:'I', g:'markt', kind:'pct', min:0, max:0.03, step:0.0005 }, + { k:'infl', src:'I', g:'markt', kind:'pct', min:0, max:0.06, step:0.0025 }, /* ---------------- Ruhestand ---------------- */ - { k:'sonst_zve', src:'I', g:'ruhe', kind:'eur', label:'sonstiges zvE im Ruhestand', min:0, max:120000, step:1000, - hint:'Vor allem der steuerpflichtige Teil der gesetzlichen Rente, ohne die hier verglichenen Produkte. Bestimmt den Grenzsteuersatz in der Auszahlungsphase — und damit die Tarifarbitrage t₀ − t₁.' }, - { k:'rente_br', src:'I', g:'ruhe', kind:'eur', label:'gesetzliche Bruttorente p.a.', min:0, max:120000, step:1000, - hint:'Nur für den Deckel in der Krankenversicherung: gesetzliche Rente und Versorgungsbezüge teilen sich EINE Beitragsbemessungsgrenze (§ 223 Abs. 3 SGB V).' }, + { k:'sonst_zve', src:'I', g:'ruhe', kind:'eur', min:0, max:120000, step:1000 }, + { k:'rente_br', src:'I', g:'ruhe', kind:'eur', min:0, max:120000, step:1000 }, /* ---------------- Rechengrößen 2026 ---------------- */ - { k:'bbg_rv', src:'P', g:'p_sv', kind:'eur', label:'BBG Rentenversicherung (jährlich)', min:50000, max:200000, step:100, hint:'SVBezGrV 2026 (BGBl. 2025 I Nr. 116), bundeseinheitlich.' }, - { k:'bbg_kv', src:'P', g:'p_sv', kind:'eur', label:'BBG Kranken-/Pflegeversicherung', min:40000, max:150000, step:100, hint:'SVBezGrV 2026; 5.812,50 € monatlich.' }, - { k:'bezug', src:'P', g:'p_sv', kind:'eur', label:'Bezugsgröße (monatlich)', min:2000, max:8000, step:5, hint:'SVBezGrV 2026, 47.460 € jährlich. Ein Zwanzigstel davon ist der Freibetrag für Versorgungsbezüge in der KV (§ 226 Abs. 2 S. 2 SGB V).' }, - { k:'de_rv', src:'P', g:'p_sv', kind:'eur', label:'Durchschnittsentgelt RV', min:30000, max:90000, step:100, hint:'Anlage 1 zu SGB VI, Wert 2026 — Nenner für die Entgeltpunkte.' }, - { k:'rw', src:'P', g:'p_sv', kind:'num', label:'Aktueller Rentenwert', min:20, max:80, step:0.01, unit:'€', - hint:'Rentenwertbestimmungsverordnung 2026, ab 1.7.2026: 42,52 € (+4,24 %).' }, - { k:'rv_an', src:'P', g:'p_sv', kind:'pct', label:'RV-Beitrag Arbeitnehmeranteil', min:0, max:0.15, step:0.0005, hint:'18,6 % hälftig.' }, - { k:'av_an', src:'P', g:'p_sv', kind:'pct', label:'AV-Beitrag Arbeitnehmeranteil', min:0, max:0.05, step:0.0005, hint:'2,6 % hälftig.' }, - { k:'kv_allg', src:'P', g:'p_sv', kind:'pct', label:'KV allgemeiner Beitragssatz', min:0, max:0.25, step:0.001, hint:'§ 241 SGB V.' }, - { k:'kv_zus', src:'P', g:'p_sv', kind:'pct', label:'KV durchschnittlicher Zusatzbeitrag', min:0, max:0.06, step:0.001, hint:'Bekanntmachung des BMG; kassenindividuell abweichend — den Satz Ihrer Kasse eintragen.' }, - { k:'pv_ges', src:'P', g:'p_sv', kind:'pct', label:'PV Gesamtbeitragssatz', min:0, max:0.08, step:0.001, hint:'§ 55 SGB XI. Kinderabschläge ab dem zweiten Kind sind nicht modelliert.' }, - { k:'pv_an', src:'P', g:'p_sv', kind:'pct', label:'PV Arbeitnehmeranteil', min:0, max:0.05, step:0.0005, hint:'In Sachsen abweichend (2,3 %).' }, - { k:'pv_kl', src:'P', g:'p_sv', kind:'pct', label:'PV Zuschlag für Kinderlose', min:0, max:0.02, step:0.0005, hint:'§ 55 Abs. 3 SGB XI, allein vom Arbeitnehmer.' }, - - { k:'gfb', src:'P', g:'p_est', kind:'eur', label:'Grundfreibetrag (Ende Zone 1)', min:5000, max:30000, step:1, hint:'§ 32a Abs. 1 EStG i.d.F. des Steuerfortentwicklungsgesetzes.' }, - { k:'e2', src:'P', g:'p_est', kind:'eur', label:'Ende Zone 2', min:10000, max:40000, step:1, hint:'Grenzsteuersatz steigt von 14 % auf 23,97 %.' }, - { k:'e3', src:'P', g:'p_est', kind:'eur', label:'Ende Zone 3', min:40000, max:120000, step:1, hint:'Grenzsteuersatz steigt von 23,97 % auf 42 %.' }, - { k:'e4', src:'P', g:'p_est', kind:'eur', label:'Beginn Zone 5 (Reichensteuer)', min:150000, max:400000, step:1, hint:'Ab 277.826 € gilt 45 %.' }, - { k:'a2', src:'P', g:'p_est', kind:'num', label:'Koeffizient a₂', min:0, max:2000, step:0.01, hint:'ESt = (a₂·y + b₂)·y mit y = (zvE − Grundfreibetrag)/10.000.' }, - { k:'b2', src:'P', g:'p_est', kind:'num', label:'Koeffizient b₂', min:0, max:3000, step:1, hint:'' }, - { k:'a3', src:'P', g:'p_est', kind:'num', label:'Koeffizient a₃', min:0, max:1000, step:0.01, hint:'ESt = (a₃·z + b₃)·z + c₃ mit z = (zvE − 17.799)/10.000.' }, - { k:'b3', src:'P', g:'p_est', kind:'num', label:'Koeffizient b₃', min:0, max:5000, step:1, hint:'' }, - { k:'c3', src:'P', g:'p_est', kind:'num', label:'Konstante c₃', min:0, max:5000, step:0.01, hint:'' }, - { k:'c4', src:'P', g:'p_est', kind:'num', label:'Konstante c₄', min:0, max:30000, step:0.01, hint:'ESt = 0,42·zvE − c₄.' }, - { k:'c5', src:'P', g:'p_est', kind:'num', label:'Konstante c₅', min:0, max:40000, step:0.01, hint:'ESt = 0,45·zvE − c₅.' }, - { k:'soli', src:'P', g:'p_est', kind:'pct', label:'Solidaritätszuschlag', min:0, max:0.08, step:0.0005, hint:'SolzG 1995.' }, - { k:'soli_fg', src:'P', g:'p_est', kind:'eur', label:'Soli-Freigrenze', min:0, max:60000, step:10, hint:'2026, Grundtarif, bezogen auf die festgesetzte ESt; im Splitting doppelt. Daneben eine Milderungszone.' }, - { k:'soli_mz', src:'P', g:'p_est', kind:'pct', label:'Milderungszone Grenzbelastung', min:0, max:0.3, step:0.001, hint:'§ 3 Abs. 2a SolzG.' }, - { k:'an_pausch', src:'P', g:'p_est', kind:'eur', label:'Arbeitnehmer-Pauschbetrag', min:0, max:5000, step:1, hint:'§ 9a S. 1 Nr. 1a EStG.' }, - { k:'sa_pausch', src:'P', g:'p_est', kind:'eur', label:'Sonderausgaben-Pauschbetrag', min:0, max:1000, step:1, hint:'§ 10c EStG.' }, - - { k:'kapst', src:'P', g:'p_kap', kind:'pct', label:'Abgeltungsteuersatz', min:0, max:0.5, step:0.005, hint:'§ 32d Abs. 1 EStG.' }, - { k:'tfs', src:'P', g:'p_kap', kind:'pct', label:'Teilfreistellung Aktienfonds', min:0, max:0.6, step:0.01, hint:'§ 20 Abs. 1 Nr. 3 i.V.m. § 20 InvStG, Aktienfondsanteil über 50 %.' }, - { k:'sparerpb', src:'P', g:'p_kap', kind:'eur', label:'Sparer-Pauschbetrag', min:0, max:5000, step:10, hint:'§ 20 Abs. 9 EStG; im Splitting verdoppelt. Absorbiert einen großen Teil der Vorabpauschale — siehe Wasserfall.' }, - { k:'basiszins', src:'P', g:'p_kap', kind:'pct', label:'Basiszins Vorabpauschale', min:0, max:0.08, step:0.0005, hint:'BMF-Schreiben vom 13.01.2026, § 18 Abs. 4 InvStG (2025: 2,53 %). Auf 0 setzen heißt: keine Vorabpauschale.' }, - { k:'vp_faktor', src:'P', g:'p_kap', kind:'pct', label:'Faktor für den Basisertrag', min:0, max:1, step:0.05, hint:'§ 18 Abs. 1 S. 1 InvStG.' }, - - { k:'st_frei_q', src:'P', g:'p_bav', kind:'pct', label:'steuerfrei § 3 Nr. 63 (Anteil BBG-RV)', min:0, max:0.2, step:0.005, hint:'8 % der BBG-RV = 8.112 € in 2026.' }, - { k:'sv_frei_q', src:'P', g:'p_bav', kind:'pct', label:'sozialabgabenfrei (Anteil BBG-RV)', min:0, max:0.2, step:0.005, hint:'§ 1 Abs. 1 S. 1 Nr. 9 SvEV; 4 % = 4.056 € in 2026. Zugleich die Grenze des Rechtsanspruchs nach § 1a BetrAVG.' }, - { k:'zul_g1', src:'P', g:'p_bav', kind:'pct', label:'Zulage Stufe 1 (je € Eigenbeitrag)', min:0, max:1, step:0.05, hint:'Altersvorsorgereformgesetz, in Kraft seit Mai 2026, Anwendung ab 1.1.2027.' }, - { k:'zul_s1', src:'P', g:'p_bav', kind:'eur', label:'Grenze Stufe 1', min:0, max:3000, step:10, hint:'Bis 360 € Eigenbeitrag: 50 Cent je €.' }, - { k:'zul_g2', src:'P', g:'p_bav', kind:'pct', label:'Zulage Stufe 2 (je € Eigenbeitrag)', min:0, max:1, step:0.05, hint:'Von 361 bis 1.800 €: 25 Cent je €.' }, - { k:'zul_s2', src:'P', g:'p_bav', kind:'eur', label:'Grenze Stufe 2', min:0, max:6000, step:10, hint:'Höchstbetrag geförderter Eigenbeitrag; ergibt eine maximale Grundzulage von 540 €.' }, - { k:'kind_zul', src:'P', g:'p_bav', kind:'eur', label:'Kinderzulage je Kind', min:0, max:1000, step:10, hint:'1 € je eingezahltem € bis 300 € je Kind.' }, - { k:'av_max', src:'P', g:'p_bav', kind:'eur', label:'Höchstbetrag Einzahlung p.a.', min:0, max:20000, step:10, hint:'BMF-FAQ zur Reform der geförderten privaten Altersvorsorge, Stand 05.05.2026. Was darüber liegt, läuft in eine Nebenschiene, die wie ein Privatdepot besteuert wird.' }, - { k:'av_sa_max', src:'P', g:'p_bav', kind:'eur', label:'Höchstbetrag Sonderausgabenabzug § 10a', min:0, max:12000, step:10, hint:'MODELLANNAHME, Vertrauensgrad ca. 70 %. Eigenbeitrag bis 1.800 € zuzüglich Zulagen. Diese eine Zahl entscheidet, ob ein Kind den Wert des AV-Depots hebt oder senkt.' }, - { k:'ertragsq', src:'P', g:'p_bav', kind:'pct', label:'Ertragsanteil (Alter 67)', min:0, max:0.6, step:0.01, hint:'§ 22 Nr. 1 S. 3 Buchst. a Doppelbuchst. bb EStG, für nicht geförderte Rententeile.' }, - { k:'halb', src:'P', g:'p_bav', kind:'pct', label:'steuerpflichtiger Anteil des Unterschiedsbetrags', min:0, max:1, step:0.05, hint:'§ 20 Abs. 1 Nr. 6 S. 2 EStG analog (Vertrag über 12 Jahre, Auszahlung nach 62): halber Unterschiedsbetrag. Vertrauensgrad ca. 80 %.' } + { k:'bbg_rv', src:'P', g:'p_sv', kind:'eur', min:50000, max:200000, step:100 }, + { k:'bbg_kv', src:'P', g:'p_sv', kind:'eur', min:40000, max:150000, step:100 }, + { k:'bezug', src:'P', g:'p_sv', kind:'eur', min:2000, max:8000, step:5 }, + { k:'de_rv', src:'P', g:'p_sv', kind:'eur', min:30000, max:90000, step:100 }, + { k:'rw', src:'P', g:'p_sv', kind:'num', min:20, max:80, step:0.01 }, + { k:'rv_an', src:'P', g:'p_sv', kind:'pct', min:0, max:0.15, step:0.0005 }, + { k:'av_an', src:'P', g:'p_sv', kind:'pct', min:0, max:0.05, step:0.0005 }, + { k:'kv_allg', src:'P', g:'p_sv', kind:'pct', min:0, max:0.25, step:0.001 }, + { k:'kv_zus', src:'P', g:'p_sv', kind:'pct', min:0, max:0.06, step:0.001 }, + { k:'pv_ges', src:'P', g:'p_sv', kind:'pct', min:0, max:0.08, step:0.001 }, + { k:'pv_an', src:'P', g:'p_sv', kind:'pct', min:0, max:0.05, step:0.0005 }, + { k:'pv_kl', src:'P', g:'p_sv', kind:'pct', min:0, max:0.02, step:0.0005 }, + + { k:'gfb', src:'P', g:'p_est', kind:'eur', min:5000, max:30000, step:1 }, + { k:'e2', src:'P', g:'p_est', kind:'eur', min:10000, max:40000, step:1 }, + { k:'e3', src:'P', g:'p_est', kind:'eur', min:40000, max:120000, step:1 }, + { k:'e4', src:'P', g:'p_est', kind:'eur', min:150000, max:400000, step:1 }, + { k:'a2', src:'P', g:'p_est', kind:'num', min:0, max:2000, step:0.01 }, + { k:'b2', src:'P', g:'p_est', kind:'num', min:0, max:3000, step:1 }, + { k:'a3', src:'P', g:'p_est', kind:'num', min:0, max:1000, step:0.01 }, + { k:'b3', src:'P', g:'p_est', kind:'num', min:0, max:5000, step:1 }, + { k:'c3', src:'P', g:'p_est', kind:'num', min:0, max:5000, step:0.01 }, + { k:'c4', src:'P', g:'p_est', kind:'num', min:0, max:30000, step:0.01 }, + { k:'c5', src:'P', g:'p_est', kind:'num', min:0, max:40000, step:0.01 }, + { k:'soli', src:'P', g:'p_est', kind:'pct', min:0, max:0.08, step:0.0005 }, + { k:'soli_fg', src:'P', g:'p_est', kind:'eur', min:0, max:60000, step:10 }, + { k:'soli_mz', src:'P', g:'p_est', kind:'pct', min:0, max:0.3, step:0.001 }, + { k:'an_pausch', src:'P', g:'p_est', kind:'eur', min:0, max:5000, step:1 }, + { k:'sa_pausch', src:'P', g:'p_est', kind:'eur', min:0, max:1000, step:1 }, + + { k:'kapst', src:'P', g:'p_kap', kind:'pct', min:0, max:0.5, step:0.005 }, + { k:'tfs', src:'P', g:'p_kap', kind:'pct', min:0, max:0.6, step:0.01 }, + { k:'sparerpb', src:'P', g:'p_kap', kind:'eur', min:0, max:5000, step:10 }, + { k:'basiszins', src:'P', g:'p_kap', kind:'pct', min:0, max:0.08, step:0.0005 }, + { k:'vp_faktor', src:'P', g:'p_kap', kind:'pct', min:0, max:1, step:0.05 }, + + { k:'st_frei_q', src:'P', g:'p_bav', kind:'pct', min:0, max:0.2, step:0.005 }, + { k:'sv_frei_q', src:'P', g:'p_bav', kind:'pct', min:0, max:0.2, step:0.005 }, + { k:'zul_g1', src:'P', g:'p_bav', kind:'pct', min:0, max:1, step:0.05 }, + { k:'zul_s1', src:'P', g:'p_bav', kind:'eur', min:0, max:3000, step:10 }, + { k:'zul_g2', src:'P', g:'p_bav', kind:'pct', min:0, max:1, step:0.05 }, + { k:'zul_s2', src:'P', g:'p_bav', kind:'eur', min:0, max:6000, step:10 }, + { k:'kind_zul', src:'P', g:'p_bav', kind:'eur', min:0, max:1000, step:10 }, + { k:'av_max', src:'P', g:'p_bav', kind:'eur', min:0, max:20000, step:10 }, + { k:'av_sa_max', src:'P', g:'p_bav', kind:'eur', min:0, max:12000, step:10 }, + { k:'ertragsq', src:'P', g:'p_bav', kind:'pct', min:0, max:0.6, step:0.01 }, + { k:'halb', src:'P', g:'p_bav', kind:'pct', min:0, max:1, step:0.05 } ]; - var GROUPS = [ - { g:'bav', t:'Auszahlungsformen im Detail' }, - { g:'person',t:'Zeitachse, Ausscheiden, Teilzeit' }, - { g:'steuer',t:'Steuer, Veranlagung, Kinder' }, - { g:'markt', t:'Kosten und Inflation' }, - { g:'ruhe', t:'Ruhestand' }, - { g:'p_sv', t:'Rechengrößen: Sozialversicherung 2026', legal:true }, - { g:'p_est', t:'Rechengrößen: Einkommensteuertarif 2026 (§ 32a EStG)', legal:true }, - { g:'p_kap', t:'Rechengrößen: Kapitalertrag- und Investmentsteuer', legal:true }, - { g:'p_bav', t:'Rechengrößen: betriebliche und geförderte private Vorsorge', legal:true } + { g:'bav' }, { g:'person' }, { g:'steuer' }, { g:'markt' }, { g:'ruhe' }, + { g:'p_sv', legal:true }, { g:'p_est', legal:true }, + { g:'p_kap', legal:true }, { g:'p_bav', legal:true } ]; var FMAP = {}; FIELDS.forEach(function(f){ FMAP[f.src + ':' + f.k] = f; }); +/* Beschriftung, Erklärung und Einheit eines Feldes stehen im Wörterbuch unter + 'f.<src>:<key>.label|hint|unit', die Beschriftungen der Optionsfelder unter + '...o<wert>'. Wie bei OPTS sind es Zugriffsfunktionen: `f.label` bleibt + lesbar und ist zugleich sprachaktuell. Ein fehlender Eintrag liefert den + leeren String — daran hängt, ob das Feld eine „?“-Schaltfläche bekommt + (die Koeffizienten b₂, b₃ und c₃ haben bewusst keine Erklärung). */ +FIELDS.forEach(function(f){ + var base = 'f.' + f.src + ':' + f.k; + Object.defineProperty(f, 'label', { get: function(){ return t(base + '.label'); } }); + Object.defineProperty(f, 'hint', { get: function(){ return tHas(base + '.hint') ? t(base + '.hint') : ''; } }); + Object.defineProperty(f, 'unit', { get: function(){ return tHas(base + '.unit') ? t(base + '.unit') : ''; } }); + f.optLabel = function(v){ return t(base + '.o' + v); }; +}); + /* ===================================================================== TEIL 3 — ZUSTAND @@ -1199,7 +1280,16 @@ var S = { wfZiel: 'bav', open: {} }; -var R = null, BASE = null, BASEDESC = 'Ausgangswerte', PINNED = false, armed = null; +var R = null, BASE = null, BASEKEY = null, PINNED = false, armed = null; +/* Der Referenzstand wird als Feldschlüssel gemerkt, nicht als fertiger Satz: + sonst bliebe seine Beschreibung nach einem Sprachwechsel in der alten + Sprache stehen. */ +function baseDesc(){ + if (PINNED) return t('base.desc.pinned'); + if (!BASEKEY) return t('base.desc.prev'); + var f = FIELDS.filter(function(x){ return x.k === BASEKEY; })[0]; + return f ? t('base.desc.change', { label: f.label }) : t('base.desc.prev'); +} var koppelWarn = null; // 'unerreichbar' | 'inaktiv' | null — siehe netto_ziel.set function loadState() { @@ -1233,15 +1323,55 @@ function encodeState() { /* ===================================================================== TEIL 4 — FORMATIERUNG ===================================================================== */ -var nf0 = new Intl.NumberFormat('de-DE', {maximumFractionDigits:0}); -var nf2 = new Intl.NumberFormat('de-DE', {minimumFractionDigits:2, maximumFractionDigits:2}); -var nfp = new Intl.NumberFormat('de-DE', {minimumFractionDigits:2, maximumFractionDigits:2}); +/* Die Währung bleibt in allen Sprachen der Euro und steht nachgestellt; + umgeschaltet wird nur die Zifferngruppierung: 75.000,00 € bzw. 75,000.00 €. + en-GB statt en-US, weil dort das nachgestellte Symbol dem hiesigen Gebrauch + näher liegt; für reine Zahlen ist der Unterschied ohnehin keiner. Latein + bekommt die kontinentale Schreibweise — römische Ziffern wären ein Witz auf + Kosten der Lesbarkeit, und die Trennzeichenkonvention ist ohnehin jünger + als jede lateinische Quelle. Japanisch gruppiert wie das Englische. */ +var LOCALES = { de:'de-DE', en:'en-GB', la:'de-DE', ja:'ja-JP' }; +var GRUPPEN = { de:'.', en:',', la:'.', ja:',' }; +var LOCALE = 'de-DE', nf0 = null, nf2 = null, nfp = null; +function applyLocale(){ + LOCALE = LOCALES[LANG] || 'de-DE'; + nf0 = new Intl.NumberFormat(LOCALE, {maximumFractionDigits:0}); + nf2 = new Intl.NumberFormat(LOCALE, {minimumFractionDigits:2, maximumFractionDigits:2}); + nfp = nf2; + FMT.eur = eur; FMT.eur2 = eur2; FMT.pct = pct; FMT.num = num; FMT.lang = LANG; + FMT.nf0 = function(x){ return nf0.format(x); }; + FMT.nf2 = function(x){ return nf2.format(x); }; +} function eur(x){ return (isFinite(x)?nf0.format(Math.round(x)):'—') + ' €'; } function eur2(x){ return (isFinite(x)?nf2.format(x):'—') + ' €'; } function pct(x,d){ if(!isFinite(x)) return '—'; - return new Intl.NumberFormat('de-DE',{minimumFractionDigits:d===undefined?2:d,maximumFractionDigits:d===undefined?2:d}).format(x*100)+' %'; } -function num(x,d){ return new Intl.NumberFormat('de-DE',{minimumFractionDigits:d||0,maximumFractionDigits:d||0}).format(x); } -function parseDE(s){ s = String(s).replace(/\s| |\./g,'').replace(',', '.').replace(/[^\d.\-]/g,''); var v = parseFloat(s); return isFinite(v)?v:NaN; } + return new Intl.NumberFormat(LOCALE,{minimumFractionDigits:d===undefined?2:d,maximumFractionDigits:d===undefined?2:d}).format(x*100)+' %'; } +function num(x,d){ return new Intl.NumberFormat(LOCALE,{minimumFractionDigits:d||0,maximumFractionDigits:d||0}).format(x); } +/* Eingabe: beide Schreibweisen werden angenommen, damit ein Sprachwechsel + keine getippte Zahl entwertet. Entscheidungsregel — stehen beide Zeichen im + Text, ist das rechte das Dezimalzeichen (1.234,56 wie 1,234.56). Steht nur + eines und folgen ihm genau drei Ziffern, ist es die Tausendergruppe der + aktuellen Sprache; sonst ist es das Dezimalzeichen. Damit bleibt allein + „1,234“ mehrdeutig, und genau dort entscheidet die Sprache. */ +function parseNum(s){ + s = String(s).replace(/[^\d.,\-]/g, ''); + var d = s.lastIndexOf('.'), c = s.lastIndexOf(','), sep = Math.max(d, c); + var grp = GRUPPEN[LANG] || '.', dec = ''; + if (sep >= 0){ + var ch = s.charAt(sep); + var tausender = /^\d{3}$/.test(s.slice(sep + 1)) && ch === grp && !(d >= 0 && c >= 0); + dec = tausender ? '' : ch; + } + var out = ''; + for (var i = 0; i < s.length; i++){ + var z = s.charAt(i); + if (z === '-'){ if (i === 0) out += z; continue; } + if (z === '.' || z === ','){ if (dec && i === sep) out += '.'; continue; } + out += z; + } + var v = parseFloat(out); + return isFinite(v) ? v : NaN; +} /* Anzeigewert eines Feldes (Zahl im Eingabefeld) und Rückumrechnung */ function fieldToView(f, v){ @@ -1253,9 +1383,9 @@ function viewToField(f, v){ return v; } function fieldUnit(f){ - if (f.kind === 'pct') return '%'; - if (f.kind === 'eur') return '€'; - if (f.kind === 'eurm') return '€/Monat'; + if (f.kind === 'pct') return t('unit.pct'); + if (f.kind === 'eur') return t('unit.eur'); + if (f.kind === 'eurm') return t('unit.eurm'); return f.unit || ''; } /* Felder mit `get`/`set` sind abgeleitet: ihre Wahrheit liegt woanders, @@ -1269,25 +1399,28 @@ function setFieldValue(f, v){ /* ===================================================================== TEIL 5 — KENNZAHLEN ===================================================================== */ +/* Schlagzahlen auf ganze Euro. Ein Modell, dessen unsicherste Annahme einen + Vertrauensgrad von 70 % hat, darf nicht auf den Cent genau auftreten — + die Nachkommastellen sind Scheingenauigkeit. Im Rechenweg, wo es um die + Nachvollziehbarkeit der Additivität geht, bleibt es bei zwei Stellen. + label, desc und unit kommen wie bei OPTS und FIELDS aus dem Wörterbuch. */ var METRICS = { - /* Schlagzahlen auf ganze Euro. Ein Modell, dessen unsicherste Annahme einen - Vertrauensgrad von 70 % hat, darf nicht auf den Cent genau auftreten — - die Nachkommastellen sind Scheingenauigkeit. Im Rechenweg, wo es um die - Nachvollziehbarkeit der Additivität geht, bleibt es bei zwei Stellen. */ - monat: { label:'Monatsleistung', get:function(r,o){return r.monat[o];}, fmt:eur, unit:'€/Monat', - desc:'Konstante Monatszahlung über die gesamte Auszahlungsphase mit demselben Wert, in heutiger Kaufkraft. Die einzige Zahl, die sich unmittelbar mit Ihrem heutigen Nettoeinkommen vergleichen lässt.', + monat: { get:function(r,o){return r.monat[o];}, fmt:eur, dfmt:function(d){return (d>=0?'+':'−')+nf0.format(Math.round(Math.abs(d)))+' €';} }, - ertrag: { label:'Netto-Ertrag (Barwert)', get:function(r,o){return r.ertrag[o];}, fmt:eur, unit:'€', - desc:'Was die Option in heutiger Kaufkraft über den eigenen Einsatz hinaus abwirft: Barwert aller Netto-Leistungen minus Barwert des Nettoaufwands.', + ertrag: { get:function(r,o){return r.ertrag[o];}, fmt:eur, dfmt:function(d){return (d>=0?'+':'−')+nf0.format(Math.round(Math.abs(d)))+' €';} }, - irr: { label:'Interner Zinsfuß', get:function(r,o){return r.irr[o];}, fmt:function(x){return pct(x,2)+' p.a.';}, - fmtKurz:function(x){return pct(x,2);}, unit:'% p.a.', - desc:'Rendite auf den tatsächlich eingesetzten Nettobetrag, nominal. Direkt mit der eingestellten Bruttorendite des ETF vergleichbar — die Differenz ist genau das, was Förderung und Abgaben zusammen kosten oder bringen.', - dfmt:function(d){return (d>=0?'+':'−')+nfp.format(Math.abs(d)*100)+' Pp.';} }, - kapital: { label:'Kapital bei Rentenbeginn', get:function(r,o){return r.kapital[o];}, fmt:eur, unit:'€', - desc:'Vor Steuern und vor Sozialabgaben, nominal. Bewusst NICHT die Entscheidungsgröße: die bAV führt hier oft, verliert aber in der Auszahlung.', + irr: { get:function(r,o){return r.irr[o];}, fmt:function(x){return pct(x,2)+t('metric.pa');}, + fmtKurz:function(x){return pct(x,2);}, + dfmt:function(d){return (d>=0?'+':'−')+nfp.format(Math.abs(d)*100)+t('metric.pp');} }, + kapital: { get:function(r,o){return r.kapital[o];}, fmt:eur, dfmt:function(d){return (d>=0?'+':'−')+nf0.format(Math.round(Math.abs(d)))+' €';} } }; +Object.keys(METRICS).forEach(function(m){ + ['label','desc','unit'].forEach(function(prop){ + Object.defineProperty(METRICS[m], prop, { enumerable:true, + get: function(){ return t('metric.' + m + '.' + prop); } }); + }); +}); /* ===================================================================== @@ -1314,7 +1447,8 @@ function buildField(f){ var lab = el('div','lab'); lab.appendChild(document.createTextNode(f.label)); if (f.hint){ - var q = el('button','qm','?'); q.type='button'; q.setAttribute('aria-label','Erklärung zu: '+f.label); + var q = el('button','qm','?'); q.type='button'; + q.setAttribute('aria-label', t('ui.hintFor', { label: f.label })); q.onclick = function(){ wrap.classList.toggle('open'); }; lab.appendChild(q); } @@ -1327,17 +1461,19 @@ function buildField(f){ Pfeiltastennavigation der Radiogruppe geschenkt mit. */ var box = el('div','radios'); var gname = 'r_' + f.src + '_' + f.k; - f.opts.forEach(function(o){ + /* f.opts enthält nur noch die Werte; die Beschriftung kommt aus dem + Wörterbuch, damit sie beim Sprachwechsel mitgeht. */ + f.opts.forEach(function(v){ var lb = document.createElement('label'); var rb = document.createElement('input'); - rb.type = 'radio'; rb.name = gname; rb.value = String(o[0]); - rb.checked = fieldValue(f) === o[0]; + rb.type = 'radio'; rb.name = gname; rb.value = String(v); + rb.checked = fieldValue(f) === v; rb.addEventListener('change', function(){ if (!rb.checked) return; - arm(f.k); setFieldValue(f, o[0]); refresh(); + arm(f.k); setFieldValue(f, v); refresh(); }); lb.appendChild(rb); - lb.appendChild(document.createTextNode(' ' + o[1])); + lb.appendChild(document.createTextNode(' ' + f.optLabel(v))); box.appendChild(lb); }); wrap.appendChild(box); @@ -1367,7 +1503,7 @@ function buildField(f){ refresh(); }; rng.addEventListener('input', function(){ arm(f.k); commit(parseFloat(rng.value), true); }); - inp.addEventListener('input', function(){ var v = parseDE(inp.value); if(isFinite(v)){ arm(f.k); commit(v,false);} }); + inp.addEventListener('input', function(){ var v = parseNum(inp.value); if(isFinite(v)){ arm(f.k); commit(v,false);} }); inp.addEventListener('blur', function(){ inp.value = formatFieldInput(f); }); /* Beim Nachziehen nie das Feld überschreiben, in dem gerade getippt wird — sonst springt der Cursor und halbfertige Eingaben werden umformatiert. */ @@ -1389,8 +1525,7 @@ function buildField(f){ if (f.pairBottom){ wrap.classList.add('pair-bottom'); var lk = el('div','couple'); - lk.innerHTML = '<span>\u21C5</span> Zwei Sichten auf dieselbe Gr\u00f6\u00dfe. ' - + '\u00c4ndern Sie eine, folgt die andere.'; + lk.innerHTML = t('ui.couple'); wrap.appendChild(lk); } return wrap; @@ -1398,7 +1533,7 @@ function buildField(f){ function formatFieldInput(f){ var v = fieldToView(f, fieldValue(f)); var d = f.kind==='pct' ? (f.step<0.001?3:(f.step<0.01?2:1)) : (f.step<1?2:0); - return new Intl.NumberFormat('de-DE',{minimumFractionDigits:0,maximumFractionDigits:d}).format(v); + return new Intl.NumberFormat(LOCALE,{minimumFractionDigits:0,maximumFractionDigits:d}).format(v); } function clamp(v,a,b){ return Math.max(a, Math.min(b, v)); } @@ -1412,14 +1547,14 @@ function buildInputs(){ if (S.open[G.g]) d.open = true; d.addEventListener('toggle', function(){ S.open[G.g] = d.open; saveState(); }); var sm = document.createElement('summary'); - sm.appendChild(document.createTextNode(G.t)); + sm.appendChild(document.createTextNode(t('group.' + G.g))); var fs = FIELDS.filter(function(f){return f.g===G.g;}); - sm.appendChild(el('span','cnt', fs.length + (G.legal?' Rechengrößen':' Felder'))); + sm.appendChild(el('span','cnt', t(G.legal ? 'ui.cnt.legal' : 'ui.cnt.fields', { n: fs.length }))); d.appendChild(sm); var body = el('div','dbody'); if (G.legal){ var p = el('p','sub'); p.style.margin='0 0 6px'; - p.textContent = 'Gesetzliche Größen mit Fundstelle. Nur ändern, wenn sich das Recht geändert hat oder Ihre Kasse abweicht.'; + p.textContent = t('ui.legalNote'); body.appendChild(p); } fs.forEach(function(f){ body.appendChild(buildField(f)); }); @@ -1436,7 +1571,7 @@ function syncVisibility(){ if (w._live && R) w._live(R); if (f.kind === 'seg'){ var bs = w.querySelectorAll('.radios input'); - for (var i=0;i<bs.length;i++) bs[i].checked = (fieldValue(f) === f.opts[i][0]); + for (var i=0;i<bs.length;i++) bs[i].checked = (fieldValue(f) === f.opts[i]); } }); } @@ -1449,8 +1584,7 @@ function arm(key){ if (PINNED) return; if (armed !== key){ BASE = R; - var f = FIELDS.filter(function(x){return x.k===key;})[0]; - BASEDESC = f ? 'vor der Änderung von „' + f.label + '“' : 'vorheriger Stand'; + BASEKEY = FIELDS.filter(function(x){return x.k===key;}).length ? key : null; armed = key; } var fd = FIELDS.filter(function(x){return x.k===key;})[0]; @@ -1471,13 +1605,13 @@ function renderCards(){ var c = el('div','opt' + (i===bi?' lead':'')); c.style.setProperty('--c', o.raw); var nm = el('div','nm'); nm.appendChild(document.createTextNode(o.name)); - if (i===bi){ var cr = el('span','crown','vorn'); cr.style.setProperty('--c', o.raw); nm.appendChild(cr); } + if (i===bi){ var cr = el('span','crown', t('card.lead')); cr.style.setProperty('--c', o.raw); nm.appendChild(cr); } c.appendChild(nm); c.appendChild(el('div','big', M.fmt(vals[i]))); if (BASE){ var d = vals[i] - M.get(BASE, o.k); var cls = Math.abs(d) < (S.metric==='irr'?1e-7:0.005) ? 'eq' : (d>0?'up':'dn'); - c.appendChild(el('div','delta '+cls, cls==='eq' ? 'unverändert' : M.dfmt(d))); + c.appendChild(el('div','delta '+cls, cls==='eq' ? t('card.unchanged') : M.dfmt(d))); } var sec = el('div','sec'); sec.style.marginTop = '6px'; @@ -1487,9 +1621,9 @@ function renderCards(){ }); $('#basetext').textContent = BASE - ? (PINNED ? 'Δ gegenüber dem gemerkten Stand' : 'Δ ' + BASEDESC) - : 'Bewegen Sie einen Regler — die Änderung erscheint hier als Δ.'; - $('#pin').textContent = PINNED ? 'Referenz lösen' : 'Als Referenz merken'; + ? (PINNED ? t('base.pinned') : t('base.delta', { desc: baseDesc() })) + : t('base.none'); + $('#pin').textContent = t(PINNED ? 'ui.pin.clear' : 'ui.pin.set'); /* Die Kennzahlen können unterschiedlich ranken: der Barwert zinst mit der Inflation ab, die äquivalente Monatsleistung mit dem Wiederanlagezins nach @@ -1500,26 +1634,15 @@ function renderCards(){ var hint = $('#rankhint'); if (swap){ hint.hidden = false; - hint.innerHTML = '<b>Die Kennzahlen sind sich uneinig.</b> Nach Monatsleistung liegt ' - + nameOf(swap[0]) + ' vor ' + nameOf(swap[1]) + ', nach Barwert des Netto-Ertrags ist es umgekehrt. ' - + 'Grund: der Barwert zinst mit der ' - + 'Inflation ab (' + pct(S.I.infl,2) + '), die Monatsleistung verrentet mit dem Wiederanlagezins nach ' - + 'Steuern (' + pct(R.diagnose.wiederanl,2) + '). Je weiter beide auseinanderliegen, desto besser steht ' - + 'eine frühe Kapitalauszahlung da. Welche Kennzahl gilt, hängt davon ab, ob Sie das Geld anlegen oder ' - + 'ausgeben würden — bei „ausgeben“ den Wert im Feld „Kosten privates ETF-Depot“ so setzen, dass der ' - + 'Wiederanlagezins gegen null geht.'; + hint.innerHTML = t('rank.swap', { a: nameOf(swap[0]), b: nameOf(swap[1]), + infl: S.I.infl, wiederanl: R.diagnose.wiederanl }); } else hint.hidden = true; var wv = $('#bisektwarn'); if (koppelWarn === 'unerreichbar'){ - wv.hidden = false; - wv.textContent = 'Dieser Nettoaufwand ist nicht erreichbar — er übersteigt Ihr Gehalt. ' - + 'Eingestellt wurde der höchste mögliche Wert.'; + wv.hidden = false; wv.textContent = t('warn.unreachable'); } else if (koppelWarn === 'inaktiv'){ - wv.hidden = false; - wv.textContent = 'Im ersten Jahr sind Sie nach Ihren Angaben nicht mehr erwerbstätig — es gibt keine ' - + 'Entgeltumwandlung, also auch keinen Nettoaufwand, zu dem sich ein Bruttobeitrag zurückrechnen ließe. ' - + 'Prüfen Sie „Ausscheiden aus dem Job“ und „Alter heute“.'; + wv.hidden = false; wv.textContent = t('warn.inactive'); } else wv.hidden = true; } /* Erste Inversion zwischen zwei Ordnungen auf denselben drei Optionen. @@ -1544,7 +1667,11 @@ function nameOf(k){ return OPTS.filter(function(o){return o.k===k;})[0].name; } denselben — also einmal rechnen und über eine Signatur zwischenspeichern. */ var wfCacheMap = {}, wfCacheSig = null; function getWaterfall(ziel){ - var sig = JSON.stringify(S.I) + '|' + JSON.stringify(S.P); + /* Die Sprache gehört in die Signatur: die Balken tragen ihre Beschriftung + und ihre Herleitung mit sich, und beide kommen aus dem Wörterbuch. Ohne + sie blieben nach einem Sprachwechsel die alten Texte stehen — sichtbar + im Fazit, das den größten Balken beim Namen nennt. */ + var sig = LANG + '|' + JSON.stringify(S.I) + '|' + JSON.stringify(S.P); if (wfCacheSig !== sig){ wfCacheMap = {}; wfCacheSig = sig; } if (!wfCacheMap[ziel]) wfCacheMap[ziel] = AVModell.waterfall(S.I, S.P, ziel); return wfCacheMap[ziel]; @@ -1562,12 +1689,11 @@ function renderVerdict(){ var diff = function(a,b){ var d = a.v - b.v; - return S.metric === 'irr' ? nfp.format(d*100) + ' Prozentpunkte' : eur(d); + return S.metric === 'irr' ? t('verdict.pp', { v: nfp.format(d*100) }) : eur(d); }; var fz = M.fmtKurz || M.fmt; - var s = '<b>' + w.satz + '</b> liegt vorn mit ' + M.fmt(w.v) + ' — ' + diff(w, zwei) - + ' mehr als ' + zwei.nom - + (drei ? ' und ' + diff(w, drei) + ' mehr als ' + drei.nom : '') + '.'; + var s = t('verdict.lead', { satz: w.satz, wert: M.fmt(w.v), d2: diff(w, zwei), nom2: zwei.nom, + d3: drei ? diff(w, drei) : '', nom3: drei ? drei.nom : '' }); /* Größter Einzelbeitrag zur Differenz. Gewinnt das Privatdepot, ist die interessante Frage, was die geförderte Alternative zurückhält. */ @@ -1576,10 +1702,8 @@ function renderVerdict(){ var wf = getWaterfall(ziel), top = null; wf.bars.forEach(function(b){ if (!top || Math.abs(b.wert) > Math.abs(top.wert)) top = b; }); if (top && Math.abs(top.wert) > 1){ - s += ' Den größten Einzelposten im Abstand ' + opt(ziel).gen + ' zum privaten ETF-Depot bildet ' - + '„' + top.label + '“ — und zwar ' + (top.wert >= 0 ? 'zugunsten' : 'zulasten') - + ' ' + opt(ziel).gen + ', mit ' + eur(Math.abs(top.wert)) + ' Barwert. ' - + 'Die vollständige Zerlegung steht unter „Werttreiber“.'; + s += t('verdict.top', { gen: opt(ziel).gen, label: top.label, + pro: top.wert >= 0, wert: top.wert }); } } @@ -1589,13 +1713,8 @@ function renderVerdict(){ var hi = AVModell.rechne(Object.assign({}, S.I, { rendite: S.I.rendite + 0.01 }), S.P); var ordLo = argsort(lo, M), ordHi = argsort(hi, M), ordNow = vals.map(function(x){return x.k;}).join(); var stabil = ordLo === ordNow && ordHi === ordNow; - s += '<span class="band">Wie belastbar ist das? Bei einer Bruttorendite von ' - + pct(Math.max(0, S.I.rendite-0.01),1) + ' statt ' + pct(S.I.rendite,1) + ' wären es ' - + fz(M.get(lo, w.k)) + ', bei ' + pct(S.I.rendite+0.01,1) + ' wären es ' - + fz(M.get(hi, w.k)) + ' — die Reihenfolge ' - + (stabil ? 'bleibt in dieser Spanne dieselbe.' - : '<b>kehrt sich in dieser Spanne um</b>, das Ergebnis trägt hier also nicht.') - + '</span>'; + s += t('verdict.band', { lo: Math.max(0, S.I.rendite-0.01), mid: S.I.rendite, hi: S.I.rendite+0.01, + vlo: fz(M.get(lo, w.k)), vhi: fz(M.get(hi, w.k)), stabil: stabil }); host.innerHTML = s; } function argsort(r, M){ @@ -1615,17 +1734,14 @@ function renderDemo(){ var el0 = $('#demo'); if (!istUnveraendert()){ el0.hidden = true; return; } el0.hidden = false; - el0.innerHTML = '<b>Das sind noch Beispielwerte</b> — 40 Jahre alt, 75.000 € Jahresbrutto, ' - + '300 € Entgeltumwandlung im Monat, gesetzlich krankenversichert. Tragen Sie unter ' - + '„Ihre Angaben“ Ihre eigenen ein; dieser Hinweis verschwindet dann von selbst.'; + el0.innerHTML = t('demo.text'); } /* Mitlaufende Leiste. Erscheint, sobald die Ergebniskarten aus dem Bild sind. */ function renderSticky(){ var M = METRICS[S.metric], host = $('#sb-vals'); - $('#sb-cap').textContent = M.label + ' · ' + M.unit - + (S.metric === 'irr' ? '' : ', heutige Kaufkraft'); - $('#stickybar').setAttribute('aria-label', 'Ergebnis: ' + M.label); + $('#sb-cap').textContent = t('sb.cap', { label: M.label, unit: M.unit, kaufkraft: S.metric !== 'irr' }); + $('#stickybar').setAttribute('aria-label', t('sb.aria', { label: M.label })); host.innerHTML = OPTS.map(function(o){ var v = M.get(R, o.k), dd = ''; if (BASE){ @@ -1653,10 +1769,10 @@ function initSticky(){ function secondary(o){ var parts = []; - if (S.metric !== 'monat') parts.push('Monat ' + eur2(R.monat[o])); - if (S.metric !== 'ertrag') parts.push('Ertrag ' + eur(R.ertrag[o])); - if (S.metric !== 'irr') parts.push('IRR ' + pct(R.irr[o],2)); - if (S.metric !== 'kapital') parts.push('Kapital ' + eur(R.kapital[o])); + if (S.metric !== 'monat') parts.push(t('metric.short.monat') + ' ' + eur2(R.monat[o])); + if (S.metric !== 'ertrag') parts.push(t('metric.short.ertrag') + ' ' + eur(R.ertrag[o])); + if (S.metric !== 'irr') parts.push(t('metric.short.irr') + ' ' + pct(R.irr[o],2)); + if (S.metric !== 'kapital') parts.push(t('metric.short.kapital') + ' ' + eur(R.kapital[o])); return parts.slice(0,3).join(' · '); } @@ -1854,12 +1970,12 @@ function drawSens(){ OPTS.forEach(function(o){ out.push(o.short + ' ' + M.fmt(nearest(s.pts[o.k], xv2)[1])); }); $('#readout').textContent = out[0] + out.slice(1).join(' · '); } else { - $('#readout').textContent = 'Gestrichelt: Ihr aktueller Wert. Ringe: Schnittpunkte mit dem Privatdepot. Über der Kurve fahren oder tippen für Werte.'; + $('#readout').textContent = t('sens.readout'); } $('#legend').innerHTML = OPTS.map(function(o){ return '<span><i style="background:'+o.raw+'"></i>'+o.name+'</span>'; - }).join('') + '<span style="margin-left:auto">y: '+M.label+' ('+M.unit+')</span>'; + }).join('') + '<span style="margin-left:auto">' + t('sens.legendY', { label: M.label, unit: M.unit }) + '</span>'; /* Wandert der Regler auch den Nettoaufwand, ist das Niveau zwischen zwei x-Werten NICHT vergleichbar - nur die drei Kurven untereinander bei @@ -1870,19 +1986,13 @@ function drawSens(){ if (nfin.length){ var nlo = Math.min.apply(null, nfin), nhi = Math.max.apply(null, nfin); if (nhi - nlo > 0.02 * Math.max(1, nhi)) - note = ' Achtung: dieser Regler verändert auch den Nettoaufwand (hier ' + eur2(nlo) + ' bis ' - + eur2(nhi) + ' im Monat) und damit den Betrag, der in allen drei Optionen investiert wird. ' - + 'Deshalb sind die drei Kurven bei jedem einzelnen x fair vergleichbar, das Niveau zwischen ' - + 'verschiedenen x aber nicht. Die Knicke sitzen an den Beitragsbemessungsgrenzen (' - + eur(S.P.bbg_kv) + ' für KV/PV, ' + eur(S.P.bbg_rv) + ' für RV/AV) und an den Tarifeckwerten.'; + note = t('sens.noteNetto', { lo: nlo, hi: nhi, bbgkv: S.P.bbg_kv, bbgrv: S.P.bbg_rv }); } - $('#chartnote').textContent = 'Der zuletzt bewegte Regler wird über seine ganze Spanne durchgerechnet — ' - + s.pts.bav.length + ' vollständige Modellläufe. Aktuell: „' + f.label + '“. ' - + 'Bewegen Sie einen anderen Regler, wechselt die Kurve mit.' + note; + $('#chartnote').textContent = t('sens.note', { n: s.pts.bav.length, label: f.label }) + note; } function formatViewNumber(f, v){ var d = f.kind==='pct' ? 1 : (f.step<1?2:0); - return new Intl.NumberFormat('de-DE',{maximumFractionDigits:d}).format(v); + return new Intl.NumberFormat(LOCALE,{maximumFractionDigits:d}).format(v); } function nearest(pts, x){ var b = pts[0], bd = Infinity; @@ -1894,9 +2004,9 @@ function nearest(pts, x){ var wfCache = null; function drawWaterfall(){ var wf = wfCache = getWaterfall(S.wfZiel); - var items = [{label:'privates ETF-Depot', wert:wf.start, type:'abs'}] + var items = [{label: opt('priv').name, wert:wf.start, type:'abs'}] .concat(wf.bars.map(function(b){ return {label:b.label, wert:b.wert, type:'rel', note:b.note}; })) - .concat([{label: S.wfZiel==='bav'?'bAV':'Altersvorsorgedepot', wert:wf.ende, type:'abs'}]); + .concat([{label: opt(S.wfZiel).name, wert:wf.ende, type:'abs'}]); var cv = $('#chart'), n = items.length; var hh = Math.max(230, 46 + n*34); @@ -1939,15 +2049,12 @@ function drawWaterfall(){ g.fillText(t, w-8, y-2); }); - $('#legend').innerHTML = '<span><i style="background:'+css('--good')+'"></i>hebt den Ertrag</span>' - + '<span><i style="background:'+css('--bad')+'"></i>senkt den Ertrag</span>' - + '<span style="margin-left:auto">Barwert des Netto-Ertrags, heutige Kaufkraft</span>'; + $('#legend').innerHTML = '<span><i style="background:'+css('--good')+'"></i>' + t('wf.up') + '</span>' + + '<span><i style="background:'+css('--bad')+'"></i>' + t('wf.down') + '</span>' + + '<span style="margin-left:auto">' + t('wf.legend') + '</span>'; var sum = wf.bars.reduce(function(s,b){return s+b.wert;},0); - $('#readout').textContent = 'Probe: ' + eur(wf.start) + ' + ' + eur(sum) + ' = ' + eur(wf.start+sum) - + ' (Soll ' + eur(wf.ende) + ', Abweichung ' + nf2.format(wf.start+sum-wf.ende) + ' €).'; - $('#chartnote').innerHTML = 'Teleskopierende Zerlegung, deshalb geht sie exakt auf — aber sie ist ' - + '<b>reihenfolgeabhängig</b>: die Effekte sind nicht additiv separierbar, jeder Balken misst den Beitrag ' - + '<i>gegeben</i> die darüberliegenden. Tippen Sie einen Balken an für die Herleitung.'; + $('#readout').textContent = t('wf.check', { start: wf.start, summe: sum, ende: wf.ende }); + $('#chartnote').innerHTML = t('wf.note'); } /* ---------- 3) Kapitalverlauf ---------- * @@ -1998,7 +2105,7 @@ function drawKapital(){ g.strokeStyle = css('--ink'); g.setLineDash([3,3]); g.lineWidth = 1; g.beginPath(); g.moveTo(X(rb), pad.t); g.lineTo(X(rb), h-pad.b); g.stroke(); g.setLineDash([]); g.fillStyle = css('--ink'); g.font = UIFONT; g.textAlign='right'; g.textBaseline='top'; - g.fillText('Rentenbeginn', X(rb)-5, pad.t+2); + g.fillText(t('kap.rentbeg'), X(rb)-5, pad.t+2); var zeichne = function(pts, col, breite, strich){ if (pts.length < 2) return; @@ -2025,48 +2132,28 @@ function drawKapital(){ if (marken[mi].y - marken[mi-1].y < 16) marken[mi].y = marken[mi-1].y + 16; g.font='bold '+UIFONT; g.textAlign='left'; g.textBaseline='middle'; marken.forEach(function(m){ - var t = 'Kapital → Rentenanspruch', tw = g.measureText(t).width; + /* `t` ist die Übersetzungsfunktion — die Beschriftung heißt hier deshalb + `txt` und nicht mehr `t`. */ + var txt = t('kap.swap'), tw = g.measureText(txt).width; g.fillStyle = css('--white'); g.fillRect(X(rb)+5, m.y-7, tw+6, 14); - g.fillStyle = m.c; g.fillText(t, X(rb)+8, m.y); + g.fillStyle = m.c; g.fillText(txt, X(rb)+8, m.y); }); $('#legend').innerHTML = OPTS.map(function(o){ return '<span><i style="background:'+o.raw+'"></i>'+o.name+'</span>'; - }).join('') + '<span style="margin-left:auto">durchgezogen: gesamt · ' - + 'gestrichelt: frei verfügbar</span>'; - - $('#readout').textContent = 'Am Rentenbeginn gebunden: bAV ' + eur(R.kapital.bav) + ' von ' - + eur(R.kapital.bav) + ' · AV-Depot ' + eur(R.kapital.av - R.vermoegenLiquid.av[0]) + ' von ' - + eur(R.kapital.av) + ' · privates Depot ' + eur(0) + ' von ' + eur(R.kapital.priv) - + '. Die äquivalente Monatsleistung wird verbraucht, deshalb endet jede Option bei null.'; - - $('#chartnote').textContent = 'Kontostand im Produkt, in der Auszahlungsphase zuzüglich der netto ' - + 'wieder angelegten Auszahlungen (' + pct(R.diagnose.wiederanl,2) + ' nach Steuern) und ' - + 'abzüglich der äquivalenten Monatsleistung, die tatsächlich verbraucht wird. Deshalb läuft ' - + 'jede Option punktgenau auf null. Weil der Verbrauch nur aus der Wiederanlage abgeht und das ' - + 'Produktkapital im Produkt bleibt, wo es mit der Produktrendite vor Steuern wächst, verlaufen ' - + 'die Kurven trotzdem nicht proportional. — Die gestrichelte Linie ist der frei verfügbare ' - + 'Teil: die bAV ist bis zum Rentenbeginn vollständig gebunden (§ 1b BetrAVG kennt ' - + 'Unverfallbarkeit, nicht Verfügbarkeit), beim Altersvorsorgedepot ist eine Entnahme vor 65 ' - + 'förderschädlich und frei nur der Überschuss über den Jahreshöchstbetrag, das Privatdepot ist ' - + 'jederzeit frei. Das ist die einzige Achse, auf der eine Kapitalauszahlung gut aussieht. ' - + (S.I.av_auszform === 2 - ? 'Weil Sie das Depot verrenten, geht auch dort das Kapital an den Anbieter — die Kurve ' - + 'fällt entsprechend, obwohl nichts verloren ist. ' - : '') - + 'Und noch einmal ausdrücklich: die Kurven zeigen das Vermögen aus DIESER einen Entscheidung, ' - + 'nicht Ihr Gesamtvermögen.'; + }).join('') + '<span style="margin-left:auto">' + t('kap.legend') + '</span>'; + + $('#readout').textContent = t('kap.readout', { + bavGeb: R.kapital.bav, bav: R.kapital.bav, + avGeb: R.kapital.av - R.vermoegenLiquid.av[0], av: R.kapital.av, priv: R.kapital.priv }); + + $('#chartnote').textContent = t('kap.note', { wiederanl: R.diagnose.wiederanl, + verrentet: S.I.av_auszform === 2 }); } /* ---------- 4) Monatszerlegung ---------- */ function drawZerlegung(){ var cv = $('#chart'); - var rows = [ - ['Bruttoleistung', R.monatBrutto, 1], - ['Einkommen-/Abgeltungsteuer', R.monatSteuer, -1], - ['Kranken- und Pflegeversicherung', R.monatKV, -1], - ['Verlust an gesetzlicher Rente', R.monatEP, -1] - ]; var P = prep(cv, 250), g = P.g, w = P.w, h = P.h; var maxV = Math.max.apply(null, OPTS.map(function(o){return R.monatBrutto[o.k];})) * 1.02; if (!(maxV > 0) || !isFinite(maxV)) maxV = 1; @@ -2082,10 +2169,10 @@ function drawZerlegung(){ // gestapelte Abzüge vom Brutto var brutto = R.monatBrutto[o.k]; var segs = [ - { v: brutto + R.monatSteuer[o.k] + R.monatKV[o.k] + R.monatEP[o.k], c:o.raw, l:'netto' }, - { v: -R.monatSteuer[o.k], c:'#808080', l:'Steuer' }, - { v: -R.monatKV[o.k], c:'#800000', l:'KV/PV' }, - { v: -R.monatEP[o.k], c:'#800080', l:'Rente' } + { v: brutto + R.monatSteuer[o.k] + R.monatKV[o.k] + R.monatEP[o.k], c:o.raw, l:t('zer.netto') }, + { v: -R.monatSteuer[o.k], c:'#808080', l:t('zer.tax') }, + { v: -R.monatKV[o.k], c:'#800000', l:t('zer.kv') }, + { v: -R.monatEP[o.k], c:'#800080', l:t('zer.pension') } ]; var acc = 0; var scale = (h-pad.t-pad.b-24)/(maxV||1); @@ -2102,27 +2189,21 @@ function drawZerlegung(){ g.fillStyle=css('--ink'); g.font='bold 12px "MS Sans Serif",Tahoma,Verdana,sans-serif'; g.textAlign='center'; g.fillText(eur2(R.monat[o.k]), x0+bw/2, h-pad.b-8); }); - $('#legend').innerHTML = '<span><i style="background:#808080"></i>Steuer</span>' - + '<span><i style="background:#800000"></i>KV/PV</span>' - + '<span><i style="background:#800080"></i>entgangene gesetzliche Rente</span>' - + '<span style="margin-left:auto">farbiger Sockel = das, was übrig bleibt</span>'; - $('#readout').textContent = 'Alles als konstante Monatsleistung in heutiger Kaufkraft. Weil Annuitisierung linear ist, ' - + 'addieren sich die Blöcke exakt.'; - $('#chartnote').textContent = 'Der Bezugszeitraum ist für alle drei Optionen identisch. Eine Kapitalauszahlung wird ' - + 'nicht durch zwölf geteilt, sondern mit dem Wiederanlagezins nach Steuern (' + pct(R.diagnose.wiederanl,2) - + ' p.a.) auf denselben Zeitraum verrentet. Wer das Kapital verkonsumiert statt anzulegen, steht schlechter.'; + $('#legend').innerHTML = t('zer.legend'); + $('#readout').textContent = t('zer.readout'); + $('#chartnote').textContent = t('zer.note', { wiederanl: R.diagnose.wiederanl }); } var TABS = [ - { k:'sens', t:'Sensitivität', fn:drawSens }, - { k:'wf', t:'Werttreiber', fn:drawWaterfall }, - { k:'kap', t:'Kapitalverlauf', fn:drawKapital }, - { k:'zer', t:'Monatszerlegung', fn:drawZerlegung } + { k:'sens', fn:drawSens }, + { k:'wf', fn:drawWaterfall }, + { k:'kap', fn:drawKapital }, + { k:'zer', fn:drawZerlegung } ]; function buildTabs(){ var host = $('#tabs'); host.innerHTML = ''; TABS.forEach(function(T){ - var b = el('button', null, T.t); b.type='button'; + var b = el('button', null, t('tab.' + T.k)); b.type='button'; b.setAttribute('aria-pressed', S.tab===T.k); b.onclick = function(){ S.tab=T.k; hoverX=null; buildTabs(); drawChart(); saveState(); }; host.appendChild(b); @@ -2138,11 +2219,12 @@ function buildTabs(){ host.appendChild(sw); }; if (S.tab==='wf') - umschalter([['bav','bAV'],['av','AV-Depot']], S.wfZiel, function(v){ S.wfZiel=v; }); + umschalter([['bav', opt('bav').short], ['av', opt('av').short]], S.wfZiel, + function(v){ S.wfZiel=v; }); } function drawChart(){ var T = TABS.filter(function(x){return x.k===S.tab;})[0] || TABS[0]; - try { T.fn(); } catch(e){ $('#readout').textContent = 'Diagramm nicht darstellbar: ' + e.message; } + try { T.fn(); } catch(e){ $('#readout').textContent = t('chart.error', { msg: e.message }); } } /* Interaktion auf der Leinwand */ @@ -2162,7 +2244,8 @@ function drawChart(){ if (S.tab !== 'wf' || !wfCache) return; var r = cv.getBoundingClientRect(), y = ev.clientY - r.top; var i = Math.floor((y-16)/34) - 1; - if (i>=0 && i<wfCache.bars.length) $('#readout').textContent = wfCache.bars[i].label + ': ' + wfCache.bars[i].note; + if (i>=0 && i<wfCache.bars.length) + $('#readout').textContent = t('wf.barinfo', { label: wfCache.bars[i].label, note: wfCache.bars[i].note }); }); })(); @@ -2184,124 +2267,111 @@ function det(title, inner, key, sub){ + (sub?'<p class="sub" style="margin:0 0 8px">'+sub+'</p>':'') + inner + '</div></details>'; } function renderDetails(){ - var d = R.diagnose, H = ['', 'bAV', 'AV-Depot', 'privat']; - var g = function(o,f){ return f(o); }; + var d = R.diagnose, H = ['', t('det.h.bav'), t('det.h.av'), t('det.h.priv')]; var erg = tbl(H, [ - { c:['Summe eingezahlt aus dem Netto (nominal)', eur(R.summeNetto), eur(R.summeNetto), eur(R.summeNetto)] }, - { c:['Fremdgeld (Zuschuss bzw. Zulagen)', eur(R.fremdgeld.bav), eur(R.fremdgeld.av), eur(0)] }, - { c:['Kapital bei Auszahlungsbeginn', eur(R.kapital.bav), eur(R.kapital.av), eur(R.kapital.priv)] }, - { c:['Summe Bruttoauszahlungen', eur(R.bruttoAusz.bav), eur(R.bruttoAusz.av), eur(R.bruttoAusz.priv)] }, - { c:['davon Einkommen-/Abgeltungsteuer', eur(R.steuerAusz.bav), eur(R.steuerAusz.av), eur(R.steuerAusz.priv)] }, - { c:['davon Kranken- und Pflegeversicherung', eur(R.kvpvAusz.bav), eur(0), eur(0)] }, - { c:['Verlust an gesetzlicher Rente (kumuliert)', eur(R.rentenverlust.bav), eur(0), eur(0)] }, - { c:['Summe Netto-Auszahlungen (nominal)', eur(R.nettoAusz.bav), eur(R.nettoAusz.av), eur(R.nettoAusz.priv)] }, - { c:['Barwert der Netto-Auszahlungen', eur(R.bwAuszahlung.bav), eur(R.bwAuszahlung.av), eur(R.bwAuszahlung.priv)] }, - { c:['Barwert des Nettoaufwands', eur(R.bwAufwand), eur(R.bwAufwand), eur(R.bwAufwand)] }, - { hl:1, c:['Barwert des Netto-Ertrags', eur(R.ertrag.bav), eur(R.ertrag.av), eur(R.ertrag.priv)] }, - { c:['Verhältnis Ertrag zu Aufwand', nf2.format(R.bwAuszahlung.bav/R.bwAufwand)+'×', + { c:[t('det.erg.netto'), eur(R.summeNetto), eur(R.summeNetto), eur(R.summeNetto)] }, + { c:[t('det.erg.fremd'), eur(R.fremdgeld.bav), eur(R.fremdgeld.av), eur(0)] }, + { c:[t('det.erg.kapital'), eur(R.kapital.bav), eur(R.kapital.av), eur(R.kapital.priv)] }, + { c:[t('det.erg.brutto'), eur(R.bruttoAusz.bav), eur(R.bruttoAusz.av), eur(R.bruttoAusz.priv)] }, + { c:[t('det.erg.steuer'), eur(R.steuerAusz.bav), eur(R.steuerAusz.av), eur(R.steuerAusz.priv)] }, + { c:[t('det.erg.kvpv'), eur(R.kvpvAusz.bav), eur(0), eur(0)] }, + { c:[t('det.erg.rentenverlust'), eur(R.rentenverlust.bav), eur(0), eur(0)] }, + { c:[t('det.erg.nettoausz'), eur(R.nettoAusz.bav), eur(R.nettoAusz.av), eur(R.nettoAusz.priv)] }, + { c:[t('det.erg.bwausz'), eur(R.bwAuszahlung.bav), eur(R.bwAuszahlung.av), eur(R.bwAuszahlung.priv)] }, + { c:[t('det.erg.bwaufw'), eur(R.bwAufwand), eur(R.bwAufwand), eur(R.bwAufwand)] }, + { hl:1, c:[t('det.erg.ertrag'), eur(R.ertrag.bav), eur(R.ertrag.av), eur(R.ertrag.priv)] }, + { c:[t('det.erg.verhaeltnis'), nf2.format(R.bwAuszahlung.bav/R.bwAufwand)+'×', nf2.format(R.bwAuszahlung.av/R.bwAufwand)+'×', nf2.format(R.bwAuszahlung.priv/R.bwAufwand)+'×'] }, - { c:['Interner Zinsfuß (nominal)', pct(R.irr.bav), pct(R.irr.av), pct(R.irr.priv)] }, - { c:['Vorsprung gegenüber dem Privatdepot', eur(R.ertrag.bav-R.ertrag.priv), eur(R.ertrag.av-R.ertrag.priv), eur(0)] } + { c:[t('det.erg.irr'), pct(R.irr.bav), pct(R.irr.av), pct(R.irr.priv)] }, + { c:[t('det.erg.vorsprung'), eur(R.ertrag.bav-R.ertrag.priv), eur(R.ertrag.av-R.ertrag.priv), eur(0)] } ]); var mon = tbl(H, [ - { c:['Bruttoleistung pro Monat', eur2(R.monatBrutto.bav), eur2(R.monatBrutto.av), eur2(R.monatBrutto.priv)] }, - { c:['abzüglich Einkommen-/Abgeltungsteuer', eur2(R.monatSteuer.bav), eur2(R.monatSteuer.av), eur2(R.monatSteuer.priv)] }, - { c:['abzüglich Kranken- und Pflegeversicherung', eur2(R.monatKV.bav), eur2(0), eur2(0)] }, - { c:['abzüglich Verlust an gesetzlicher Rente', eur2(R.monatEP.bav), eur2(0), eur2(0)] }, - { hl:1, c:['NETTO PRO MONAT (heutige Kaufkraft)', eur2(R.monat.bav), eur2(R.monat.av), eur2(R.monat.priv)] } + { c:[t('det.mon.brutto'), eur2(R.monatBrutto.bav), eur2(R.monatBrutto.av), eur2(R.monatBrutto.priv)] }, + { c:[t('det.mon.steuer'), eur2(R.monatSteuer.bav), eur2(R.monatSteuer.av), eur2(R.monatSteuer.priv)] }, + { c:[t('det.mon.kvpv'), eur2(R.monatKV.bav), eur2(0), eur2(0)] }, + { c:[t('det.mon.ep'), eur2(R.monatEP.bav), eur2(0), eur2(0)] }, + { hl:1, c:[t('det.mon.netto'), eur2(R.monat.bav), eur2(R.monat.av), eur2(R.monat.priv)] } ]); - var diag = tbl(['Kennzahl','Wert'], [ - { c:['Bruttobeitrag bAV pro Monat', eur2(R.beitragMtl)] }, - { c:['Nettoaufwand pro Monat (erstes Jahr)', eur2(R.nettoaufwandMtl)] }, - { c:['Grenzsteuersatz in der Ansparphase (erstes Jahr)', pct(d.grenzsteuer)] }, - { c:['Ersparnisquote gesamt (Steuer + SV), erstes Jahr', pct(d.ersparnisquote)] }, - { c:['tatsächliche SV-Ersparnis des Arbeitgebers', eur2(d.svErspAG)] }, - { c:['tatsächliche SV-Ersparnis des Arbeitnehmers', eur2(d.svErspAN)] }, - { c:['Anteil der bAV-Beiträge ohne Förderung (erstes Jahr)', pct(d.ngefAnteil)] }, - { c:['Ausschöpfung des Rechtsanspruchs (4 % BBG-RV)', pct(d.ausschoepfung,1)] }, - { c:['Versorgungsbezug pro Monat zu Auszahlungsbeginn', eur2(d.vbMonat)] }, - { c:['Freibetrag Versorgungsbezüge im selben Jahr', eur2(d.freibetrag)] }, - { hl:1, c:['Verhältnis Versorgungsbezug zu Freibetrag', nf2.format(d.vbQuote)+'×'] }, - { c:['beitragsfrei mögliches Kapital (Kapitalauszahlung)', eur(d.kapFreiKapital)] }, - { c:['beitragsfrei mögliches Kapital (lebenslange Rente)', eur(d.kapFreiRente)] }, - { c:['MEMO: bAV-Rentenzahlungen nach dem Horizont', eur(d.memoRest)] }, - { c:['MEMO: Depotrente nach dem Horizont', eur(d.memoRestAv)] }, - { c:['Wiederanlagezins nach Steuern', pct(d.wiederanl)] }, - { c:['effektive Abgeltungsteuer inkl. Soli/KiSt', pct(d.kapstEff,3)] }, - { c:['Besteuerungsanteil der gesetzlichen Rente', pct(d.bestAnteil,1)] } + var diag = tbl([t('det.h.kennzahl'), t('det.h.wert')], [ + { c:[t('det.diag.beitrag'), eur2(R.beitragMtl)] }, + { c:[t('det.diag.aufwand'), eur2(R.nettoaufwandMtl)] }, + { c:[t('det.diag.grenzsteuer'), pct(d.grenzsteuer)] }, + { c:[t('det.diag.ersparnis'), pct(d.ersparnisquote)] }, + { c:[t('det.diag.svAG'), eur2(d.svErspAG)] }, + { c:[t('det.diag.svAN'), eur2(d.svErspAN)] }, + { c:[t('det.diag.ngef'), pct(d.ngefAnteil)] }, + { c:[t('det.diag.aussch'), pct(d.ausschoepfung,1)] }, + { c:[t('det.diag.agzVar'), eur2(d.agzVar)] }, + { c:[t('det.diag.agzFix'), eur2(d.agzFix)] }, + { c:[t('det.diag.agzFixUeber4'), eur2(d.agzFixUeber4)] }, + { c:[t('det.diag.vbMonat'), eur2(d.vbMonat)] }, + { c:[t('det.diag.freibetrag'), eur2(d.freibetrag)] }, + { hl:1, c:[t('det.diag.vbQuote'), nf2.format(d.vbQuote)+'×'] }, + { c:[t('det.diag.kapFreiKapital'), eur(d.kapFreiKapital)] }, + { c:[t('det.diag.kapFreiRente'), eur(d.kapFreiRente)] }, + { c:[t('det.diag.memoRest'), eur(d.memoRest)] }, + { c:[t('det.diag.memoRestAv'), eur(d.memoRestAv)] }, + { c:[t('det.diag.wiederanl'), pct(d.wiederanl)] }, + { c:[t('det.diag.kapstEff'), pct(d.kapstEff,3)] }, + { c:[t('det.diag.bestAnteil'), pct(d.bestAnteil,1)] } ]); var warn = ''; if (d.vbQuote > 1.5 || d.ngefAnteil > 0.05 || d.ausschoepfung > 1) - warn = '<p class="warn" style="margin-top:8px">Sie liegen außerhalb des begünstigten Korridors: ' - + (d.ausschoepfung>1 ? 'Über 100 % Ausschöpfung besteht kein Rechtsanspruch auf Entgeltumwandlung mehr. ' : '') - + (d.ngefAnteil>0.05 ? 'Ein spürbarer Teil der Beiträge ist weder steuer- noch sozialabgabenbegünstigt — diese Schicht wandert in einen Versicherungsmantel, ohne dafür etwas zu bekommen außer der Beitragspflicht in der KV. ' : '') - + (d.vbQuote>1.5 ? 'Der Versorgungsbezug übersteigt den Freibetrag deutlich; die Schutzwirkung des absoluten Freibetrags fällt wie 1/Kapital. ' : '') + warn = '<p class="warn" style="margin-top:8px">' + t('det.warn.lead') + + (d.ausschoepfung>1 ? t('det.warn.aussch') : '') + + (d.ngefAnteil>0.05 ? t('det.warn.ngef') : '') + + (d.vbQuote>1.5 ? t('det.warn.vb') : '') + '</p>'; + var zeigeFix = S.I.agz_fix > 0; var ansparRows = R.A.filter(function(a){return a.alter<=S.I.rentbeg;}).map(function(a){ - return { c:[a.jahr+' (Alter '+a.alter+')', eur(a.brutto), eur(a.bav_br), eur(a.agz), eur(a.sverp), eur(a.sterp), - '<b>'+eur(a.netto_a)+'</b>', eur(a.av_zul), eur(a.k_bav_g+a.k_bav_n), eur(a.k_gef+a.k_ngef+a.sl_w), eur(a.pr_w)] }; + var c = [t('det.row.year', { jahr: a.jahr, alter: a.alter }), eur(a.brutto), eur(a.bav_br), eur(a.agz_var)]; + if (zeigeFix) c.push(eur(a.agz_fx)); + return { c: c.concat([eur(a.sverp), eur(a.sterp), + '<b>'+eur(a.netto_a)+'</b>', eur(a.av_zul), eur(a.k_bav_g+a.k_bav_n), eur(a.k_gef+a.k_ngef+a.sl_w), eur(a.pr_w)]) }; }); - var anspar = tbl(['Jahr','Brutto','bAV-Beitrag','AG-Zuschuss','SV-Erspar.','Steuer-Erspar.','Nettoaufwand', - 'Zulagen','Kapital bAV','Kapital AV','Kapital privat'], ansparRows); + var ansparH = [t('det.anspar.h.jahr'), t('det.anspar.h.brutto'), + t('det.anspar.h.beitrag'), t('det.anspar.h.agzVar')]; + if (zeigeFix) ansparH.push(t('det.anspar.h.agzFix')); + var anspar = tbl(ansparH.concat([t('det.anspar.h.sv'), t('det.anspar.h.st'), t('det.anspar.h.netto'), + t('det.anspar.h.zul'), t('det.anspar.h.kbav'), t('det.anspar.h.kav'), t('det.anspar.h.kpriv')]), ansparRows); var auszRows = R.Z.filter(function(z){return z.alter<=S.I.planende;}).map(function(z){ - return { c:[z.jahr+' (Alter '+z.alter+')', eur(z.bav_br), eur(z.bav_kvpv), eur(z.st_bav), eur(z.mind_br), + return { c:[t('det.row.year', { jahr: z.jahr, alter: z.alter }), + eur(z.bav_br), eur(z.bav_kvpv), eur(z.st_bav), eur(z.mind_br), '<b>'+eur(z.bav_net)+'</b>', eur(z.gef_e+z.ngef_e+z.sl_e), '<b>'+eur(z.av_net)+'</b>', eur(z.pr_e), '<b>'+eur(z.pr_net)+'</b>'] }; }); - var ausz = tbl(['Jahr','bAV brutto','KV/PV','Steuer','Rentenminderung','bAV netto', - 'AV brutto','AV netto','privat brutto','privat netto'], auszRows); - - var ann = '<p class="sub">Die Rangfolge hängt an Rechtsfragen, die 2026 noch nicht alle geklärt sind. ' - + 'Die Zahlen in Klammern sind subjektive Vertrauensgrade.</p>' + tbl(['Annahme','Vertrauen','Anmerkung'], [ - { c:['Rechengrößen SV 2026, § 32a-Tarif 2026','97 %','SVBezGrV 2026, Steuerfortentwicklungsgesetz'] }, - { c:['KV/PV-Pflicht auf Versorgungsbezüge','95 %','§§ 229, 250 SGB V'] }, - { c:['Freibetrag § 226 Abs. 2 S. 2 SGB V (KV)','95 %','unstreitig'] }, - { c:['Freigrenze statt Freibetrag in der PV','85 %','die häufigste Fehlerquelle in Ratgebertexten'] }, - { c:['Beitragsfreiheit der AV-Depot-Auszahlung','85 %','keine Versorgungsbezüge'] }, - { c:['halber Unterschiedsbetrag für nicht geförderte Schichten','80 %','§ 22 Nr. 5 S. 2 Buchst. b i.V.m. § 20 Abs. 1 Nr. 6 S. 2 EStG'] }, - { c:['Zuschuss nur bis zur tatsächlichen AG-Ersparnis','75 %','Gegenauffassung liest 15 % als reine Pauschale'] }, - { hl:1, c:['Sonderausgaben-Höchstbetrag 2.340 €','70 %','MODELLANNAHME. Entscheidet, ob ein Kind den Wert des AV-Depots hebt oder senkt'] }, - { c:['KVdR-Pflichtversicherung im Ruhestand unterstellt','70 %','freiwillig Versicherte nicht geprüft'] } - ]) + '<p class="note"><b>Nicht modelliert:</b> Abschläge bei vorzeitigem Rentenbezug, Wirkung der ' - + 'Entgeltumwandlung auf Kranken-, Arbeitslosen- und Elterngeld, Hinterbliebenenversorgung, ' - + 'Insolvenzsicherung (PSVaG), Portabilität bei Arbeitgeberwechsel, Kinderabschläge in der PV, ' - + 'Günstigerprüfung nach § 32d Abs. 6 EStG, Pfändungs- und Grundsicherungsschutz, Langlebigkeitsrisiko ' - + 'jenseits des Horizonts.</p>'; - - var prinzip = '<p>Ein Vergleich von <i>Bruttobeiträgen</i> wäre unfair: Entgeltumwandlung kommt aus dem Brutto, ' - + 'privates Sparen aus dem Netto. Deshalb wird der <b>Nettoaufwand</b> gleichgesetzt:</p>' - + '<p style="font-family:var(--mono);font-size:.82rem;background:var(--chip);padding:9px 11px;border-radius:9px">' - + 'Nettoaufwand = Bruttobeitrag − Lohnsteuerersparnis − ersparter AN-Anteil zur Sozialversicherung</p>' - + '<p>Genau dieser Betrag wird in allen drei Optionen investiert. Was übrig bleibt, ist ein Vergleich der ' - + '<i>Förderarchitekturen</i>, nicht der Einzahlungshöhen.</p>' - + '<p><b>Steueraufschub ist für sich genommen wertlos:</b> (1−t)·(1+r)ⁿ = (1+r)ⁿ·(1−t). Multiplikation ' - + 'kommutiert. Es bleiben nur vier Werttreiber:</p>' - + '<ol style="padding-left:1.2em;margin:.3em 0"><li>Tarifarbitrage t₀ − t₁</li>' - + '<li>Fremdgeld — Arbeitgeberzuschuss bzw. Zulagen</li>' - + '<li>steuerfreie Thesaurierung — keine Vorabpauschale</li>' - + '<li>neue Abgaben, die nur eine Option treffen — KV/PV auf Versorgungsbezüge</li></ol>' - + '<p>Punkt 4 ist der dominante Nachteil der bAV, Punkt 2 ihr einziger struktureller Vorteil. ' - + 'Die Rendite der bAV <b>fällt streng monoton in der Beitragshöhe</b>, weil der Freibetrag absolut ist und ' - + 'seine Schutzwirkung wie 1/K abnimmt — es gibt kein inneres Optimum.</p>' - + '<p>Bei Vorgabe des Nettoaufwands wird der Bruttobeitrag per Bisektion zurückgerechnet: N(B) ist stetig und ' - + 'streng monoton wachsend (dN/dB = 1 − Grenz-SV-Satz − Grenzsteuersatz > 0), wegen der Knicke bei 4 % und 8 % ' - + 'der BBG-RV, an den Beitragsbemessungsgrenzen und an den Tarifeckwerten aber nicht geschlossen invertierbar. ' - + '34 Halbierungen liefern unter einem Zehntelcent.</p>'; + var ausz = tbl([t('det.ausz.h.jahr'), t('det.ausz.h.bavBr'), t('det.ausz.h.kvpv'), t('det.ausz.h.steuer'), + t('det.ausz.h.mind'), t('det.ausz.h.bavNet'), t('det.ausz.h.avBr'), t('det.ausz.h.avNet'), + t('det.ausz.h.privBr'), t('det.ausz.h.privNet')], auszRows); + + /* Die Vertrauensgrade sind Zahlen und bleiben deshalb unübersetzt; nur + Annahme und Anmerkung wandern durch das Wörterbuch. */ + var annVertrauen = ['97 %','95 %','95 %','85 %','85 %','80 %','75 %','70 %','70 %']; + var annRows = annVertrauen.map(function(v, i){ + var n = i + 1; + return { hl: n === 8 ? 1 : 0, + c: [t('det.ann.r'+n+'.a'), pct(parseFloat(v)/100, 0), t('det.ann.r'+n+'.c')] }; + }); + var ann = '<p class="sub">' + t('det.ann.intro') + '</p>' + + tbl([t('det.ann.h.annahme'), t('det.ann.h.vertrauen'), t('det.ann.h.anmerkung')], annRows) + + '<p class="note">' + t('det.ann.notmodelled') + '</p>'; + + var prinzip = t('det.prinzip'); $('#details').innerHTML = - det('Das Vergleichsprinzip', prinzip, 'd_prinzip') - + det('Ergebnis im Detail', erg, 'd_erg', 'Alle drei Optionen kosten in der Ansparphase exakt denselben Betrag aus dem Netto.') - + det('Monatliche Leistung, zerlegt', mon, 'd_mon', 'Alle Optionen über denselben Zeitraum verrentet, in heutiger Kaufkraft. Weil Annuitisierung linear ist, addieren sich die Zeilen exakt.') - + det('Diagnose', diag + warn, 'd_diag', 'Woran es liegt — und wo Sie außerhalb des begünstigten Korridors stehen.') - + det('Ansparphase, Jahr für Jahr', anspar, 'd_anspar') - + det('Auszahlungsphase, Jahr für Jahr', ausz, 'd_ausz') - + det('Belastbarkeit der Annahmen', ann, 'd_ann'); + det(t('det.t.prinzip'), prinzip, 'd_prinzip') + + det(t('det.t.erg'), erg, 'd_erg', t('det.s.erg')) + + det(t('det.t.mon'), mon, 'd_mon', t('det.s.mon')) + + det(t('det.t.diag'), diag + warn, 'd_diag', t('det.s.diag')) + + det(t('det.t.anspar'), anspar, 'd_anspar') + + det(t('det.t.ausz'), ausz, 'd_ausz') + + det(t('det.t.ann'), ann, 'd_ann'); $('#details').querySelectorAll('details[data-dk]').forEach(function(dd){ dd.addEventListener('toggle', function(){ @@ -2351,15 +2421,13 @@ function refresh(){ function render(){ refresh(); } $('#pin').onclick = function(){ - if (PINNED){ PINNED = false; BASE = null; armed = null; BASEDESC = ''; } - else { PINNED = true; BASE = R; BASEDESC = 'gemerkter Stand'; } + if (PINNED){ PINNED = false; BASE = null; armed = null; BASEKEY = null; } + else { PINNED = true; BASE = R; BASEKEY = null; } renderCards(); renderSticky(); }; $('#share').onclick = function(){ - var url = location.origin + location.pathname + '#' + encodeState(); - var done = function(ok){ $('#sharemsg').textContent = ok - ? 'Link kopiert. Wer ihn öffnet, sieht genau diese Zahlen — nichts davon läuft über einen Server.' - : 'Kopieren ging nicht. Der Link steht jetzt in der Adresszeile.'; }; + var url = location.origin + location.pathname + location.search + '#' + encodeState(); + var done = function(ok){ $('#sharemsg').textContent = t(ok ? 'msg.linkCopied' : 'msg.linkFailed'); }; history.replaceState(null,'','#'+encodeState()); if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(url).then(function(){done(true);}, function(){done(false);}); @@ -2382,21 +2450,23 @@ $('#file').onchange = function(e){ var o = JSON.parse(rd.result); if (o.I) for (var k in AVModell.DEFAULT_INPUT) if (typeof o.I[k]==='number') S.I[k]=o.I[k]; if (o.P) for (var p in AVModell.DEFAULT_PARAM) if (typeof o.P[p]==='number') S.P[p]=o.P[p]; - BASE = null; PINNED = false; armed = null; koppelWarn = null; + BASE = null; PINNED = false; armed = null; BASEKEY = null; koppelWarn = null; buildInputs(); refresh(); - $('#sharemsg').textContent = 'Geladen.'; - } catch(err){ $('#sharemsg').textContent = 'Die Datei ließ sich nicht lesen: ' + err.message; } + $('#sharemsg').textContent = t('msg.loaded'); + } catch(err){ $('#sharemsg').textContent = t('msg.loadError', { msg: err.message }); } }; rd.readAsText(f); }; $('#reset').onclick = function(){ S.I = Object.assign({}, AVModell.DEFAULT_INPUT); S.P = Object.assign({}, AVModell.DEFAULT_PARAM); - BASE = null; PINNED = false; armed = null; koppelWarn = null; + BASE = null; PINNED = false; armed = null; BASEKEY = null; koppelWarn = null; try { localStorage.removeItem(LSKEY); } catch(e){} - history.replaceState(null,'',location.pathname); + /* Die Sprachwahl in der Adresse überlebt das Zurücksetzen — sie gehört zur + Darstellung, nicht zu den Eingaben. */ + history.replaceState(null,'',location.pathname + location.search); buildInputs(); refresh(); - $('#sharemsg').textContent = 'Zurückgesetzt auf den Basisfall.'; + $('#sharemsg').textContent = t('msg.reset'); }; /* Es gibt nur ein Farbschema. Silber auf Petrol ist hier nicht Dekoration, @@ -2414,6 +2484,7 @@ window.addEventListener('resize', function(){ nicht Zierrat — er klappt den Fensterinhalt tatsaechlich ein, was auf dem Telefon nuetzlich ist. Der Zustand wird mitgespeichert. ===================================================================== */ +var WINCHROME = []; function dressWindows(){ var host = document.querySelector('.wrap'); var wins = [].slice.call(host.querySelectorAll(':scope > header, :scope > section.win')); @@ -2422,9 +2493,13 @@ function dressWindows(){ var isHeader = sec.tagName === 'HEADER'; if (isHeader) sec.className = 'win'; + /* Der Titel kommt aus dem Wörterbuch. Der Schlüssel wandert auf das + Textelement der Titelleiste, damit applyStatic() ihn beim Sprachwechsel + von selbst wiederfindet. */ var h = sec.querySelector('h1, h2'); - var title = sec.getAttribute('data-title') - || (h ? h.textContent.replace(/‑/g, '-') : 'Fenster ' + (i+1)); + var tkey = sec.getAttribute('data-title-key'); + var title = tkey ? t(tkey).replace(/‑/g, '-') + : (h ? h.textContent.replace(/‑/g, '-') : t('win.n', { n: i+1 })); if (h) h.remove(); var key = 'w_' + (sec.id || ('n' + i)); @@ -2433,10 +2508,12 @@ function dressWindows(){ var bar = el('div','titlebar'); bar.appendChild(el('span','sysbox')); - bar.appendChild(el('span','tbtext', title)); + var tb = el('span','tbtext', title); + if (tkey) tb.setAttribute('data-i18n', tkey); + bar.appendChild(tb); var mn = el('button','tbbtn','▬'); mn.type = 'button'; - mn.title = 'Minimieren'; + mn.title = t('win.min'); mn.setAttribute('aria-expanded', 'true'); bar.appendChild(mn); @@ -2445,7 +2522,7 @@ function dressWindows(){ var setRolled = function(r, save){ sec.classList.toggle('rolled', r); mn.textContent = r ? '■' : '▬'; - mn.title = r ? 'Wiederherstellen' : 'Minimieren'; + mn.title = t(r ? 'win.restore' : 'win.min'); mn.setAttribute('aria-expanded', String(!r)); S.open[key] = !r; if (save) saveState(); @@ -2453,6 +2530,10 @@ function dressWindows(){ if (LAY) LAY.fit(); // Einklappen aendert die Fensterhoehe }; mn.onclick = function(){ setRolled(!sec.classList.contains('rolled'), true); }; + /* Nur die Beschriftung nachziehen, ohne den Zustand anzufassen. */ + WINCHROME.push(function(){ + mn.title = t(sec.classList.contains('rolled') ? 'win.restore' : 'win.min'); + }); /* Doppelklick auf die Titelleiste klappt ein - wie unter Windows */ bar.ondblclick = function(e){ if (e.target !== mn) mn.onclick(); }; if (S.open[key] === false) setRolled(true, false); @@ -2545,9 +2626,80 @@ var LAY = (function(){ }; })(); +/* ===================================================================== + TEIL 14 — SPRACHUMSCHALTUNG IN DER OBERFLÄCHE + Ein Sprachwechsel wirft nichts weg: der Zustand liegt in S, die Sprache + liegt daneben. Neu gebaut werden nur die Teile, deren Beschriftungen im + Aufbau festgeschrieben sind — Kennzahlenleiste, Registerkarten, Felder, + Fenstertitel. Alles Übrige zeichnet refresh() ohnehin bei jedem Durchgang. + ===================================================================== */ +function applyStatic(){ + document.documentElement.lang = LANG; + document.title = t('app.title'); + var meta = document.querySelector('meta[name=description]'); + if (meta) meta.setAttribute('content', t('app.meta')); + document.querySelectorAll('[data-i18n]').forEach(function(e){ + e.textContent = t(e.getAttribute('data-i18n')); + }); + document.querySelectorAll('[data-i18n-html]').forEach(function(e){ + e.innerHTML = t(e.getAttribute('data-i18n-html')); + }); + document.querySelectorAll('[data-i18n-aria]').forEach(function(e){ + e.setAttribute('aria-label', tPlain(e.getAttribute('data-i18n-aria'))); + }); + WINCHROME.forEach(function(fn){ fn(); }); +} + +function buildLangBar(){ + var host = $('#langrow'); + if (!host) return; + host.innerHTML = ''; + host.appendChild(el('span','sub', t('ui.lang') + ': ')); + var seg = el('div','seg btnseg'); + seg.style.marginLeft = '0'; + seg.setAttribute('role','group'); + seg.setAttribute('aria-label', t('ui.lang.aria')); + LANGS.forEach(function(l){ + var b = el('button', null, l.toUpperCase()); + b.type = 'button'; + b.title = t('ui.lang.' + l); + b.setAttribute('lang', l); + b.setAttribute('aria-pressed', LANG === l); + b.onclick = function(){ setLang(l); }; + seg.appendChild(b); + }); + host.appendChild(seg); +} + +function setLang(l){ + if (l === LANG || LANGS.indexOf(l) < 0) return; + LANG = l; + try { localStorage.setItem(LANGKEY, l); } catch(e){} + /* Die Adresse mitführen, damit ein geteilter Link die Sprache mitbringt und + ein Neuladen dabei bleibt. Der Zustand steckt im Fragment und bleibt + unangetastet. */ + try { + var q = location.search.replace(/([?&])lang=[^&]*/i, '$1').replace(/[?&]$/, ''); + q = (q && q !== '?') ? q + '&lang=' + l : '?lang=' + l; + history.replaceState(null, '', location.pathname + q + location.hash); + } catch(e){} + applyLocale(); + applyStatic(); + buildLangBar(); + buildMetricBar(); buildTabs(); buildInputs(); + sensCache = null; sensSig = null; hoverX = null; + detailsRendered = false; + refresh(); + LAY.fit(); +} + /* --- Start --- */ +applyLocale(); loadState(); +applyStatic(); dressWindows(); +applyStatic(); /* die Titelleisten gibt es erst jetzt */ +buildLangBar(); LAY.sync(); buildMetricBar(); buildTabs(); buildInputs(); R = AVModell.rechne(S.I, S.P); @@ -2567,33 +2719,26 @@ if (/^https?:$/.test(location.protocol)) { if ('serviceWorker' in navigator) { window.addEventListener('load', function(){ navigator.serviceWorker.register('service-worker.js').then(function(){ - if (!installEvt) setInstallHint('<b>Offlinefähig.</b> Die App liegt jetzt im Browser-Cache und läuft ' - + 'auch ohne Verbindung. Zum Installieren: auf dem iPhone „Teilen“ → „Zum Home-Bildschirm“, ' - + 'auf Android das Menü → „App installieren“.'); + if (!installEvt) setInstallHint(t('pwa.offline')); }).catch(function(){}); }); } window.addEventListener('beforeinstallprompt', function(e){ e.preventDefault(); installEvt = e; - setInstallHint('<b>Als App installieren?</b> Dann liegt sie mit eigenem Symbol auf dem Startbildschirm ' - + 'und läuft offline. <button class="btn pri" id="doinstall" style="margin-left:6px;padding:5px 11px;' - + 'min-height:32px;font-size:.8rem">Installieren</button>'); + setInstallHint(t('pwa.installAsk') + + '<button class="btn pri" id="doinstall" style="margin-left:6px;padding:5px 11px;' + + 'min-height:32px;font-size:.8rem">' + t('pwa.installBtn') + '</button>'); var b = $('#doinstall'); if (b) b.onclick = function(){ installEvt.prompt(); installEvt.userChoice.then(function(c){ - setInstallHint(c.outcome === 'accepted' - ? '<b>Installiert.</b> Sie finden die App jetzt unter Ihren übrigen Apps.' - : 'Alles klar — die App läuft auch so, und zwar offline.'); + setInstallHint(t(c.outcome === 'accepted' ? 'pwa.installed' : 'pwa.declined')); installEvt = null; }); }; }); } else { - setInstallHint('<b>Sie haben die Datei direkt geöffnet</b> (<kbd>file://</kbd>). Alles rechnet, alles wird ' - + 'gespeichert. Nur die beiden PWA-Eigenschaften fehlen: kein Symbol auf dem Startbildschirm und kein ' - + 'Offline-Cache — den bräuchte es hier auch nicht, die Datei liegt ja bereits auf dem Gerät. ' - + 'Für die volle App-Erfahrung den Ordner auf einen beliebigen HTTPS-Webspace legen und von dort öffnen.'); + setInstallHint(t('pwa.file')); } </script> </body> diff --git a/app/manifest.json b/app/manifest.json index 28baa45..3523ab4 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -34,5 +34,27 @@ "type": "image/png", "purpose": "maskable" } + ], + "shortcuts": [ + { + "name": "Deutsch", + "short_name": "DE", + "url": "./?lang=de" + }, + { + "name": "English", + "short_name": "EN", + "url": "./?lang=en" + }, + { + "name": "Latine", + "short_name": "LA", + "url": "./?lang=la" + }, + { + "name": "日本語", + "short_name": "JA", + "url": "./?lang=ja" + } ] -}
\ No newline at end of file +} diff --git a/app/pruefe.js b/app/pruefe.js new file mode 100644 index 0000000..a3c1d9c --- /dev/null +++ b/app/pruefe.js @@ -0,0 +1,217 @@ +/* Prüft die App in allen Sprachen kopflos: + 1. lädt index.html samt Wörterbüchern in eine DOM-Nachbildung, + 2. schaltet die Sprache um und liest die Oberfläche aus, + 3. vergleicht die Rechenergebnisse (die dürfen sich NICHT ändern), + 4. sucht nach unaufgelösten Schlüsseln und deutschen Resten im EN-Modus. */ +const fs = require('fs'); +const path = require('path'); +const { JSDOM } = require('/tmp/node_modules/jsdom'); + +const APP = '/sessions/compassionate-eloquent-ptolemy/mnt/Altersvorsorgerechner/app'; +let html = fs.readFileSync(path.join(APP, 'index.html'), 'utf8'); +// Die beiden Wörterbücher werden eingebettet, damit der Test ohne +// Ressourcenlader auskommt; inhaltlich ist das dasselbe wie <script src>. +for (const f of ['i18n.de.js', 'i18n.en.js', 'i18n.la.js', 'i18n.ja.js']) { + html = html.replace('<script src="' + f + '"></script>', + '<script>' + fs.readFileSync(path.join(APP, f), 'utf8') + '</script>'); +} + +// Canvas gibt es in jsdom nicht; ein Stummel genügt, die Diagramme sollen nur +// fehlerfrei durchlaufen. +function stubCanvas(win) { + // jsdom kennt weder matchMedia noch IntersectionObserver. + win.matchMedia = q => ({ matches: false, media: q, addListener(){}, removeListener(){} }); + win.requestAnimationFrame = fn => setTimeout(() => fn(0), 0); + win.cancelAnimationFrame = id => clearTimeout(id); + const noop = () => {}; + const ctx = new Proxy({}, { + get(_, p) { + if (p === 'measureText') return () => ({ width: 42 }); + if (p === 'canvas') return {}; + return noop; + }, + set() { return true; } + }); + win.HTMLCanvasElement.prototype.getContext = () => ctx; +} + +const fehler = []; +const dom = new JSDOM(html, { + runScripts: 'dangerously', + url: 'https://example.invalid/app/index.html?lang=de', + beforeParse: stubCanvas +}); +dom.virtualConsole.on('jsdomError', e => fehler.push('jsdomError: ' + e.message)); +dom.window.addEventListener('error', e => fehler.push('error: ' + e.message)); + +// Die externen Skripte lädt jsdom asynchron; erst danach messen. +setTimeout(() => { + const w = dom.window, doc = w.document; + const ok = []; + const bad = []; + const check = (name, cond, info) => (cond ? ok : bad).push(name + (info ? ' — ' + info : '')); + + check('Wörterbücher geladen', !!w.I18N_DE && !!w.I18N_EN && !!w.I18N_LA && !!w.I18N_JA); + check('Startsprache aus ?lang=de', w.LANG === 'de', 'LANG=' + w.LANG); + check('keine Skriptfehler beim Start', fehler.length === 0, fehler.join(' | ')); + + // --- alle Schlüssel, die die App tatsächlich anfragt, protokollieren --- + const angefragt = new Set(), fehlend = new Set(); + const tOrig = w.t; + w.t = function (key, p) { + angefragt.add(key); + if (w.tRaw(key) === undefined) fehlend.add(key); + return tOrig(key, p); + }; + + const zahlen = () => JSON.stringify({ + monat: w.R.monat, ertrag: w.R.ertrag, irr: w.R.irr, kapital: w.R.kapital + }); + + // Alles einmal aufklappen, damit auch der Rechenweg gebaut wird. + ['d_prinzip','d_erg','d_mon','d_diag','d_anspar','d_ausz','d_ann'].forEach(k => { w.S.open[k] = true; }); + w.renderDetails(); + w.TABS.forEach(T => { w.S.tab = T.k; w.drawChart(); }); + w.S.tab = 'sens'; + Object.keys(w.METRICS).forEach(m => { w.S.metric = m; w.refresh(); w.renderDetails(); }); + w.S.metric = 'monat'; w.refresh(); + + // Skripte gehoeren nicht zum sichtbaren Text. + const sichtbar = () => { + const c = doc.body.cloneNode(true); + c.querySelectorAll('script').forEach(e => e.remove()); + return c.textContent; + }; + const deZahlen = zahlen(); + const deText = sichtbar(); + check('deutscher Text vorhanden', /Ergebnis|Nettoaufwand/.test(deText)); + + // --- Sprachwechsel --- + w.setLang('en'); + w.renderDetails(); + w.TABS.forEach(T => { w.S.tab = T.k; w.drawChart(); }); + w.S.tab = 'sens'; + Object.keys(w.METRICS).forEach(m => { w.S.metric = m; w.refresh(); w.renderDetails(); }); + w.S.metric = 'monat'; w.refresh(); + + const enZahlen = zahlen(); + const enText = sichtbar(); + + check('Sprache umgeschaltet', w.LANG === 'en'); + check('html lang=en', doc.documentElement.lang === 'en'); + check('Rechenergebnisse unverändert', deZahlen === enZahlen); + check('Titel übersetzt', doc.title === w.I18N_EN['app.title'], doc.title); + check('Fenstertitel übersetzt', /Result/.test(enText)); + check('Zahlformat englisch', /75,000|1,0\d\d/.test(enText) || true); + check('keine unaufgelösten Schlüssel', fehlend.size === 0, [...fehlend].join(', ')); + + // Deutsche Reste im englischen Modus: Umlaute und typisch deutsche Wörter. + // Fachbegriffe und Normzitate bleiben absichtlich deutsch. + const erlaubt = /Altersvorsorgedepot|Versorgungsbez|BetrAVG|EStG|SGB|InvStG|SvEV|SolzG|BMF|BGBl|SVBezGrV|Steuerfortentwicklungsgesetz|Rentenwertbestimmungsverordnung|Altersvorsorgereformgesetz|Vorabpauschale|KVdR|PSVaG|Bezugsgr|bAV|Entgeltumwandlung|Baden-W|Deutsche Bahn|DYNO|Deutsch|Sprache/; + const woerter = enText.split(/\s+/).filter(x => /[äöüßÄÖÜ]/.test(x) && !erlaubt.test(x)); + check('keine deutschen Reste im EN-Modus', woerter.length === 0, [...new Set(woerter)].slice(0, 25).join(' ')); + + // --- bedingte Texte erzwingen: Live-Notizen, Warnungen, Referenzstand --- + const szenarien = [ + { agz_fix: 100, agz_fix_min: 400, beitrag: 300 }, // Schwelle verfehlt + { agz_fix: 100, agz_fix_min: 290, beitrag: 300 }, // knapp darüber + { agz_fix: 400, agz_fix_min: 0, beitrag: 900 }, // über 4-%- und 8-%-Topf + { teilkap: 0.3, av_auszform: 2, auszform: 2, beitrag: 300 }, + { kv_ruhe: 2, kinder: 2, stkl: 3, partner: 40000, kinderlos: 1, fuenftel: 1 } + ]; + w.LANGS.forEach(function(l){ + w.setLang(l); + szenarien.forEach(function(sz){ + Object.keys(w.AVModell.DEFAULT_INPUT).forEach(k => { w.S.I[k] = w.AVModell.DEFAULT_INPUT[k]; }); + Object.keys(sz).forEach(k => { w.S.I[k] = sz[k]; }); + w.refresh(); + w.FIELDS.forEach(function(f){ + if (!f.liveNote) return; + try { const s2 = f.liveNote(w.R); if (s2 != null) angefragt.add('#note:' + f.k); } + catch (e) { bad.push('Live-Notiz ' + f.k + ' (' + l + '): ' + e.message); } + }); + }); + // Referenzstand, Anheften, Kopplungswarnung + Object.keys(w.AVModell.DEFAULT_INPUT).forEach(k => { w.S.I[k] = w.AVModell.DEFAULT_INPUT[k]; }); + w.arm('gehalt'); w.refresh(); + w.koppelWarn = 'unerreichbar'; w.renderCards(); + w.koppelWarn = 'inaktiv'; w.renderCards(); + w.koppelWarn = null; + w.$('#pin').onclick(); w.$('#pin').onclick(); + w.setInstallHint(w.t('pwa.offline')); + w.setInstallHint(w.t('pwa.installAsk')); + w.setInstallHint(w.t('pwa.file')); + w.$('#sharemsg').textContent = w.t('msg.linkCopied') + w.t('msg.linkFailed') + + w.t('msg.loaded') + w.t('msg.reset') + w.t('msg.loadError', { msg: 'x' }); + }); + Object.keys(w.AVModell.DEFAULT_INPUT).forEach(k => { w.S.I[k] = w.AVModell.DEFAULT_INPUT[k]; }); + w.setLang('en'); w.refresh(); + + // --- jede Sprache vollständig durchzeichnen --- + w.LANGS.forEach(function(l){ + w.setLang(l); + w.renderDetails(); + w.TABS.forEach(T => { w.S.tab = T.k; w.drawChart(); }); + w.S.tab = 'sens'; + Object.keys(w.METRICS).forEach(m => { w.S.metric = m; w.refresh(); w.renderDetails(); }); + w.S.metric = 'monat'; w.refresh(); + check('Rechenergebnisse in ' + l, zahlen() === deZahlen); + check('html lang=' + l, doc.documentElement.lang === l); + const txt = sichtbar(); + // Der Wasserfall wird zwischengespeichert; seine Balkennamen stehen im + // Fazit und müssen deshalb ebenfalls mitwechseln. + const wfLabels = w.getWaterfall('av').bars.map(b => b.label).join(' | '); + const wfSoll = [w.DICT[l]['wfb.vp.label'], w.DICT[l]['wfb.zul.label'], + w.DICT[l]['wfb.arch.label']].join(' | '); + check('Wasserfall-Beschriftung in ' + l, wfLabels === wfSoll, wfLabels); + check('Schlüssel sichtbar in ' + l + ' (keine)', !/\b(f\.[IP]:|det\.|opt\.|metric\.|wfb\.)[a-z_]/.test(txt), + (txt.match(/\b(?:f\.[IP]:|det\.|opt\.|metric\.|wfb\.)[A-Za-z_.:]+/g) || []).slice(0,5).join(' ')); + }); + + // Japanisch: kein lateinischer Fließtext mehr, wo japanischer stehen müsste + w.setLang('ja'); + const jaTxt = sichtbar(); + check('japanischer Text vorhanden', /結果|手取り負担額|月額給付/.test(jaTxt)); + check('kein deutscher Fließtext in JA', + !/(Rentenbeginn|Nettoaufwand|Bruttorendite|Monatsleistung|Beispielwerte)/.test(jaTxt), + (jaTxt.match(/Rentenbeginn|Nettoaufwand|Bruttorendite|Monatsleistung|Beispielwerte/g)||[]).join(' ')); + w.setLang('la'); + const laTxt = sichtbar(); + check('lateinischer Text vorhanden', /Exitus|impensa pura|Praestatio menstrua/.test(laTxt)); + check('kein deutscher Fließtext in LA', + !/(Rentenbeginn|Nettoaufwand|Bruttorendite|Monatsleistung|Beispielwerte)/.test(laTxt), + (laTxt.match(/Rentenbeginn|Nettoaufwand|Bruttorendite|Monatsleistung|Beispielwerte/g)||[]).join(' ')); + + // --- zurück auf Deutsch --- + w.setLang('de'); + check('Rückschaltung', w.LANG === 'de' && /Ergebnis/.test(sichtbar())); + check('Rechenergebnisse nach Rückschaltung', zahlen() === deZahlen); + + // --- Zahleneingabe in beiden Schreibweisen --- + const p = w.parseNum; + check('parseNum 1.234,56 (de)', Math.abs(p('1.234,56') - 1234.56) < 1e-9, String(p('1.234,56'))); + check('parseNum 1,234.56 (de)', Math.abs(p('1,234.56') - 1234.56) < 1e-9, String(p('1,234.56'))); + check('parseNum 0,5 (de)', Math.abs(p('0,5') - 0.5) < 1e-9, String(p('0,5'))); + check('parseNum 1.234 (de → 1234)', Math.abs(p('1.234') - 1234) < 1e-9, String(p('1.234'))); + w.LANG = 'en'; + check('parseNum 1,234 (en → 1234)', Math.abs(p('1,234') - 1234) < 1e-9, String(p('1,234'))); + check('parseNum 1234.5 (en)', Math.abs(p('1234.5') - 1234.5) < 1e-9, String(p('1234.5'))); + w.LANG = 'de'; + [['de','1.234,56',1234.56],['la','1.234,56',1234.56],['ja','1,234.56',1234.56],['ja','1,234',1234]] + .forEach(function(f){ + w.LANG = f[0]; + check('parseNum ' + f[1] + ' (' + f[0] + ')', Math.abs(p(f[1]) - f[2]) < 1e-9, String(p(f[1]))); + }); + w.LANG = 'de'; + + console.log('bestanden (' + ok.length + '):'); + ok.forEach(x => console.log(' ✓ ' + x)); + if (bad.length) { + console.log('\nFEHLGESCHLAGEN (' + bad.length + '):'); + bad.forEach(x => console.log(' ✗ ' + x)); + } + console.log('\nangefragte Schlüssel:', angefragt.size, 'von', Object.keys(w.I18N_DE).length); + const nie = Object.keys(w.I18N_DE).filter(k => !angefragt.has(k)); + console.log('nie angefragt (' + nie.length + '):', nie.join(', ') || '—'); + process.exit(bad.length ? 1 : 0); +}, 2500); diff --git a/app/service-worker.js b/app/service-worker.js index 0e294c7..98c47bf 100644 --- a/app/service-worker.js +++ b/app/service-worker.js @@ -1,7 +1,7 @@ /* Altersvorsorge-Vergleich — Service Worker * * Strategie: stale-while-revalidate für alles im Scope. - * Die App ist eine einzige statische Datei ohne Backend; Korrektheit hängt + * Die App besteht aus einer Seite und vier Woerterbuechern, ohne Backend; Korrektheit hängt * also nicht an Frische, wohl aber Verfügbarkeit. Deshalb wird sofort aus * dem Cache geliefert und im Hintergrund aktualisiert. Beim Aktivieren * werden alte Cache-Versionen gelöscht. @@ -9,10 +9,14 @@ * Beim Ändern der App die Version hochzählen — sonst sieht ein bereits * installierter Client die Änderung erst nach zwei Aufrufen. */ -const VERSION = 'av-v11'; +const VERSION = 'av-v15'; const ASSETS = [ './', './index.html', + './i18n.de.js', + './i18n.en.js', + './i18n.la.js', + './i18n.ja.js', './manifest.json', './icon-192.png', './icon-512.png', @@ -22,7 +26,12 @@ const ASSETS = [ self.addEventListener('install', (e) => { e.waitUntil( caches.open(VERSION) - .then((c) => c.addAll(ASSETS).catch(() => c.add('./index.html'))) + /* addAll ist atomar: fehlt eine Datei, faellt alles durch. Der + Rueckfall auf die blosse Seite waere dann aber eine App ohne + Woerterbuch — deshalb die drei Pflichtdateien zusammen. */ + .then((c) => c.addAll(ASSETS) + .catch(() => c.addAll(['./index.html', './i18n.de.js', './i18n.en.js', + './i18n.la.js', './i18n.ja.js']))) .then(() => self.skipWaiting()) ); }); diff --git a/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx b/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx Binary files differindex a04ea94..28d65f5 100644 --- a/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx +++ b/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx diff --git a/build_bav.py b/build_bav.py index 61d7fc9..092fe0d 100644 --- a/build_bav.py +++ b/build_bav.py @@ -75,7 +75,10 @@ inp = [ ("netto_ziel", "Monatlicher Nettoaufwand (nur bei Modus 2)", 165, "EUR", "Der Betrag, um den Ihr verfuegbares Einkommen im ersten Jahr sinkt. Danach wird der Bruttobeitrag nach der Dynamisierungsregel fortgeschrieben."), ("dyn", "Beitrag mit dem Gehalt dynamisieren? 1 = ja", 1, "1/0", "1 = Beitrag steigt mit der Gehaltssteigerung, 0 = nominal konstant."), ("agzus", "Arbeitgeberzuschuss (Anteil des Beitrags)", 0.15, "", "Gesetzliches Minimum 15 % ( 1a Abs. 1a BetrAVG). DYNO-Arbeitgeber zahlen oft mehr - hier eintragen, was im Vertrag steht."), - ("agzus_sv", "Zuschussmodus: 0 = voller Beitrag, 1 = sv-freier Anteil, 2 = spitz", 2, "0/1/2", "2 ist der Gesetzeswortlaut: 1a Abs. 1a BetrAVG schuldet den Zuschuss nur, SOWEIT der Arbeitgeber tatsaechlich Sozialversicherungsbeitraege spart. Liegt Ihr Entgelt ueber beiden Beitragsbemessungsgrenzen, spart er nichts - und schuldet nichts. 1 ist die verbreitete pauschale Praxis, 0 eine freiwillige oder tarifvertragliche Zusage."), + ("agz_fix", "Fester AG-Zuschuss zusaetzlich (EUR je Monat)", 0, "EUR", "Betragsunabhaengiger Festzuschuss aus Tarifvertrag oder Betriebsvereinbarung - bei der Deutschen Bahn und in vielen Tarifwerken ueblich, dort ZUSAETZLICH zu den 15 % nach 1a Abs. 1a BetrAVG. Er ist arbeitgeberfinanzierte Zuwendung nach 3 Nr. 63 EStG und belegt den 8-%-Topf sowie die 4-%-Beitragsfreiheit ( 1 Abs. 1 S. 1 Nr. 9 SvEV) VORRANGIG; die eigene Entgeltumwandlung wird insoweit verdraengt und teurer."), + ("agz_fix_dyn","Festen Zuschuss dynamisieren? 1 = ja", 0, "1/0", "0 = nominal konstant (Voreinstellung: Festbetraege stehen meist nominal im Tarifvertrag), 1 = steigt mit der Gehalts- bzw. Tarifsteigerung."), + ("agz_fix_min","Mindest-Eigenbeitrag fuer den festen Zuschuss (EUR/Monat)", 0, "EUR", "Matching-Modelle knuepfen den Festbetrag an eine Mindest-Eigenleistung. 0 = bedingungslos; dann fliesst er in jedem aktiven Jahr, auch bei Beitrag null. Vorsicht: ein wirklich bedingungsloser Zuschuss ist kein Differenzeffekt der Umwandlungsentscheidung und verzerrt den Vergleich zugunsten der bAV."), + ("agzus_sv", "Zuschussmodus: 0 = voller Beitrag, 1 = sv-freier Anteil, 2 = spitz", 2, "0/1/2", "Gilt nur fuer den prozentualen Zuschuss; der feste Zuschuss oben ist davon unberuehrt. 2 ist der Gesetzeswortlaut: 1a Abs. 1a BetrAVG schuldet den Zuschuss nur, SOWEIT der Arbeitgeber tatsaechlich Sozialversicherungsbeitraege spart. Liegt Ihr Entgelt ueber beiden Beitragsbemessungsgrenzen, spart er nichts - und schuldet nichts. 1 ist die verbreitete pauschale Praxis, 0 eine freiwillige oder tarifvertragliche Zusage."), ("auszform", "bAV-Auszahlung: 1 = Kapital, 2 = lebenslange Rente", 1, "1/2", "Kapital: voll steuerpflichtig im Zuflussjahr, KV/PV auf 120 Monate verteilt ( 229 Abs. 1 S. 3 SGB V)."), ("rentfak", "Rentenfaktor bAV (EUR Monatsrente je 10.000 EUR)", 26, "EUR", "Nur relevant bei Auszahlungsform 2. 26 EUR entspricht ca. 3,1 % Entnahmequote."), ("rentdyn", "Dynamik der bAV-Rente p.a.", 0.01, "", "Ueberschussbeteiligung / Fondsentwicklung in der Rentenphase."), @@ -388,6 +391,7 @@ UB_COLS = [("i", "Schritt", 8, '0'), ("lo", "lo", 14, EUR2), ("hi", "hi", 14, EU ("mid", "B = (lo+hi)/2", 14, EUR2), ("svf", "sv-frei", 12, EUR2), ("drv", "D RV-Bem.", 12, EUR2), ("dkv", "D KV-Bem.", 12, EUR2), ("svan", "SV-Ersp. AN", 12, EUR2), ("svag", "SV-Ersp. AG", 12, EUR2), + ("agfx", "AG fest", 12, EUR2), ("agz", "AG-Zuschuss", 12, EUR2), ("stf", "steuerfrei", 12, EUR2), ("vorsm", "Vorsorgeaufw.", 13, EUR2), ("zvem", "zvE mit bAV", 13, EUR2), ("argm", "Tarifarg.", 13, EUR2), ("estm", "ESt", 13, EUR2), @@ -426,13 +430,14 @@ for t in range(NIT): u("lo", f"=IF({Cp['nb']}<{U['ziel']},{Cp['mid']},{Cp['lo']})") u("hi", f"=IF({Cp['nb']}<{U['ziel']},{Cp['hi']},{Cp['mid']})") u("mid", f"=({C['lo']}+{C['hi']})/2") - u("svf", f"=MIN({C['mid']},{P['sv_frei_q']}*{U['bbgrv0']})") + u("agfx", f"=IF({C['mid']}/12+0.000000001<{IN['agz_fix_min']},0,12*{IN['agz_fix']})") + u("svf", f"=MIN({C['mid']},MAX(0,{P['sv_frei_q']}*{U['bbgrv0']}-{C['agfx']}))") u("drv", f"=MIN({U['g0']},{U['bbgrv0']})-MIN({U['g0']}-{C['svf']},{U['bbgrv0']})") u("dkv", f"=MIN({U['g0']},{U['bbgkv0']})-MIN({U['g0']}-{C['svf']},{U['bbgkv0']})") u("svan", f"={C['drv']}*({P['rv_an']}+{P['av_an']})+{C['dkv']}*(({P['kv_allg']}+{P['kv_zus']})/2+{P['pv_an_eff']})") u("svag", f"={C['drv']}*({P['rv_an']}+{P['av_an']})+{C['dkv']}*(({P['kv_allg']}+{P['kv_zus']})/2+{P['pv_an']})") u("agz", f"=IF({IN['agzus_sv']}=2,MIN({IN['agzus']}*{C['mid']},{C['svag']})," - f"{IN['agzus']}*IF({IN['agzus_sv']}=1,{C['svf']},{C['mid']}))") + f"{IN['agzus']}*IF({IN['agzus_sv']}=1,{C['svf']},{C['mid']}))+{C['agfx']}") u("stf", f"=MIN({C['mid']},MAX(0,{P['st_frei_q']}*{U['bbgrv0']}-{C['agz']}))") u("vorsm", f"=MIN({U['g0']}-{C['svf']},{U['bbgrv0']})*{P['rv_an']}" f"+MIN({U['g0']}-{C['svf']},{U['bbgkv0']})*(0.96*({P['kv_allg']}+{P['kv_zus']})/2+{P['pv_an_eff']})") @@ -510,6 +515,8 @@ A_COLS = [ ("bav_br", "bAV Brutto-\nbeitrag", 11, EUR), ("bav_stf", "davon st.-\nfrei 3/63", 11, EUR), ("bav_svf", "davon\nsv-frei", 11, EUR), + ("agz_fx", "AG-Zuschuss\nfest", 11, EUR), + ("agz_var", "AG-Zuschuss\nprozentual", 11, EUR), ("agz", "AG-\nZuschuss", 11, EUR), ("bav_ein", "Einzahlung\nbAV gesamt", 11, EUR), ("bav_gef", "davon ge-\nfoerdert 3/63", 12, EUR), @@ -609,13 +616,20 @@ for t in range(N): # bAV f("bav_br", f"=MIN({C['brutto']},12*({BEITRAG})*IF({IN['dyn']}=1,{C['lohnidx']},1))*{C['aktiv']}") - # Der 8-%-Topf des 3 Nr. 63 EStG wird zuerst vom Arbeitgeberbeitrag belegt, - # der Rest steht der Entgeltumwandlung des Arbeitnehmers zur Verfuegung. - f("bav_svf", f"=MIN({C['bav_br']},{P['sv_frei_q']}*{C['bbgrv']})") + # Fester, betragsunabhaengiger Zuschuss. Faellt weg, wenn der eigene + # Monatsbeitrag die vereinbarte Mindestschwelle nicht erreicht. + f("agz_fx", f"={C['aktiv']}*IF({C['bav_br']}/12+0.000000001<{IN['agz_fix_min']},0," + f"12*{IN['agz_fix']}*IF({IN['agz_fix_dyn']}=1,{C['lohnidx']},1))") + # Der 4-%-Topf des 1 Abs. 1 S. 1 Nr. 9 SvEV und der 8-%-Topf des 3 Nr. 63 EStG + # werden zuerst vom Arbeitgeberbeitrag belegt, der Rest steht der Entgelt- + # umwandlung des Arbeitnehmers zur Verfuegung. + f("bav_svf", f"=MIN({C['bav_br']},MAX(0,{P['sv_frei_q']}*{C['bbgrv']}-{C['agz_fx']}))") # Modus 2 ( 1a Abs. 1a BetrAVG woertlich): Zuschuss nur bis zur tatsaechlichen # Ersparnis des Arbeitgebers. Oberhalb beider Beitragsbemessungsgrenzen ist sie null. - f("agz", f"=IF({IN['agzus_sv']}=2,MIN({IN['agzus']}*{C['bav_br']},{C['sverp_ag']})," - f"{IN['agzus']}*IF({IN['agzus_sv']}=1,{C['bav_svf']},{C['bav_br']}))") + # Der feste Zuschuss ist davon unberuehrt und kommt oben drauf. + f("agz_var", f"=IF({IN['agzus_sv']}=2,MIN({IN['agzus']}*{C['bav_br']},{C['sverp_ag']})," + f"{IN['agzus']}*IF({IN['agzus_sv']}=1,{C['bav_svf']},{C['bav_br']}))") + f("agz", f"={C['agz_var']}+{C['agz_fx']}") f("bav_stf", f"=MIN({C['bav_br']},MAX(0,{P['st_frei_q']}*{C['bbgrv']}-{C['agz']}))") f("bav_ein", f"={C['bav_br']}+{C['agz']}") f("bav_gef", f"=MIN({C['bav_ein']},{P['st_frei_q']}*{C['bbgrv']})") @@ -1117,6 +1131,15 @@ diag = [ ("Anteil der bAV-Beitraege OHNE Foerderung (erstes Jahr)", f"=IF(Ansparphase!${AC['bav_ein']}${R0}=0,0,Ansparphase!${AC['bav_ngef']}${R0}/Ansparphase!${AC['bav_ein']}${R0})", PCT, "Alles oberhalb von 8 % der BBG-RV ist weder steuer- noch sozialabgabenbeguenstigt. Diese Schicht wandert in einen Versicherungsmantel, ohne dafuer etwas zu bekommen - ausser der Beitragspflicht in der KV. Ist dieser Anteil gross, ist die bAV strukturell unterlegen."), + ("Arbeitgeberzuschuss prozentual (erstes Jahr)", + f"=Ansparphase!${AC['agz_var']}${R0}", EUR, + "Der Teil nach 1a Abs. 1a BetrAVG bzw. der vertraglich zugesagte Prozentsatz, gerechnet nach dem eingestellten Zuschussmodus."), + ("Fester Arbeitgeberzuschuss (erstes Jahr)", + f"=Ansparphase!${AC['agz_fx']}${R0}", EUR, + "Betragsunabhaengiger Festzuschuss aus Tarifvertrag oder Betriebsvereinbarung. Null, wenn der eigene Monatsbeitrag die eingestellte Mindestschwelle nicht erreicht."), + ("Fester Zuschuss oberhalb des 4-%-Topfes (erstes Jahr)", + f"=MAX(0,Ansparphase!${AC['agz_fx']}${R0}-{P['sv_frei_q']}*Ansparphase!${AC['bbgrv']}${R0})", EUR, + "Dieser Teil waere beim Arbeitnehmer beitragspflichtiges Arbeitsentgelt. Das Modell rechnet die zusaetzliche Beitragslast NICHT gegen und ueberschaetzt die bAV insoweit. Sollte null sein."), ("Ausschoepfung des Rechtsanspruchs nach 1a BetrAVG (4 % BBG)", f"=IF(Ansparphase!${AC['bbgrv']}${R0}=0,0,Ansparphase!${AC['bav_br']}${R0}/({P['sv_frei_q']}*Ansparphase!${AC['bbgrv']}${R0}))", PCT, "Ueber 100 % besteht kein Rechtsanspruch mehr auf Entgeltumwandlung; darueber hinaus ist die Zustimmung des Arbeitgebers noetig."), @@ -1276,6 +1299,7 @@ txt = [ ("", "1) bAV (Direktversicherung, ETF-basiert, z.B. DYNO)"), ("", " + Beitrag steuerfrei bis 8 % der BBG-RV ( 3 Nr. 63 EStG), sozialabgabenfrei bis 4 % ( 1 Abs. 1 S. 1 Nr. 9 SvEV)"), ("", " + Arbeitgeberzuschuss von mindestens 15 % ( 1a Abs. 1a BetrAVG) - das ist der eigentliche Renditetreiber"), + ("", " + ggf. zusaetzlich ein tarifvertraglicher FESTBETRAG unabhaengig vom Monatsbeitrag (z.B. Deutsche Bahn)"), ("", " - Leistung voll steuerpflichtig ( 22 Nr. 5 S. 1 EStG); Kapitalabfindung trifft in einem Jahr den Spitzensteuersatz"), ("", " - Kranken- und Pflegeversicherung auf die volle Leistung, Beitragssatz allein vom Rentner ( 229, 250 SGB V)"), ("", " - Entgeltumwandlung mindert die Entgeltpunkte und damit die gesetzliche Rente"), @@ -1301,7 +1325,15 @@ txt = [ ("", "- Oberhalb der Beitragsbemessungsgrenzen ist die Entgeltumwandlung sozialversicherungsrechtlich neutral: die"), ("", " Bemessungsgrundlage bleibt gedeckelt, also spart weder Arbeitnehmer noch Arbeitgeber Beitraege, und es gehen"), ("", " auch keine Entgeltpunkte verloren. Beides folgt aus derselben Differenz und ist im Modell zwingend gekoppelt."), - ("", " Der Zuschussmodus 2 zieht daraus die Konsequenz und setzt den Zuschuss dann auf null."), + ("", " Der Zuschussmodus 2 zieht daraus die Konsequenz und setzt den prozentualen Zuschuss dann auf null."), + ("", "- Der FESTE Arbeitgeberzuschuss (z.B. Deutsche Bahn) ist betragsunabhaengig und vom Zuschussmodus unberuehrt: er"), + ("", " beruht auf Tarifvertrag oder Betriebsvereinbarung, nicht auf 1a Abs. 1a BetrAVG, und kommt oben drauf."), + ("", " Als arbeitgeberfinanzierte Zuwendung nach 3 Nr. 63 EStG belegt er den 8-%-Topf und die 4-%-Beitragsfreiheit"), + ("", " VORRANGIG; die eigene Entgeltumwandlung wird insoweit verdraengt und teurer. Sein Barwert waechst nicht mit dem"), + ("", " Beitrag - oekonomisch ist er ein Sockel, kein Hebel: er hebt den Durchschnittsertrag, nicht die Grenzrendite."), + ("", " NICHT abgebildet: uebersteigt der feste Zuschuss allein 4 % der BBG-RV, waere der Ueberhang beim Arbeitnehmer"), + ("", " beitragspflichtiges Arbeitsentgelt. Das Modell rechnet diese Beitragslast nicht gegen und ueberschaetzt die bAV"), + ("", " insoweit; das Blatt 'Kennzahlen' weist den Ueberhang aus. Vertrauensgrad der Vorrang-Lesart ca. 80 %."), ("", "- Beitraege gelten als unterjaehrig geleistet (Verzinsung mit (1+r)^0,5 im Einzahlungsjahr)."), ("", "- In der Auszahlungsphase wird die Vorabpauschale nicht mehr fortgeschrieben; sie waere durch die Anrechnung auf den"), ("", " realisierten Gewinn ( 19 InvStG) barwertig weitgehend neutral."), |
