kimal3 0.2.10 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +135 -0
  2. package/dist/api.js +16 -7
  3. package/dist/arbeitsplatz-dateien.js +23 -19
  4. package/dist/argumente.js +25 -97
  5. package/dist/befehle/arbeitsplatz.js +50 -68
  6. package/dist/befehle/firma-daten.js +24 -26
  7. package/dist/befehle/kontext-fragen.js +57 -47
  8. package/dist/befehle/kontext-gemeinsam.js +84 -40
  9. package/dist/befehle/kontext-hoch.js +40 -46
  10. package/dist/befehle/kontext-runter.js +70 -51
  11. package/dist/befehle/login.js +23 -9
  12. package/dist/befehle/logout.js +9 -4
  13. package/dist/befehle/projekt-liste.js +24 -29
  14. package/dist/befehle/projekt-neu.js +25 -31
  15. package/dist/befehle/skill-install.js +78 -49
  16. package/dist/befehle/skill-tausch.js +28 -0
  17. package/dist/befehle/wache.js +12 -6
  18. package/dist/befehle/werkbank.js +98 -87
  19. package/dist/befehle-katalog-en.js +185 -0
  20. package/dist/befehle-katalog.js +10 -49
  21. package/dist/befehlswoerter.js +69 -0
  22. package/dist/einstellungen-mischen.js +2 -2
  23. package/dist/einstellungen-schreiben.js +8 -5
  24. package/dist/fehler.js +5 -5
  25. package/dist/firmen-liste.js +8 -5
  26. package/dist/format.js +41 -0
  27. package/dist/fragen-lesen.js +3 -3
  28. package/dist/hilfe.js +24 -0
  29. package/dist/kimal3.js +77 -71
  30. package/dist/kontext-dateien.js +30 -23
  31. package/dist/kontext-hoch-zeile.js +5 -3
  32. package/dist/kontext-rat.js +151 -0
  33. package/dist/kontext-stand.js +64 -210
  34. package/dist/nachricht.js +39 -15
  35. package/dist/projekt-neu-firma.js +9 -9
  36. package/dist/projekt-zeilen.js +40 -54
  37. package/dist/slash-befehle.js +36 -0
  38. package/dist/sprache.js +114 -0
  39. package/dist/texte/allgemein.js +99 -0
  40. package/dist/texte/arbeitsplatz.js +135 -0
  41. package/dist/texte/kontext-hoch.js +128 -0
  42. package/dist/texte/kontext.js +171 -0
  43. package/dist/texte/projekte.js +126 -0
  44. package/dist/texte/skill.js +87 -0
  45. package/dist/texte/wache.js +36 -0
  46. package/dist/texte/werkbank.js +161 -0
  47. package/dist/wache-rechnung.js +27 -40
  48. package/dist/werkbank-name.js +28 -10
  49. package/dist/wort.js +98 -0
  50. package/package.json +2 -2
  51. package/skill/SKILL.md +9 -9
  52. package/skill/commands/k3.md +1 -1
  53. package/skill/commands/k3feierabend.md +99 -97
  54. package/skill/commands/k3kontext.md +1 -1
  55. package/skill/commands/k3kurs.md +6 -5
  56. package/skill/commands/k3start.md +2 -1
  57. package/skill/en/SKILL.md +451 -0
  58. package/skill/en/commands/k3.md +106 -0
  59. package/skill/en/commands/k3analyse.md +59 -0
  60. package/skill/en/commands/k3context.md +92 -0
  61. package/skill/en/commands/k3course.md +54 -0
  62. package/skill/en/commands/k3list.md +26 -0
  63. package/skill/en/commands/k3project.md +50 -0
  64. package/skill/en/commands/k3start.md +47 -0
  65. package/skill/en/commands/k3wrapup.md +99 -0
  66. package/dist/zz-kaputt.js +0 -1
@@ -1,8 +1,13 @@
1
1
  import { feld } from "../api.js";
2
2
  import { alsWort } from "../argumente.js";
3
3
  import { schlussFehler } from "../fehler.js";
4
+ import { befehlIn, schalterIn } from "../befehlswoerter.js";
5
+ import { sprache } from "../sprache.js";
6
+ import { ALLGEMEIN_TEXTE } from "../texte/allgemein.js";
7
+ import { KONTEXT_TEXTE } from "../texte/kontext.js";
4
8
  import { BASIS_URL } from "../anmeldung.js";
5
9
  import { findeArbeitsplatz } from "../kontext-dateien.js";
10
+ import { ORDNER_NAMEN } from "../kontext-stand.js";
6
11
  import { angeboteneFirmen } from "../firmen-liste.js";
7
12
  /**
8
13
  * Was `kontext runter` (`kontext-runter.ts`) und `kontext hoch`
@@ -36,28 +41,56 @@ import { angeboteneFirmen } from "../firmen-liste.js";
36
41
  * stderr, der Aufrufer gibt nur noch `1` zurueck.
37
42
  */
38
43
  export function arbeitsplatzOderAbsage() {
39
- const ordner = findeArbeitsplatz(process.cwd());
40
- if (ordner !== null)
41
- return ordner;
42
- /*
43
- 🔴 **ECHTE UMLAUTE** -- dieselbe Begruendung wie bei `fremdeDateiAbsage`
44
- zwanzig Zeilen tiefer und in `kontext-fragen.ts:26`: Das hier liest ein
45
- Kunde im Terminal. Bis Review-Runde 4 stand hier die ASCII-Umschrift,
46
- waehrend die Schwesterfunktion in DERSELBEN Datei Umlaute setzte und es im
47
- Kommentar begruendete -- eine Datei, zwei Schreibweisen.
48
-
49
- ⚠️ Der Umlaut-Fix an `kontext-hoch.ts` (Runde 3) hat diesen Zwilling
50
- stehen lassen, und die Wache schlug nicht an: Sie prueft den zitierten
51
- Ausschnitt „Hier steht kein KI-Arbeitsplatz", und der traegt zufaellig
52
- keinen Umlaut. *Eine Wache deckt den Satz, den sie kennt -- nicht die
53
- Regel dahinter.*
54
- */
55
- console.error("Hier steht kein KI-Arbeitsplatz — ich suche von hier aufwärts nach einem");
56
- console.error("Ordner, der `kontext/` UND `CLAUDE.md` enthält.");
57
- console.error("");
58
- console.error(" Wechsle in deinen Arbeitsplatz-Ordner und versuch es dort noch einmal.");
44
+ const fund = findeArbeitsplatz(process.cwd());
45
+ if (fund.art === "gefunden")
46
+ return { pfad: fund.pfad, sprache: fund.sprache };
47
+ arbeitsplatzAbsageZeilen(fund, sprache()).forEach((z) => console.error(z));
59
48
  return null;
60
49
  }
