369 lines
16 KiB
Markdown
369 lines
16 KiB
Markdown
<!-- status: build -->
|
||
<!-- lastReviewed: 2026-04-10 -->
|
||
|
||
# Statische Texte eliminieren — vollständige i18n-Migration
|
||
|
||
## Fortschritt
|
||
|
||
| Phase | Inhalt | Status |
|
||
|-------|--------|--------|
|
||
| 1 | Feature-Module (`mainXxx.py`), `registry.py`, `interfaceBootstrap.py` | **done** |
|
||
| 2 | `nodeDefinitions/*`, `portTypes.py`, `nodeRegistry.py`, `entryPoints.py` | **done** |
|
||
| 3–5 | Datamodels, `frontendTypes`, Frontend `t()` | offen |
|
||
|
||
**Umsetzung 1–2:** Skript `local/scripts/_flatten_i18n_dicts.py` (mehrsprachige Dicts → deutscher String / `json.dumps`). Anschliessend manuell: `mainSystem.py` AICore-`providerLabels`-Fallback; `portTypes.PortField.description` auf `str`; `_deriveFormPayloadSchema` / `_deriveTransformSchema`; `entryPoints._normalize_title` liefert `str`. **`Role.description`** bleibt `TextMultilingual`: `coerce_text_multilingual()` in `datamodelUtils.py` wandelt Template-Strings/Dicts; Bootstrap- und Feature-`Role(...)`-Aufrufe nutzen `coerce_text_multilingual(...)`.
|
||
|
||
## Problemstellung
|
||
|
||
Trotz Phase 1–7 der i18n-Unification existieren noch **~640+ statische mehrsprachige Dicts** (`{"en":…, "de":…, "fr":…}`) im Gateway und **~60+ Stellen** im Frontend. Diese Dicts:
|
||
|
||
- Müssen bei jeder neuen Sprache manuell erweitert werden
|
||
- Umgehen das zentrale i18n-System (`t()`, Admin-UI, AI-Übersetzung)
|
||
- Sind inkonsistent (manche nur en/de, manche nur en/fr, manche en/de/fr)
|
||
|
||
**Ziel:** Jeder UI-sichtbare Text wird als **deutscher Klartext-Key** gespeichert und zur Laufzeit über `t()` übersetzt. Mehrsprachige Dicts verschwinden komplett.
|
||
|
||
---
|
||
|
||
## Architektur-Entscheidung
|
||
|
||
### Prinzip: Deutscher Klartext = i18n-Key
|
||
|
||
```
|
||
# VORHER (statisch, 3 Sprachen hardcoded):
|
||
"label": {"en": "Documents", "de": "Dokumente", "fr": "Documents"}
|
||
|
||
# NACHHER (dynamisch, beliebig viele Sprachen):
|
||
"label": "Dokumente"
|
||
```
|
||
|
||
Beim Boot registriert `_registerXxxLabels()` den deutschen Text als Key im `xx`-Basisset. `t("Dokumente")` liefert zur Laufzeit die Übersetzung in der aktuellen Sprache.
|
||
|
||
### Sonderfälle
|
||
|
||
| Fall | Lösung |
|
||
|------|--------|
|
||
| `outputLabels` (Listen von Strings pro Sprache) | Jedes Element einzeln als Key: `"Ja"`, `"Nein"` |
|
||
| `_ISO_LABELS` (Sprachnamen) | Bleiben statisch — sind ISO-Referenzdaten, keine UI-Texte |
|
||
| Locale-Code-Maps (`"de": "de-DE"`) | Bleiben statisch — technische Mappings |
|
||
| `BUILTIN_PLANS` (Subscription) | Werden zu Keys, AI übersetzt |
|
||
| Datamodel `frontend_options` | Labels werden zu Keys, Boot-Registrierung |
|
||
|
||
---
|
||
|
||
## Datei-Index (vollständig)
|
||
|
||
### Gateway — Feature-Module (Kategorie A: RBAC/Katalog-Labels)
|
||
|
||
| # | Datei | Stellen | Muster | Phase |
|
||
|---|-------|---------|--------|-------|
|
||
| A1 | `features/trustee/mainTrustee.py` | ~62 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `UI_OBJECTS`, `TEMPLATE_ROLES`, Workflow-Gruppen, Rollen-Beschreibungen | 1 |
|
||
| A2 | `system/mainSystem.py` | ~27 | `DATA_OBJECTS`, LLM-Provider-Labels | 1 |
|
||
| A3 | `features/commcoach/mainCommcoach.py` | ~17 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` | 1 |
|
||
| A4 | `features/teamsbot/mainTeamsbot.py` | ~11 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` | 1 |
|
||
| A5 | `features/workspace/mainWorkspace.py` | ~10 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` | 1 |
|
||
| A6 | `features/chatbot/mainChatbot.py` | ~7 | `DATA_OBJECTS`, `TEMPLATE_ROLES` | 1 |
|
||
| A7 | `features/neutralization/mainNeutralization.py` | ~7 | `DATA_OBJECTS`, `RESOURCE_OBJECTS` | 1 |
|
||
| A8 | `features/realEstate/mainRealEstate.py` | ~6 | `DATA_OBJECTS`, `RESOURCE_OBJECTS` | 1 |
|
||
| A9 | `features/graphicalEditor/mainGraphicalEditor.py` | ~6 | Permissions, Rollen-Beschreibungen | 1 |
|
||
| A10 | `serviceCenter/registry.py` | ~14 | Service-Kategorie-Labels | 1 |
|
||
| A11 | `interfaces/interfaceBootstrap.py` | ~4 | Template-Rollen-Beschreibungen (admin/user/viewer/sysadmin) | 1 |
|
||
|
||
**Summe Kategorie A: ~171 Stellen, 11 Dateien**
|
||
|
||
### Gateway — Graph-Editor Node-Definitionen (Kategorie B)
|
||
|
||
| # | Datei | Stellen | Muster | Phase |
|
||
|---|-------|---------|--------|-------|
|
||
| B1 | `graphicalEditor/nodeDefinitions/clickup.py` | ~50 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B2 | `graphicalEditor/nodeDefinitions/input.py` | ~31 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B3 | `graphicalEditor/portTypes.py` | ~33 | Port-Feld-Descriptions | 2 |
|
||
| B4 | `graphicalEditor/nodeDefinitions/sharepoint.py` | ~27 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B5 | `graphicalEditor/nodeDefinitions/email.py` | ~27 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B6 | `graphicalEditor/nodeDefinitions/ai.py` | ~23 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B7 | `graphicalEditor/nodeDefinitions/trustee.py` | ~20 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B8 | `graphicalEditor/nodeDefinitions/flow.py` | ~14 | Node-Label, `outputLabels` (Sonderfall: Liste) | 2 |
|
||
| B9 | `graphicalEditor/nodeDefinitions/data.py` | ~9 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B10 | `graphicalEditor/nodeDefinitions/triggers.py` | ~8 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B11 | `graphicalEditor/nodeDefinitions/file.py` | ~7 | Node-Label, Parameter-Descriptions | 2 |
|
||
| B12 | `graphicalEditor/nodeRegistry.py` | ~10 | Kategorie-Labels | 2 |
|
||
| B13 | `graphicalEditor/entryPoints.py` | ~3 | Entry-Point-Titel | 2 |
|
||
|
||
**Summe Kategorie B: ~262 Stellen, 13 Dateien**
|
||
|
||
### Gateway — Datamodel-Options (Kategorie C)
|
||
|
||
| # | Datei | Stellen | Muster | Phase |
|
||
|---|-------|---------|--------|-------|
|
||
| C1 | `datamodels/datamodelRbac.py` | ~19 | Scope/Access-Level Option-Labels (en/de/fr) | 3 |
|
||
| C2 | `datamodels/datamodelChat.py` | ~7 | Status/Mode Option-Labels (en/fr, kein de!) | 3 |
|
||
| C3 | `datamodels/datamodelMessaging.py` | ~11 | Channel/Status Option-Labels (en/fr) | 3 |
|
||
| C4 | `datamodels/datamodelNotification.py` | ~8 | Type/Status Option-Labels (en/de) | 3 |
|
||
| C5 | `datamodels/datamodelUam.py` | ~7 | Subscription-Status + Sprach-Options (en/de/fr) | 3 |
|
||
| C6 | `datamodels/datamodelSubscription.py` | ~8 | Plan-Titel + Descriptions (en/de/fr, teils ohne fr) | 3 |
|
||
| C7 | `datamodels/datamodelFiles.py` | ~4 | Scope Option-Labels (en/de) | 3 |
|
||
| C8 | `datamodels/datamodelDataSource.py` | ~4 | Scope Option-Labels (en/de) | 3 |
|
||
| C9 | `datamodels/datamodelFeatureDataSource.py` | ~4 | Scope Option-Labels (en/de) | 3 |
|
||
| C10 | `datamodels/datamodelUiLanguage.py` | ~3 | Sync-Status Option-Labels (de/en) | 3 |
|
||
| C11 | `features/trustee/datamodelFeatureTrustee.py` | ~13 | Währung + Dokumenttyp Option-Labels | 3 |
|
||
| C12 | `features/neutralization/datamodelFeatureNeutralizer.py` | ~4 | Scope Option-Labels | 3 |
|
||
|
||
**Summe Kategorie C: ~92 Stellen, 12 Dateien**
|
||
|
||
### Gateway — Sonstige (Kategorie D)
|
||
|
||
| # | Datei | Stellen | Muster | Phase |
|
||
|---|-------|---------|--------|-------|
|
||
| D1 | `shared/frontendTypes.py` | ~14 | `CUSTOM_TYPE_DESCRIPTIONS` (aktuell ungenutzt) | 4 |
|
||
| D2 | `features/trustee/accounting/connectors/accountingConnectorBexio.py` | ~4 | Connector-Label + Feld-Labels | 4 |
|
||
| D3 | `features/trustee/accounting/connectors/accountingConnectorRma.py` | ~4 | Connector-Label + Feld-Labels | 4 |
|
||
| D4 | `features/trustee/accounting/connectors/accountingConnectorAbacus.py` | ~5 | Connector-Label + Feld-Labels | 4 |
|
||
|
||
**Summe Kategorie D: ~27 Stellen, 4 Dateien**
|
||
|
||
### Frontend (Kategorie E)
|
||
|
||
| # | Datei | Stellen | Muster | Phase |
|
||
|---|-------|---------|--------|-------|
|
||
| E1 | `pages/Store.tsx` | ~5 | `FEATURE_DESCRIPTIONS` Dict (de/en/fr) | 5 |
|
||
| E2 | `types/mandate.ts` | ~45 | Statische Navigation-Labels (de/en) | 5 |
|
||
| E3 | `pages/views/trustee/TrusteeAbschlussView.tsx` | ~6 | Tile-Titel/Descriptions (de/en/fr) | 5 |
|
||
| E4 | `pages/views/trustee/TrusteeAnalyseView.tsx` | ~4 | Tile-Descriptions (de/en) | 5 |
|
||
| E5 | `components/UnifiedDataBar/UnifiedDataBar.tsx` | ~3 | `_TAB_LABELS` (de/en/fr) | 5 |
|
||
| E6 | `api/featuresApi.ts` | ~4 | Mock-Labels (de/en) | 5 |
|
||
| E7 | Diverse (12 Dateien) | ~12 | Hardcoded deutsche Strings ohne `t()` | 5 |
|
||
|
||
**Summe Kategorie E: ~79 Stellen, ~18 Dateien**
|
||
|
||
### Ausnahmen (bleiben statisch)
|
||
|
||
| Datei | Grund |
|
||
|-------|-------|
|
||
| `routes/routeI18n.py` (`_ISO_LABELS`) | ISO-Referenzdaten (Sprachnamen), kein UI-Text |
|
||
| `serviceAgent/coreTools/_mediaTools.py` | Locale-Code-Map (`"de"→"de-DE"`), technisch |
|
||
| `serviceAgent/conversationManager.py` | Sprach-Name-Map für Agent-Kontext, technisch |
|
||
|
||
---
|
||
|
||
## Gesamtübersicht
|
||
|
||
| Kategorie | Dateien | Stellen | Komplexität |
|
||
|-----------|---------|---------|-------------|
|
||
| A: Feature-Module | 11 | ~171 | Mittel — uniformes Muster, braucht Boot-Registrierung |
|
||
| B: Node-Definitionen | 13 | ~262 | Mittel — uniformes Muster, braucht eigene Registrierung |
|
||
| C: Datamodel-Options | 12 | ~92 | Hoch — verschiedene Patterns, braucht generische Lösung |
|
||
| D: Sonstige Gateway | 4 | ~27 | Niedrig — einfache Ersetzung |
|
||
| E: Frontend | ~18 | ~79 | Niedrig — `t()` wrappen |
|
||
| **Total** | **~58** | **~631** | |
|
||
|
||
---
|
||
|
||
## Umsetzungsplan (5 Phasen)
|
||
|
||
### Phase 1: Feature-Module (Kategorie A) — Composer-geeignet
|
||
|
||
**Aufwand:** Mittel | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
|
||
|
||
**Vorbereitung (einmalig, Opus):**
|
||
- `_registerRbacLabels()` in `i18nRegistry.py` erweitern: neben `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` auch scannen:
|
||
- `UI_OBJECTS[].label`
|
||
- Workflow-Gruppen-Labels (`TRUSTEE_WORKFLOW_GROUPS`, `TRUSTEE_SERVICE_CATEGORIES`)
|
||
- Rollen-`description` Blöcke
|
||
- Service-Kategorie-Labels (`serviceCenter/registry.py`)
|
||
- Bootstrap-Rollen (`interfaceBootstrap.py`)
|
||
|
||
**Transformation (pro Datei, Composer):**
|
||
Jedes `"label": {"en": "X", "de": "Y", "fr": "Z"}` wird zu `"label": "Y"` (deutscher Text).
|
||
Jedes `"description": {"en": "X", "de": "Y", "fr": "Z"}` wird zu `"description": "Y"`.
|
||
|
||
**Regel für Composer:**
|
||
```
|
||
In der Datei [DATEI]:
|
||
- Ersetze jedes Dict {"en": "...", "de": "DEUTSCH", "fr": "..."} durch den deutschen Wert "DEUTSCH"
|
||
- Ersetze jedes Dict {"de": "DEUTSCH", "en": "...", "fr": "..."} durch "DEUTSCH"
|
||
- Wenn kein "de" vorhanden: nimm "en" Wert
|
||
- Lasse alle anderen Felder unverändert
|
||
```
|
||
|
||
**Dateien:** A1–A11 (11 Dateien)
|
||
|
||
**Akzeptanzkriterien:**
|
||
- [ ] Kein `"fr":` mehr in den 11 Dateien (ausser Ausnahmen)
|
||
- [ ] `_registerRbacLabels()` registriert alle neuen Key-Kategorien
|
||
- [ ] Gateway startet fehlerfrei
|
||
- [ ] Admin-UI Sprachen-Seite zeigt neue Keys im xx-Set
|
||
|
||
---
|
||
|
||
### Phase 2: Node-Definitionen (Kategorie B) — Composer-geeignet
|
||
|
||
**Aufwand:** Mittel | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
|
||
|
||
**Vorbereitung (einmalig, Opus):**
|
||
- Neue Funktion `_registerNodeLabels()` in `i18nRegistry.py`:
|
||
- Scannt `STATIC_NODE_TYPES` aus `nodeDefinitions/__init__.py`
|
||
- Registriert `label`, `description`, `parameters[].description`, `outputLabels[]` als Keys
|
||
- Context: `node.label`, `node.desc`, `node.param`, `node.output`
|
||
- Scannt `portTypes.PORT_TYPE_CATALOG` für Port-Feld-Descriptions
|
||
- Context: `port.desc`
|
||
- Scannt `nodeRegistry` Kategorie-Labels
|
||
- Context: `node.category`
|
||
- Scannt `entryPoints` Titel
|
||
- Context: `node.entry`
|
||
|
||
**Transformation (pro Datei, Composer):**
|
||
Identisches Muster wie Phase 1: Dict → deutscher String.
|
||
|
||
**Sonderfall `flow.py` `outputLabels`:**
|
||
```python
|
||
# VORHER:
|
||
"outputLabels": {"en": ["Yes", "No"], "de": ["Ja", "Nein"], "fr": ["Oui", "Non"]}
|
||
|
||
# NACHHER:
|
||
"outputLabels": ["Ja", "Nein"]
|
||
```
|
||
|
||
**Dateien:** B1–B13 (13 Dateien)
|
||
|
||
**Akzeptanzkriterien:**
|
||
- [ ] Kein `"fr":` mehr in den 13 Dateien
|
||
- [ ] `_registerNodeLabels()` registriert alle Node-Keys
|
||
- [ ] Graph-Editor zeigt Nodes korrekt an (Labels übersetzt)
|
||
- [ ] Node-Parameter-Descriptions im Config-Panel korrekt
|
||
|
||
---
|
||
|
||
### Phase 3: Datamodel-Options (Kategorie C) — Opus nötig
|
||
|
||
**Aufwand:** Hoch | **Modell:** Opus | **Risiko:** Mittel
|
||
|
||
**Warum Opus:** Die Patterns sind uneinheitlich (en/fr, en/de, en/de/fr), die `frontend_options` Struktur variiert, und die Boot-Registrierung muss generisch über alle Datamodels funktionieren.
|
||
|
||
**Vorbereitung:**
|
||
- Neue generische Funktion `_registerDatamodelOptionLabels()` in `i18nRegistry.py`:
|
||
- Scannt alle Pydantic-Modelle mit `json_schema_extra` → `frontend_options`
|
||
- Extrahiert `label` Dicts und registriert den deutschen (oder englischen) Text als Key
|
||
- Context: `option.{ModelName}.{fieldName}`
|
||
- `BUILTIN_PLANS` in `datamodelSubscription.py`: `title`/`description` zu Keys
|
||
|
||
**Transformation (pro Datei):**
|
||
```python
|
||
# VORHER:
|
||
{"value": "active", "label": {"en": "Active", "de": "Aktiv", "fr": "Actif"}}
|
||
|
||
# NACHHER:
|
||
{"value": "active", "label": "Aktiv"}
|
||
```
|
||
|
||
**Problem: Fehlende Sprachen.** Einige Dicts haben kein `"de"` (z.B. `datamodelChat.py` nur en/fr). Hier wird `"en"` als Fallback genommen und der englische Text als Key registriert — die AI-Übersetzung erzeugt dann `de` und `fr`.
|
||
|
||
**Dateien:** C1–C12 (12 Dateien)
|
||
|
||
**Akzeptanzkriterien:**
|
||
- [ ] Kein `"fr":` / `"en":` Dict-Pattern mehr in den 12 Dateien
|
||
- [ ] `_registerDatamodelOptionLabels()` registriert alle Option-Keys
|
||
- [ ] Frontend Select-Felder zeigen korrekte Labels
|
||
- [ ] Subscription-Plan-Texte korrekt übersetzt
|
||
|
||
---
|
||
|
||
### Phase 4: Sonstige Gateway (Kategorie D) — Composer-geeignet
|
||
|
||
**Aufwand:** Niedrig | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
|
||
|
||
**Transformation:**
|
||
- `frontendTypes.py`: `CUSTOM_TYPE_DESCRIPTIONS` komplett entfernen (ungenutzt) + `getCustomTypeDescription()` und `registerCustomType()` vereinfachen oder entfernen
|
||
- Accounting-Connectors: `displayName()` und Feld-Labels zu deutschen Strings
|
||
|
||
**Dateien:** D1–D4 (4 Dateien)
|
||
|
||
**Akzeptanzkriterien:**
|
||
- [ ] `CUSTOM_TYPE_DESCRIPTIONS` entfernt
|
||
- [ ] Accounting-Connector-Labels als deutsche Strings
|
||
- [ ] Kein `"fr":` mehr in den 4 Dateien
|
||
|
||
---
|
||
|
||
### Phase 5: Frontend (Kategorie E) — Composer-geeignet
|
||
|
||
**Aufwand:** Niedrig | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
|
||
|
||
**Transformation:**
|
||
- Alle `{"en": "X", "de": "Y", "fr": "Z"}` Dicts durch `t("Y")` ersetzen
|
||
- Alle hardcodierten deutschen Strings in JSX-Attributen mit `t()` wrappen
|
||
- `mandate.ts` Navigation-Labels: zu `t()`-Keys (Backend liefert bereits i18n-Keys)
|
||
- `_TAB_LABELS` in `UnifiedDataBar.tsx`: zu `t()`-Aufrufen
|
||
- `FEATURE_DESCRIPTIONS` in `Store.tsx`: zu `t()`-Keys
|
||
|
||
**Dateien:** E1–E7 (~18 Dateien)
|
||
|
||
**Akzeptanzkriterien:**
|
||
- [ ] Kein statisches `de:`/`en:`/`fr:` Dict-Pattern im Frontend
|
||
- [ ] Alle UI-Texte via `t()` getaggt
|
||
- [ ] Sprache wechseln → alle Texte ändern sich
|
||
|
||
---
|
||
|
||
## Composer-Anweisungen (Copy-Paste-fertig)
|
||
|
||
### Für Phase 1 + 2 + 4 (uniforme Dict→String Ersetzung):
|
||
|
||
```
|
||
Aufgabe: Ersetze in [DATEI] alle statischen mehrsprachigen Dicts durch den deutschen Klartext-String.
|
||
|
||
Regeln:
|
||
1. {"en": "...", "de": "DEUTSCH", "fr": "..."} → "DEUTSCH"
|
||
2. {"de": "DEUTSCH", "en": "...", "fr": "..."} → "DEUTSCH"
|
||
3. {"en": "ENGLISH", "fr": "..."} (kein "de") → "ENGLISH"
|
||
4. {"en": "ENGLISH", "de": "DEUTSCH"} (kein "fr") → "DEUTSCH"
|
||
5. Listen-Sonderfall: {"en": [...], "de": [LISTE], "fr": [...]} → [LISTE]
|
||
6. Nur "label", "description", "title", "displayName" Felder ändern
|
||
7. Keine Imports, Funktionssignaturen oder Logik ändern
|
||
8. Keine Kommentare hinzufügen
|
||
```
|
||
|
||
### Für Phase 5 (Frontend t()-Wrapping):
|
||
|
||
```
|
||
Aufgabe: Ersetze in [DATEI] alle statischen Texte durch t()-Aufrufe.
|
||
|
||
Regeln:
|
||
1. {"en": "...", "de": "DEUTSCH", "fr": "..."} → t("DEUTSCH")
|
||
2. Hardcoded string "DEUTSCH" in label/title/placeholder/aria-label → t("DEUTSCH")
|
||
3. Import { useLanguage } from '...LanguageContext' hinzufügen falls fehlend
|
||
4. const { t } = useLanguage(); in der Komponente falls fehlend
|
||
5. Keine Logik ändern, nur Texte wrappen
|
||
```
|
||
|
||
---
|
||
|
||
## Reihenfolge und Abhängigkeiten
|
||
|
||
```
|
||
Phase 1 ──→ Phase 2 ──→ Phase 3 ──→ Phase 4
|
||
(A) (B) (C) (D)
|
||
│
|
||
▼
|
||
Phase 5
|
||
(E)
|
||
```
|
||
|
||
- Phase 1 muss zuerst: erweitert `_registerRbacLabels()` als Grundlage
|
||
- Phase 2 nach 1: braucht das Pattern von Phase 1
|
||
- Phase 3 nach 2: komplexeste Phase, braucht neue generische Registrierung
|
||
- Phase 4 + 5: unabhängig voneinander, nach Phase 1
|
||
|
||
---
|
||
|
||
## Aufwandsschätzung
|
||
|
||
| Phase | Modell | Dateien | Geschätzte Zeit |
|
||
|-------|--------|---------|-----------------|
|
||
| 1 (Vorbereitung) | Opus | 1 | 15 min |
|
||
| 1 (Transformation) | Composer | 11 | 30 min |
|
||
| 2 (Vorbereitung) | Opus | 1 | 15 min |
|
||
| 2 (Transformation) | Composer | 13 | 30 min |
|
||
| 3 | Opus | 13 | 45 min |
|
||
| 4 | Composer | 4 | 10 min |
|
||
| 5 | Composer | 18 | 30 min |
|
||
| **Total** | | **~58** | **~3 Stunden** |
|