50
+ /**
51
+ * Die Zeilen der Absage, wenn kein (eindeutiger) Arbeitsplatz da ist -- als reine Rechnung mit der
52
+ * Sprache als Pflichtwert, damit `check:cli` beide Sprachen misst (Teil 3, Task 10).
53
+ *
54
+ * 🔴 **ECHTE UMLAUTE** -- dieselbe Begruendung wie bei `fremdeDateiAbsage`
55
+ * weiter unten und in `kontext-fragen.ts`: Das hier liest ein Kunde im
56
+ * Terminal. Bis Review-Runde 4 stand hier die ASCII-Umschrift, waehrend die
57
+ * Schwesterfunktion in DERSELBEN Datei Umlaute setzte -- eine Datei, zwei
58
+ * Schreibweisen. ⚠️ Der Umlaut-Fix an `kontext-hoch.ts` (Runde 3) hat diesen
59
+ * Zwilling stehen lassen, und die Wache schlug nicht an: Sie prueft den
60
+ * zitierten Ausschnitt „Hier steht kein KI-Arbeitsplatz", und der traegt
61
+ * zufaellig keinen Umlaut. *Eine Wache deckt den Satz, den sie kennt -- nicht
62
+ * die Regel dahinter.*
63
+ */
64
+ export function arbeitsplatzAbsageZeilen(fund, s) {
65
+ const t = KONTEXT_TEXTE[s];
66
+ // Spec 3.2: zwei Namen nebeneinander -- melden, nicht still einen waehlen, und nichts schreiben.
67
+ if (fund.art === "beide")
68
+ return [t.bothFolders1(fund.pfad), t.bothFolders2, "", t.bothFolders3];
69
+ return [t.noWorkplace1, t.noWorkplace2, "", t.noWorkplace3];
70
+ }
71
+ /**
72
+ * US-13: Der Ordner hat die alte Sprache, die Firma inzwischen eine andere.
73
+ * Nichts schreiben (sonst stuenden zwei Sprachen nebeneinander), den Weg nennen.
74
+ * ⚠️ ECHTE UMLAUTE -- das liest ein Kunde.
75
+ * `firmenname` (aus der Antwort des Servers) kommt als `--firma "…"` in den Rat, damit
76
+ * `arbeitsplatz neu` bei mehreren Firmen nicht gleich zurueckfragt (Final-Review, Fund 3);
77
+ * `null` (aelterer Server ohne Namen) -> der Rat ohne `--firma`, wie bisher.
78
+ */
79
+ export function fremdeSpracheAbsage(ordner, firma, firmenname) {
80
+ fremdeSpracheZeilen(ordner, firma, firmenname, sprache()).forEach((z) => console.error(z));
81
+ }
82
+ /** Die Zeilen von `fremdeSpracheAbsage` -- rein, Sprache des Menschen als Pflichtwert (`check:cli`). */
83
+ export function fremdeSpracheZeilen(ordner, firma, firmenname, s) {
84
+ const t = KONTEXT_TEXTE[s];
85
+ return [
86
+ t.languageChanged1(firma, ordner, ORDNER_NAMEN[ordner].kontext),
87
+ t.languageChanged2,
88
+ "",
89
+ t.languageChanged3,
90
+ t.languageChangedCommand(befehlIn("arbeitsplatz neu", s), schalterIn("firma", s), firmenname === null ? null : alsWort(firmenname, true)), // kein-anzeigetext: Befehlsschlüssel
91
+ t.languageChanged4,
92
+ ];
93
+ }
61
94
  /**
62
95
  * Was ein Mensch liest, wenn eine der fuenf Dateien keine einzige ihrer Fragen
63
96
  * mehr traegt -- **woertlich dieselben Zeilen fuer `kontext hoch` und
@@ -66,7 +99,7 @@ export function arbeitsplatzOderAbsage() {
66
99
  * 🔴 **WARUM SIE GETEILT WIRD.** `kontext fragen` zaehlte eine solche Datei
67
100
  * vorher stillschweigend mit NULL Fragen und liess sie aus beiden Summen
68
101
  * fallen. Gemessen mit der ausgelieferten `README.md` als `kontext/marke.md`:
69
- * „12 von 12 Fragen" statt 17, „Die Marke" nirgends genannt -- und nach zwoelf
102
+ * „12 von 12 Fragen" statt der vollen Zahl, „Die Marke" nirgends genannt -- und nach zwoelf
70
103
  * Antworten meldete der Befehl `✓ Alle 12 Fragen … sind beantwortet`, waehrend
71
104
  * `kontext hoch` einen Schritt spaeter auf genau dieser Datei abbrach.
72
105
  * *Der lesende Befehl gab die beruhigende Auskunft, der schreibende
@@ -83,10 +116,13 @@ export function arbeitsplatzOderAbsage() {
83
116
  * 🔑 ECHTE UMLAUTE -- das hier liest ein Kunde im Terminal. Die ASCII-Umschrift
84
117
  * dieses Ordners gilt nur fuer Kommentare.
85
118
  */
86
- export function fremdeDateiAbsage(pfad, titel, ankerPraefix, folge) {
87
- console.error(`„${pfad}" sieht nicht aus wie „${titel}". ${folge}`);
88
- console.error(` Ich finde darin keine einzige Frage dieser Datei (Zeilen wie \`<!-- k3:${ankerPraefix}… -->\`).`);
89
- console.error(" Hol sie mit `kimal3 kontext runter` zurück und trag deine Antworten dort ein.");
119
+ export function fremdeDateiAbsage(pfad, feld, ankerPraefix, folge) {
120
+ fremdeDateiZeilen(pfad, feld, ankerPraefix, folge, sprache()).forEach((z) => console.error(z));
121
+ }
122
+ /** Die Zeilen von `fremdeDateiAbsage` -- rein, Sprache des Menschen als Pflichtwert (`check:cli`). */
123
+ export function fremdeDateiZeilen(pfad, feld, ankerPraefix, folge, s) {
124
+ const t = KONTEXT_TEXTE[s];
125
+ return [t.fileNotLike(pfad, t.dateiTitel[feld], folge), t.noQuestionFound(ankerPraefix), t.fetchItBack];
90
126
  }
91
127
  /** `--firma`/`--firma-id` -- die einzigen Optionen, die beide Richtungen kennen. */
92
128
  export const ERLAUBT = ["firma", "firma-id"];
@@ -111,19 +147,23 @@ export const ERLAUBT = ["firma", "firma-id"];
111
147
  * den Ausweg (`--ueberschreiben`) -- sie kommt woertlich vom Server und wird
112
148
  * hier nur ausgegeben, nicht neu formuliert.
113
149
  */
114
- export function absage(antwort, befehl) {
115
- const meldung = feld(antwort.koerper, "meldung") ?? "Das hat nicht geklappt.";
150
+ export function absage(antwort, befehl, optionen) {
151
+ const s = sprache();
152
+ const t = ALLGEMEIN_TEXTE[s];
153
+ const meldung = feld(antwort.koerper, "meldung") ?? t.genericFailure;
116
154
  if (antwort.status === 402) {
117
155
  console.error(meldung);
118
156
  return 4;
119
157
  }
120
158
  if (antwort.status === 401) {
121
- console.error(`${meldung} Bitte \`kimal3 login\` ausführen.`);
159
+ console.error(`${meldung}${t.pleaseSignIn}`);
122
160
  return 2;
123
161
  }
124
162
  if (antwort.status === 409) {
125
163
  const grund = feld(antwort.koerper, "grund");
126
- if (grund === "veraltet") {
164
+ // Teil 2: `update-noetig` und `sprache-anders` (Task 14) kommen wie `veraltet` als fertiger
165
+ // Satz in der Kontosprache -- keine Firmenliste darunter, die mit der Lage nichts zu tun hat.
166
+ if (grund === "veraltet" || grund === "update-noetig" || grund === "sprache-anders") {
127
167
  console.error(meldung);
128
168
  return 1;
129
169
  }
@@ -137,22 +177,22 @@ export function absage(antwort, befehl) {
137
177
  typeof x.name === "string")
138
178
  : [];
139
179
  if (auswahl.length > 0) {
140
- console.error("\n Diese beiden heissen gleich — nimm die ID:");
141
- firmenIdZeilenKontext(auswahl, befehl).forEach((z) => console.error(z));
180
+ console.error(t.sameNameUseId);
181
+ firmenIdZeilenKontext(auswahl, befehl, optionen, s).forEach((z) => console.error(z));
142
182
  }
143
183
  else {
144
- const zeilen = firmenZeilenKontext(namen, befehl);
184
+ const zeilen = firmenZeilenKontext(namen, befehl, optionen, s);
145
185
  if (zeilen.length > 0) {
146
- console.error("\n Deine Firmen:");
186
+ console.error(t.yourCompanies);
147
187
  zeilen.forEach((z) => console.error(z));
148
188
  }
149
189
  }
150
190
  if (grund === "keine-firma") {
151
- console.error(`\n Eine Firma anlegen: ${BASIS_URL}/firma/neu`);
191
+ console.error(t.createCompany(BASIS_URL));
152
192
  }
153
193
  return 1;
154
194
  }
155
- const schluss = schlussFehler(antwort.status, meldung);
195
+ const schluss = schlussFehler(antwort.status, meldung, s);
156
196
  console.error(schluss.text);
157
197
  return schluss.wert;
158
198
  }
@@ -160,7 +200,10 @@ export function absage(antwort, befehl) {
160
200
  * `firmenZeilen` aus `firmen-liste.ts`, nur mit dem richtigen Befehl in der
161
201
  * Zeile -- s. Kommentar an `absage`.
162
202
  */
163
- function firmenZeilenKontext(namen, befehl) {
203
+ export function firmenZeilenKontext(namen, befehl, optionen, s) {
204
+ const t = ALLGEMEIN_TEXTE[s];
205
+ // Befehl, schon getippte Optionen, dann der Firmen-Schalter -- in der Sprache des Satzes.
206
+ const vorne = `${befehlIn(befehl, s)}${optionen}`;
164
207
  const anzahl = (name) => namen.filter((n) => n === name).length;
165
208
  const gesehen = new Set();
166
209
  const zeilen = [];
@@ -170,15 +213,16 @@ function firmenZeilenKontext(namen, befehl) {
170
213
  gesehen.add(name);
171
214
  const wieOft = anzahl(name);
172
215
  if (wieOft === 1) {
173
- zeilen.push(` kimal3 ${befehl} --firma ${alsWort(name, true)}`);
216
+ zeilen.push(t.companyLine(vorne, schalterIn("firma", s), alsWort(name, true)));
174
217
  }
175
218
  else {
176
- zeilen.push(` „${name}" — kommt ${wieOft}× vor und ist über den Namen nicht wählbar.`);
219
+ zeilen.push(t.nameNotUnique(name, wieOft));
177
220
  }
178
221
  }
179
222
  return zeilen;
180
223
  }
181
224
  /** `firmenIdZeilen` aus `firmen-liste.ts`, nur mit dem richtigen Befehl. */
182
- function firmenIdZeilenKontext(auswahl, befehl) {
183
- return auswahl.map((f, i) => ` kimal3 ${befehl} --firma-id "${f.id}" # ${f.name} (${i + 1}.)`);
225
+ export function firmenIdZeilenKontext(auswahl, befehl, optionen, s) {
226
+ const vorne = `${befehlIn(befehl, s)}${optionen}`;
227
+ return auswahl.map((f, i) => ALLGEMEIN_TEXTE[s].companyIdLine(vorne, schalterIn("firma-id", s), f.id, f.name, i + 1));
184
228
  }
@@ -1,6 +1,9 @@
1
- import { zerlege } from "../argumente.js";
1
+ import { schalterOhneWert, zerlege } from "../argumente.js";
2
+ import { sprache } from "../sprache.js";
3
+ import { ALLGEMEIN_TEXTE } from "../texte/allgemein.js";
4
+ import { KONTEXT_HOCH_TEXTE } from "../texte/kontext-hoch.js";
2
5
  import { ruf } from "../api.js";
3
- import { FIRMEN_TEXTE, alsMerkzettel, bekannterStandAus, merkzettelNachHoch, } from "../kontext-stand.js";
6
+ import { firmenTexte, alsMerkzettel, bekannterStandAus, merkzettelNachHoch, } from "../kontext-stand.js";
4
7
  import { traegtFragenVon } from "../fragen-lesen.js";
5
8
  import { leseEineDatei, leseMerkzettelDatei, schreibeMerkzettelDatei } from "../kontext-dateien.js";
6
9
  import { ERLAUBT, absage, arbeitsplatzOderAbsage, fremdeDateiAbsage } from "./kontext-gemeinsam.js";
@@ -12,7 +15,7 @@ import { hochZeile } from "../kontext-hoch-zeile.js";
12
15
  `marke_md`). Fuer vier der fuenf Dateien war das richtig; die fuenfte,
13
16
  `kontext/kurzprofil.md`, fuellt `drei_saetze` und haette daraus `kurzprofil_md`
14
17
  bekommen -- ein Feld, das die Route nicht kennt. Die Zuordnung steht jetzt als
15
- Tabelle in `FIRMEN_TEXTE` (`../kontext-stand.js`), und dass sie mit `FELDER`
18
+ Tabelle in `firmenTexte` (`../kontext-stand.js`), und dass sie mit `FELDER`
16
19
  in der App uebereinstimmt, prueft `check:kontext`.
17
20
  */
18
21
  // 📌 `bekannterStandAus` wohnte hier (Fix-Runde 1, Review-Fund 1) und ist seit
@@ -21,25 +24,10 @@ import { hochZeile } from "../kontext-hoch-zeile.js";
21
24
  // von HIER haette `api.ts` in einen Kreisimport mit sich selbst gezwungen
22
25
  // (diese Datei importiert `ruf` aus `../api.js`). Die volle Begruendung der
23
26
  // Rechnung selbst steht jetzt dort.
24
- const HILFE_HOCH = `
25
- kimal3 kontext hoch [--firma "Name" | --firma-id "…"] [--ueberschreiben]
26
-
27
- Schickt deine lokalen Aenderungen an allen fuenf Firmen-Texten nach oben --
28
- fuer den Firmen-Admin. Nur wer Admin ist, darf das; die App entscheidet.
29
-
30
- Die fuenf sind die Dateien in kontext/: kurzprofil.md, firma.md, marke.md,
31
- systeme.md, sicherheit.md.
32
-
33
- Welche Fragen darin noch offen sind, zeigt "kimal3 kontext fragen".
34
-
35
- --firma "Name" Noetig, wenn du zu mehreren Firmen gehoerst.
36
- Die Absage nennt dir die Namen als fertige Befehle.
37
-
38
- --firma-id "…" Fuer den Fall, dass zwei deiner Firmen GLEICH heissen.
39
-
40
- --ueberschreiben Speichert auch dann, wenn die Firma inzwischen von
41
- jemand anderem geaendert wurde (§7d).
42
- `;
27
+ /** Die Hilfe von `kontext hoch` / `context upload` in der Sprache des Menschen. */
28
+ export function hilfe(s) {
29
+ return KONTEXT_HOCH_TEXTE[s].uploadHelp;
30
+ }
43
31
  /**
44
32
  * Schickt die fuenf Firmen-Texte des gefundenen Arbeitsplatzes nach oben --
45
33
  * die Gegenrichtung zu `kontextRunter` (`kontext-runter.ts`). Fundregel,
@@ -58,20 +46,21 @@ const HILFE_HOCH = `
58
46
  * 🔴 **Importiert bewusst KEIN `FELDER` aus `src/app/firma/kontext-felder.ts`.**
59
47
  * `cli/src/` ist ein eigenes Paket mit eigenem `tsconfig` und darf nichts aus
60
48
  * `src/` importieren (s. Kopf von `kontext-stand.ts`). Welche Datei welches
61
- * Feld fuellt, steht deshalb in `FIRMEN_TEXTE` -- und dass diese Tabelle mit
49
+ * Feld fuellt, steht deshalb in `firmenTexte` -- und dass diese Tabelle mit
62
50
  * `FELDER` uebereinstimmt, ist eine Pruefung in `check:kontext`.
63
51
  */
64
52
  export async function kontextHoch(argv) {
53
+ let s = sprache();
54
+ let t = KONTEXT_HOCH_TEXTE[s];
65
55
  if (argv.includes("--help") || argv.includes("-h")) {
66
- console.log(HILFE_HOCH);
56
+ console.log(hilfe(s));
67
57
  return 0;
68
58
  }
69
- const ueberschreiben = argv.includes("--ueberschreiben");
70
- // ⚠️ `zerlege` kennt keine Optionen ohne Wert -- `--ueberschreiben` wird
71
- // deshalb VOR der Zerlegung herausgefiltert, genau wie `--help`/`-h` oben
59
+ // ⚠️ `zerlege` kennt keine Optionen ohne Wert -- `--ueberschreiben` (englisch `--overwrite`)
60
+ // wird deshalb VOR der Zerlegung herausgefiltert, genau wie `--help`/`-h` oben
72
61
  // nie an `zerlege` gereicht werden.
73
- const ohneFlag = argv.filter((a) => a !== "--ueberschreiben");
74
- const zerlegt = zerlege(ohneFlag, ERLAUBT);
62
+ const { gesetzt: ueberschreiben, rest: ohneFlag } = schalterOhneWert(argv, "ueberschreiben");
63
+ const zerlegt = zerlege(ohneFlag, ERLAUBT, s);
75
64
  if (!zerlegt.ok) {
76
65
  console.error(zerlegt.meldung);
77
66
  return 1;
@@ -83,8 +72,8 @@ export async function kontextHoch(argv) {
83
72
  // (`bekannterStandAus`), nicht nur aus den Dateien, die gleich gelesen
84
73
  // werden. Die Firmenzeile traegt genau EIN `geaendert_am` fuer alle fuenf
85
74
  // Felder -- jeder nicht-leere Eintrag kennt es also, auch der der `CLAUDE.md`.
86
- const lokalerMerkzettel = leseMerkzettelDatei(ordner);
87
- const bekannterStand = bekannterStandAus(lokalerMerkzettel);
75
+ const lokalerMerkzettel = leseMerkzettelDatei(ordner.pfad);
76
+ const bekannterStand = bekannterStandAus(lokalerMerkzettel, ordner.sprache);
88
77
  /*
89
78
  ---- die fuenf Texte lesen ---------------------------------------------
90
79
 
@@ -95,15 +84,15 @@ export async function kontextHoch(argv) {
95
84
  benennen kann, ist teurer als ein Abbruch, der klar sagt, warum.*
96
85
  */
97
86
  const werte = {};
98
- for (const datei of FIRMEN_TEXTE) {
99
- const gelesen = leseEineDatei(ordner, datei.pfad);
87
+ for (const datei of firmenTexte(ordner.sprache)) {
88
+ const gelesen = leseEineDatei(ordner.pfad, datei.pfad);
100
89
  if (!gelesen.ok) {
101
90
  console.error(gelesen.meldung);
102
91
  return 1;
103
92
  }
104
93
  if (gelesen.inhalt === null) {
105
- console.error(`„${datei.pfad}" gibt es hier nicht -- ich schicke nichts Halbes hoch.`);
106
- console.error(" Erst `kimal3 kontext runter` ausfuehren, dann bearbeiten, dann noch einmal.");
94
+ console.error(t.uploadFileMissing(datei.pfad));
95
+ console.error(t.uploadFetchFirst);
107
96
  return 1;
108
97
  }
109
98
  /*
@@ -121,7 +110,7 @@ export async function kontextHoch(argv) {
121
110
  angelegt hat, weil `kontext runter` noch nie lief.
122
111
  */
123
112
  if (!traegtFragenVon(gelesen.inhalt, datei.ankerPraefix)) {
124
- fremdeDateiAbsage(datei.pfad, datei.titel, datei.ankerPraefix, "Nichts wurde gespeichert.");
113
+ fremdeDateiAbsage(datei.pfad, datei.feld, datei.ankerPraefix, t.nothingSaved);
125
114
  return 1;
126
115
  }
127
116
  werte[datei.feld] = gelesen.inhalt;
@@ -134,23 +123,28 @@ export async function kontextHoch(argv) {
134
123
  suche.set("firma", wunsch);
135
124
  if (wunschId !== undefined)
136
125
  suche.set("firma-id", wunschId);
137
- const pfad = suche.toString() ? `/api/cli/kontext?${suche.toString()}` : "/api/cli/kontext";
138
- const koerper = { stand: bekannterStand, ...werte };
126
+ const pfad = suche.toString() ? `/api/cli/kontext?${suche.toString()}` : "/api/cli/kontext"; // kein-anzeigetext: Pfad der Schnittstelle
127
+ // 🌐 Teil 2: Die Sprache des ORDNERS geht mit. Der Server haelt sie gegen die Firmensprache und
128
+ // sagt `sprache-anders` (409), statt deutsche Texte in eine englische Firma zu schreiben (US-13).
129
+ const koerper = { stand: bekannterStand, ...werte, sprache: ordner.sprache };
139
130
  if (ueberschreiben)
140
131
  koerper.ueberschreiben = true;
141
- const antwort = await ruf(pfad, { methode: "POST", mitAusweis: true, koerper });
132
+ const antwort = await ruf(pfad, { methode: "POST", mitAusweis: true, koerper }); // kein-anzeigetext: HTTP-Verb
133
+ // 🌐 Die Antwort kann die Kontosprache umgestellt haben (Spec 3.2, Schritt 3): Was ab hier gedruckt wird, in der neuen.
134
+ s = sprache();
135
+ t = KONTEXT_HOCH_TEXTE[s];
142
136
  if (antwort.status !== 200) {
143
- return absage(antwort, "kontext hoch");
137
+ return absage(antwort, "kontext hoch", ""); // kein-anzeigetext: Befehlsschlüssel
144
138
  }
145
139
  // 🔴 Ein `200` mit unlesbarem Rumpf ist KEIN Erfolg -- dasselbe Muster wie
146
140
  // in `kontextRunter` und in `firma-daten.ts`.
147
141
  if (antwort.koerper === null || typeof antwort.koerper !== "object") {
148
- console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
142
+ console.error(ALLGEMEIN_TEXTE[s].unreadableAnswer);
149
143
  return 3;
150
144
  }
151
145
  const ergebnis = alsHochAntwort(antwort.koerper);
152
146
  if (ergebnis === null) {
153
- console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
147
+ console.error(ALLGEMEIN_TEXTE[s].unreadableAnswer);
154
148
  return 3;
155
149
  }
156
150
  /*
@@ -173,7 +167,7 @@ export async function kontextHoch(argv) {
173
167
  CLAUDE.md" und riete zu `kontext hoch` -- dem Befehl, der sie gar nicht
174
168
  mitnimmt. Gemessen auf der Kundenstrecke am 2026-08-25.
175
169
  */
176
- const zuSchreiben = merkzettelNachHoch(ergebnis.merkzettel, lokalerMerkzettel, FIRMEN_TEXTE.map((d) => d.pfad));
170
+ const zuSchreiben = merkzettelNachHoch(ergebnis.merkzettel, lokalerMerkzettel, firmenTexte(ordner.sprache).map((d) => d.pfad), ordner.sprache);
177
171
  /*
178
172
  🔴 **DIE ERFOLGSMELDUNG STEHT VOR DEM MERKZETTEL** (Review-Runde 4).
179
173
 
@@ -186,8 +180,8 @@ export async function kontextHoch(argv) {
186
180
  was daneben schiefging. Was hochgeschickt wurde, haengt nicht daran, ob ein
187
181
  Merkzettel auf der Platte landet.
188
182
  */
189
- console.log(hochZeile(ergebnis.uebernommen));
190
- const merkzettelSchreiben = schreibeMerkzettelDatei(ordner, zuSchreiben);
183
+ console.log(hochZeile(ergebnis.uebernommen, s));
184
+ const merkzettelSchreiben = schreibeMerkzettelDatei(ordner.pfad, zuSchreiben);
191
185
  if (!merkzettelSchreiben.ok) {
192
186
  /*
193
187
  🔴 **ECHTE UMLAUTE -- das liest ein Kunde im Terminal** (Projektregel,
@@ -209,7 +203,7 @@ export async function kontextHoch(argv) {
209
203
  verteilt und ist durch eine `includes`-Pruefung ohnehin nicht erfassbar.
210
204
  *Wer den Satz hier aendert, aendert ihn dort von Hand mit.*
211
205
  */
212
- console.error("Die Firma hat deine Änderungen gespeichert — nur der lokale Merkzettel ließ sich nicht schreiben:");
206
+ console.error(t.savedButTracking);
213
207
  console.error(merkzettelSchreiben.meldung);
214
208
  // B-d (Dennis, 17.09.): Das ist ein ERFOLG -- die Firma hat gespeichert.
215
209
  // Der Rueckgabewert ist fuer Maschinen da, und fuer die ist "gespeichert"
@@ -1,10 +1,14 @@
1
1
  import { zerlege } from "../argumente.js";
2
+ import { sprache } from "../sprache.js";
3
+ import { ALLGEMEIN_TEXTE } from "../texte/allgemein.js";
4
+ import { KONTEXT_TEXTE } from "../texte/kontext.js";
5
+ import { ratNachUnberuehrt } from "../kontext-rat.js";
2
6
  import { ruf } from "../api.js";
3
7
  import { heuteAus } from "../nachricht.js";
4
8
  import { istUnsichererPfad } from "../arbeitsplatz-dateien.js";
5
- import { LEER, VERWALTETE_DATEIEN, alsMerkzettel, sicherungsOrdner, sicherungsZiel, lageAusMerkzetteln, ratNachUnberuehrt, } from "../kontext-stand.js";
9
+ import { LEER, verwalteteDateien, alsMerkzettel, sicherungsOrdner, sicherungsZiel, lageAusMerkzetteln, } from "../kontext-stand.js";
6
10
  import { leseEineDatei, leseMerkzettelDatei, schreibeMerkzettelDatei, schreibeVerwalteteDatei, sichereDatei, } from "../kontext-dateien.js";
7
- import { ERLAUBT, absage, arbeitsplatzOderAbsage } from "./kontext-gemeinsam.js";
11
+ import { ERLAUBT, absage, arbeitsplatzOderAbsage, fremdeSpracheAbsage } from "./kontext-gemeinsam.js";
8
12
  /**
9
13
  * Holt den Firmenkontext von der App und legt ihn in den verwalteten Dateien
10
14
  * des gefundenen Arbeitsplatzes ab -- `CLAUDE.md` und die fuenf Dateien in
@@ -21,7 +25,7 @@ import { ERLAUBT, absage, arbeitsplatzOderAbsage } from "./kontext-gemeinsam.js"
21
25
  * Git benutzt.
22
26
  *
23
27
  * ⛔ **Unterhalb von `werkbank-<name>/` wird nichts geschrieben** (Spec §4).
24
- * Dieser Befehl kennt genau die Pfade aus `VERWALTETE_DATEIEN`, und in keinem
28
+ * Dieser Befehl kennt genau die Pfade aus `verwalteteDateien(sprache)`, und in keinem
25
29
  * davon steht ein Personenname.
26
30
  *
27
31
  * 🔁 **Ruling T9-2:** Die Gegenrichtung, `kontextHoch`, wohnt seit dem Split
@@ -29,25 +33,18 @@ import { ERLAUBT, absage, arbeitsplatzOderAbsage } from "./kontext-gemeinsam.js"
29
33
  * Firmenwahl-Absage und die `--firma`/`--firma-id`-Optionen ueber
30
34
  * `kontext-gemeinsam.ts`.
31
35
  */
32
- const HILFE = `
33
- kimal3 kontext runter [--firma "Name" | --firma-id "…"]
34
-
35
- Holt den Firmenkontext (CLAUDE.md + die fuenf Dateien in kontext/) und legt
36
- ihn in deinem KI-Arbeitsplatz ab. Was du selbst geaendert hast, fasst der
37
- Befehl nicht an -- er sagt dir, wie du es einbringst.
38
-
39
- --firma "Name" Noetig, wenn du zu mehreren Firmen gehoerst.
40
- Die Absage nennt dir die Namen als fertige Befehle.
41
-
42
- --firma-id "…" Fuer den Fall, dass zwei deiner Firmen GLEICH heissen.
43
- Auch diese Absage nennt dir fertige Befehle.
44
- `;
36
+ /** Die Hilfe von `kontext runter` / `context download` in der Sprache des Menschen. */
37
+ export function hilfe(s) {
38
+ return KONTEXT_TEXTE[s].downloadHelp;
39
+ }
45
40
  export async function kontextRunter(argv) {
41
+ let s = sprache();
42
+ let t = KONTEXT_TEXTE[s];
46
43
  if (argv.includes("--help") || argv.includes("-h")) {
47
- console.log(HILFE);
44
+ console.log(hilfe(s));
48
45
  return 0;
49
46
  }
50
- const zerlegt = zerlege(argv, ERLAUBT);
47
+ const zerlegt = zerlege(argv, ERLAUBT, s);
51
48
  if (!zerlegt.ok) {
52
49
  console.error(zerlegt.meldung);
53
50
  return 1;
@@ -66,23 +63,32 @@ export async function kontextRunter(argv) {
66
63
  suche.set("firma", wunsch);
67
64
  if (wunschId !== undefined)
68
65
  suche.set("firma-id", wunschId);
69
- const pfad = suche.toString() ? `/api/cli/kontext?${suche.toString()}` : "/api/cli/kontext";
70
- const antwort = await ruf(pfad, { methode: "GET", mitAusweis: true });
66
+ const pfad = suche.toString() ? `/api/cli/kontext?${suche.toString()}` : "/api/cli/kontext"; // kein-anzeigetext: Pfad der Schnittstelle
67
+ const antwort = await ruf(pfad, { methode: "GET", mitAusweis: true }); // kein-anzeigetext: HTTP-Verb
68
+ // 🌐 Die Antwort kann die Kontosprache umgestellt haben (Spec 3.2, Schritt 3): Was ab hier gedruckt wird, in der neuen.
69
+ s = sprache();
70
+ t = KONTEXT_TEXTE[s];
71
71
  if (antwort.status !== 200) {
72
- return absage(antwort, "kontext runter");
72
+ return absage(antwort, "kontext runter", ""); // kein-anzeigetext: Befehlsschlüssel
73
73
  }
74
74
  // 🔴 Ein `200` mit unlesbarem Rumpf ist KEIN Erfolg -- dasselbe Muster wie
75
75
  // in `firma-daten.ts`. `ruf` verschluckt Parse-Fehler zu `null`.
76
76
  if (antwort.koerper === null || typeof antwort.koerper !== "object") {
77
- console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
77
+ console.error(ALLGEMEIN_TEXTE[s].unreadableAnswer);
78
78
  return 3;
79
79
  }
80
80
  const daten = alsKontextAntwort(antwort.koerper);
81
81
  if (daten === null) {
82
- console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
82
+ console.error(ALLGEMEIN_TEXTE[s].unreadableAnswer);
83
83
  return 3;
84
84
  }
85
- const lokalerMerkzettel = leseMerkzettelDatei(ordner);
85
+ // 🔴 US-13: Die Firma spricht inzwischen eine andere Sprache als dieser Ordner. VOR der
86
+ // Schleife pruefen -- sonst stuenden nach dem Schreiben zwei Sprachen nebeneinander.
87
+ if (daten.sprache !== ordner.sprache) {
88
+ fremdeSpracheAbsage(ordner.sprache, daten.sprache, daten.firma.name);
89
+ return 1;
90
+ }
91
+ const lokalerMerkzettel = leseMerkzettelDatei(ordner.pfad);
86
92
  const heute = heuteAus(new Date());
87
93
  const geschrieben = [];
88
94
  // Punkt A-1: die Teilmenge von `geschrieben`, bei der NICHT die Firma etwas
@@ -92,23 +98,35 @@ export async function kontextRunter(argv) {
92
98
  const vorlageErneuert = [];
93
99
  const gesicherte = [];
94
100
  const unberuehrt = [];
95
- for (const verwalteterPfad of VERWALTETE_DATEIEN) {
101
+ /*
102
+ 🔴 **ERST ALLES PRUEFEN UND LESEN, DANN SCHREIBEN** (Final-Review, Fund 2).
103
+ Bis dahin lief Pruefen und Schreiben in EINER Schleife: Fehlte in der
104
+ Antwort die vierte Datei, waren `CLAUDE.md` und die ersten drei schon
105
+ ueberschrieben, der Merkzettel aber nicht -- ein halber Stand, den das
106
+ naechste `runter` falsch einordnet. Jetzt bricht eine unvollstaendige
107
+ Lieferung (oder eine unlesbare lokale Datei) ab, bevor ein Byte geschrieben ist.
108
+ */
109
+ const vorbereitet = [];
110
+ for (const verwalteterPfad of verwalteteDateien(ordner.sprache)) {
96
111
  const serverDatei = daten.dateien.find((d) => d.pfad === verwalteterPfad);
97
112
  if (!serverDatei) {
98
- console.error(`Die Antwort enthaelt keine Datei „${verwalteterPfad}". Bitte melden.`);
113
+ console.error(t.answerMissingFile(verwalteterPfad));
99
114
  return 3;
100
115
  }
101
116
  // Zweites Netz -- s. Kommentar an `schreibeVerwalteteDatei`: `pfad` kommt
102
117
  // hier aus der eigenen Iteration, nicht roh vom Server, trotzdem geprueft.
103
118
  if (istUnsichererPfad(serverDatei.pfad)) {
104
- console.error(`Die Antwort enthaelt einen unsicheren Pfad: „${serverDatei.pfad}". Bitte melden.`);
119
+ console.error(t.answerUnsafePath(serverDatei.pfad));
105
120
  return 3;
106
121
  }
107
- const lokalGelesen = leseEineDatei(ordner, verwalteterPfad);
122
+ const lokalGelesen = leseEineDatei(ordner.pfad, verwalteterPfad);
108
123
  if (!lokalGelesen.ok) {
109
124
  console.error(lokalGelesen.meldung);
110
125
  return 1;
111
126
  }
127
+ vorbereitet.push({ pfad: verwalteterPfad, serverInhalt: serverDatei.inhalt, lokalerInhalt: lokalGelesen.inhalt });
128
+ }
129
+ for (const { pfad: verwalteterPfad, serverInhalt, lokalerInhalt } of vorbereitet) {
112
130
  /*
113
131
  🔴 **DIE BEIDEN MERKZETTEL GEHEN ALS GANZES HINEIN -- Punkt A-1,
114
132
  2026-08-26.** Hier standen vorher drei Zeilen, die den Eintrag, den
@@ -125,15 +143,15 @@ export async function kontextRunter(argv) {
125
143
  */
126
144
  const lage = lageAusMerkzetteln({
127
145
  pfad: verwalteterPfad,
128
- lokalerInhalt: lokalGelesen.inhalt,
129
- serverInhalt: serverDatei.inhalt,
146
+ lokalerInhalt,
147
+ serverInhalt,
130
148
  lokal: lokalerMerkzettel,
131
149
  vomServer: daten.merkzettel,
132
150
  });
133
151
  if (lage === "beides") {
134
- // `lokalGelesen.inhalt` ist hier garantiert eine Zeichenkette:
152
+ // `lokalerInhalt` ist hier garantiert eine Zeichenkette:
135
153
  // `lageFuerDatei` liefert "beides" nie, wenn `lokalerInhalt === null`.
136
- const sicherung = sichereDatei(ordner, verwalteterPfad, lokalGelesen.inhalt, heute);
154
+ const sicherung = sichereDatei(ordner.pfad, verwalteterPfad, lokalerInhalt, heute, ordner.sprache);
137
155
  if (!sicherung.ok) {
138
156
  console.error(sicherung.meldung);
139
157
  return 1;
@@ -163,7 +181,7 @@ export async function kontextRunter(argv) {
163
181
  vermisst hat, weil ihn niemand erreichen konnte.
164
182
  */
165
183
  if (lage === "server-neuer" || lage === "beides" || lage === "vorlage-neuer") {
166
- const geschriebenErg = schreibeVerwalteteDatei(ordner, verwalteterPfad, serverDatei.inhalt);
184
+ const geschriebenErg = schreibeVerwalteteDatei(ordner.pfad, verwalteterPfad, serverInhalt, ordner.sprache);
167
185
  if (!geschriebenErg.ok) {
168
186
  console.error(geschriebenErg.meldung);
169
187
  return 1;
@@ -200,7 +218,7 @@ export async function kontextRunter(argv) {
200
218
  dieselbe Sorte Fehlalarm wie eine, die nie ankommt.*
201
219
  */
202
220
  const merkzettelZumSchreiben = { firma_id: daten.firma.id, dateien: daten.merkzettel.dateien };
203
- const merkzettelSchreiben = schreibeMerkzettelDatei(ordner, merkzettelZumSchreiben);
221
+ const merkzettelSchreiben = schreibeMerkzettelDatei(ordner.pfad, merkzettelZumSchreiben);
204
222
  if (!merkzettelSchreiben.ok) {
205
223
  console.error(merkzettelSchreiben.meldung);
206
224
  return 1;
@@ -217,49 +235,46 @@ export async function kontextRunter(argv) {
217
235
  // (vor dem Ergebnis) und nennt den Ordner. Die eigentliche Reihenfolge
218
236
  // (erst sichern, dann ueberschreiben) erzwingt schon der Code oben.
219
237
  if (gesicherte.length > 0) {
220
- console.log(`Ich sichere deine lokale Fassung, bevor ich sie ersetze -- Ordner: ${sicherungsOrdner(heute)}`);
238
+ console.log(t.backupHeader(sicherungsOrdner(ordner.sprache, heute)));
221
239
  for (const g of gesicherte) {
222
240
  if (g.nebenVorhandener) {
223
- const bisherige = sicherungsZiel(heute, g.pfad).split("/").pop();
241
+ const bisherige = sicherungsZiel(ordner.sprache, heute, g.pfad).split("/").pop();
224
242
  const neue = g.ziel.split("/").pop();
225
- console.log(` „${g.pfad}": Von heute gibt es schon eine Sicherung (${bisherige}) -- die bleibt ` +
226
- `unveraendert stehen. Deine neuere Fassung sichere ich zusaetzlich als ${neue}, ` +
227
- "bevor ich die Datei im Ordner ersetze.");
243
+ console.log(t.backupAlreadyToday(g.pfad, bisherige, neue));
228
244
  }
229
245
  else {
230
- console.log(` „${g.pfad}" -> ${g.ziel.split("/").pop()}`);
246
+ console.log(t.backupPlain(g.pfad, g.ziel.split("/").pop()));
231
247
  }
232
248
  }
233
249
  }
234
250
  if (geschrieben.length > 0) {
235
- console.log(`✓ aktualisiert: ${geschrieben.join(", ")}`);
251
+ console.log(t.updated(geschrieben.join(", ")));
236
252
  }
237
253
  // 🔴 Punkt A-1: Eine eigene Zeile, kein stilles Mitschreiben. „Aktualisiert"
238
254
  // heisst fuer den Leser sonst automatisch „meine Firma hat etwas gesagt" --
239
255
  // und dann sucht er in der App eine Aenderung, die es dort nicht gibt.
240
256
  if (vorlageErneuert.length > 0) {
241
- console.log(` Davon aus unserer verbesserten Vorlage: ${vorlageErneuert.join(", ")}`);
242
- console.log(" Diese Dateien waren seit dem letzten Abgleich mit der Firma unverändert — es ist nichts\n" +
243
- " von dir verloren gegangen. Hättest du seitdem etwas geändert, hätte ich vorher gesichert.");
257
+ console.log(t.fromOurTemplate(vorlageErneuert.join(", ")));
258
+ console.log(t.templateUnchanged);
244
259
  }
245
260
  // §8: Ein Fehlerfall darf nie denselben Namen tragen wie ein Erfolgsfall --
246
261
  // "nichts geschrieben" heisst hier DREI verschiedene Dinge, und die Ausgabe
247
262
  // muss sie unterscheiden, nicht nur die Rueckgabe (alle drei geben 0).
248
263
  if (unberuehrt.length > 0) {
249
- console.log(`Ich fasse nichts an -- du hast lokal geaendert: ${unberuehrt.join(", ")}`);
264
+ console.log(t.leftAlone(unberuehrt.join(", ")));
250
265
  // 🔴 Der Rat haengt an der DATEI, nicht nur an der Rolle -- s.
251
- // `ratNachUnberuehrt` (kontext-stand.ts), Review-Befund F-3.
252
- for (const zeile of ratNachUnberuehrt(unberuehrt, daten.rolle, daten.adminName)) {
266
+ // `ratNachUnberuehrt` (kontext-rat.ts), Review-Befund F-3.
267
+ for (const zeile of ratNachUnberuehrt(unberuehrt, daten.rolle, daten.adminName, ordner.sprache, s)) {
253
268
  console.log(zeile);
254
269
  }
255
270
  }
256
271
  if (geschrieben.length === 0 && unberuehrt.length === 0) {
257
- const alleLeer = VERWALTETE_DATEIEN.every((p) => (daten.merkzettel.dateien[p]?.stand ?? LEER) === LEER);
272
+ const alleLeer = verwalteteDateien(ordner.sprache).every((p) => (daten.merkzettel.dateien[p]?.stand ?? LEER) === LEER);
258
273
  if (alleLeer) {
259
- console.log("Deine Firma hat noch nichts hinterlegt. Bis dahin bleiben die Vordrucke stehen.");
274
+ console.log(t.companyEmpty);
260
275
  }
261
276
  else {
262
- console.log("Dein Firmenkontext ist aktuell.");
277
+ console.log(t.upToDate);
263
278
  }
264
279
  }
265
280
  return 0;
@@ -277,6 +292,8 @@ function alsKontextAntwort(koerper) {
277
292
  const firmaId = firmaRoh.id;
278
293
  if (typeof firmaId !== "string" || firmaId.trim() === "")
279
294
  return null;
295
+ const firmaNameRoh = firmaRoh.name;
296
+ const firmaName = typeof firmaNameRoh === "string" && firmaNameRoh.trim() !== "" ? firmaNameRoh : null;
280
297
  const rolle = k.rolle;
281
298
  if (rolle !== "admin" && rolle !== "member")
282
299
  return null;
@@ -301,5 +318,7 @@ function alsKontextAntwort(koerper) {
301
318
  const merkzettel = alsMerkzettel(k.merkzettel);
302
319
  if (merkzettel === null)
303
320
  return null;
304
- return { firma: { id: firmaId }, rolle, adminName, dateien, merkzettel };
321
+ // 🌐 Teil 2: die Firmensprache. Fehlt das Feld, antwortet ein Server vor 0.3.0 -- der kennt nur Deutsch.
322
+ const sprache = k.sprache === "en" ? "en" : "de";
323
+ return { firma: { id: firmaId, name: firmaName }, rolle, adminName, dateien, merkzettel, sprache };
305
324
  }