Tłumaczenia
Tłumacz nazwy/opisy na 18+ języków — z automatycznym widgetem A-E gdy jest grade
Co robi ten tryb
Wgrywasz CSV z IdoSell (nazwa + opisy pol) → Vertex AI Batch Prediction tłumaczy na wybrane języki → wbudowany weryfikator (`weryfikator_tlumaczen.txt`) ocenia jakość 1-10. **Jeśli produkt ma `parameter_id=36890` (grade Bioshi)**, pipeline automatycznie wyciąga widget z polskiego opisu (`extract_widget()`), tłumaczy etykiety przez deterministyczny dict `WIDGET_TRANSLATIONS[lang]` (grade A-E, 4 filary, tagi) i wstrzykuje na początek każdego z 23 języków. Wynik: CSV w formacie IdoSell z gotowymi opisami widget+treść + XLSX archiwum + Sheets tab `weryfikacja`.
Kiedy używać
- Pojedyncze produkty wymagające retłumaczenia (z widgetem jeśli mają grade)
- Wybrane kolumny dla całego katalogu (np. tylko nazwy)
- Aktualizacja tłumaczeń po zmianie polskiego oryginału
- Naprawa polish leak wykrytego przez stróża
- Wgranie widgetu A-E do nowo dodanych języków (re-translate z auto-widget injection)
👤 Kroki wymagające operatora
⚙️ Realny pipeline (bioshi-score-v2)
To są ścieżki do kodu w drugim projekcie na pulpicie — webapp w przyszłości będzie te skrypty triggerował automatycznie zamiast wymagać uruchomienia z `.bat`.
Szczegółowa instrukcja procesu
- 0.1 Auto-detekcja delimitera CSV (`;`, `,` lub tab)
- 0.2 Auto-detekcja kolumn: EAN, nazwa, opis, short_desc
- 0.3 Czyszczenie BOM i whitespace z nazw kolumn
- 0.4 Filtrowanie: pomijanie wierszy bez EAN lub bez wybranych elementów
- 0.5 Test mode: tylko N pierwszych wierszy (domyślnie 20)
- 0.6 XLSX → CSV konwersja (jeśli input to XLSX)
- Plik CSV/XLSX (upload użytkownika)
- Kolumny: `@code_producer`, `/description/name[pol]`, `/description/long_desc[pol]`, `/description/short_desc[pol]`
- Lista produktów w pamięci (EAN, nazwa, krótki opis, długi opis HTML)
- Raport: X produktów, Y kolumn, encoding
- Inny encoding (Windows-1250/ASCII) → fallback UTF-8-sig
- Brak EAN → pipeline stop, DM operatora
- CSV >500MB → wolne parsowanie
input_products.xlsx- 1.1 Load `base_rules.txt` — uniwersalne reguły (jakość, struktura HTML, format JSON)
- 1.2 Per język: load `{lang}.txt`, extract `lang_code` z linii `#CODE:xxx`
- 1.3 Build response_format z wybranymi elementami
- 1.4 Build JSONL: per (język × produkt) = 1 entry
- 1.5 Entry: base_rules + lang_prompt + response_format + product_data
- 1.6 Metadata per entry: {id: EAN, lang: lang_code, lang_file: lang_key}
- Lista produktów z step 0
- Wybrane języki (18 opcji)
- Prompty: `config/translation_prompts/base_rules.txt` + `languages/{lang}.txt` (18 plików)
- Response schema: {name, short_desc, description_html}
- JSONL: `data/temp/batch_translations_{run_id}.jsonl`
- Metadane: `data/temp/batch_job_info_{run_id}.json`
- Prompt file not found (język wyłączony) → error
- JSONL >500MB → Vertex AI submission may fail
- Encoding issue w promptach → force UTF-8
batch_translations_{run_id}.jsonlbatch_job_info_{run_id}.json- 2.1 Upload JSONL → GCS bucket
- 2.2 `aiplatform.init()` z project, location, credentials
- 2.3 `aiplatform.BatchPredictionJob.create()` z `sync=False`
- 2.4 Save job_info JSON: resource_name, gcs_source, gcs_output, bucket, timestamp
- JSONL z step 1
- Service account credentials
- Config (`shared_config.json`): project_id, location='europe-west1', model='publishers/google/models/gemini-2.5-flash'
- GCS bucket: test (`edycja-tresci-tlumaczenia-test`) vs prod (`...-prod`)
- Batch Job Resource Name (projects/.../batchPredictionJobs/...)
- GCS input/output paths
- SA missing permissions (AI Platform, Storage) → auth error
- GCS bucket forbidden
- Model deprecated → Vertex API error
- Quota exceeded → rate limit
batch_job_info_{run_id}.json- 3.1 Co 30s: `job = aiplatform.BatchPredictionJob(resource_name)`
- 3.2 Check `job.state` (1=QUEUED, 2=PENDING, 3=RUNNING, 4=SUCCEEDED, 5=FAILED, 6=CANCELLED)
- 3.3 Logging co 5 min
- 3.4 Terminal: SUCCEEDED → next step; FAILED/CANCELLED → error stop
- 3.5 Timeout: >2h → warning na Slack (non-blocking)
- Job resource_name z step 2
- Polling interval: 30 sec
- Job state: SUCCEEDED
- GCS output dir z prediction results
- Batch timeout >2h (rare) → Slack warning, continue
- Vertex AI infra failure → FAILED state
- Polling network error → retry max 3
- Job stuck in PENDING → operator force cancel via Slack
- 4.1 List blobs w GCS prefix → find `*.jsonl` z 'prediction'
- 4.2 Download i parse każdej linii: {metadata, response}
- 4.3 Extract metadata: {id: EAN, lang: lang_code}
- 4.4 Extract response: `candidate[0].content.parts[0].text`
- 4.5 Clean JSON (remove code fences) i parse
- 4.6 Fallback regex jeśli JSON parse fail
- 4.7 Build results dict per EAN per lang
- 4.8 Validate completeness — missing list (EAN, lang)
- 4.9 If missing AND attempt <2 → retry JSONL → submit batch (step 2-3)
- 4.10 Merge retry results
- GCS output prefix: `gs://bucket/output/{run_id}/`
- Lista oczekiwanych (EAN, lang_code)
- `translation_results_{run_id}.json`: {ean: {lang_code: {name, short_desc, description_html}}}
- Raport: OK / Błędy / Brakujące
- Gemini zwraca non-JSON → fallback regex (może zgubić pola)
- Unicode issues (escaped `\u0105`) → auto-unescape
- Missing translations → auto-retry up to 2×
- Po retry wciąż >10% missing → continue z partial + warning
translation_results_{run_id}.json- 4b.1 Loop per (EAN × lang) z wynikami z step 4
- 4b.2 Jeśli `bioshi_grade is None` → skip (produkt bez score nie dostaje widgetu)
- 4b.3 `extract_scoring_widget(pol_desc)` — DETERMINISTYCZNE — wycina `<style>...Sora...</div>` z polskiego opisu
- 4b.4 `translate_widget(widget_html, lang)` — DETERMINISTYCZNE string.replace() — zamienia 4 grupy etykiet:
- • grade descriptions (5 pełnych zdań A/B/C/D/E z WIDGET_TRANSLATIONS[lang][f'grade_{grade}'])
- • tags wewnątrz `<span>...</span>` (`Krótki skład` → `Kurze Zusammensetzung` itd.)
- • nazwy filarów w `<div>` (`Skład/Pochodzenie/Przetworzenie/Opakowanie` → odpowiedniki)
- • wartości filarów (`Krótki, Zrównoważony, Złożona receptura, Rolnictwo ekologiczne, ...`)
- 4b.5 Wklejenie translated_widget na początek `description_html[lang]` (przed treścią z Gemini)
- 4b.6 Format finalny: `translated_widget + '\n\n<!-- Opis produktu -->\n' + DESC_STYLE + body_lang`
- 4b.7 Lekcje 13k uploadu: dla 5 unsupported langs (lav/ltz/mlt/por/slv) WIDGET_TRANSLATIONS nie ma wpisu — `translate_widget` zwraca polski widget niezmieniony → polish leak → te langi pomijać LUB dorobić dict
- Tłumaczenia z step 4: {ean: {lang_code: {name, short_desc, description_html}}}
- Per-EAN flag `bioshi_grade` (A/B/C/D/E lub None) — z CSV inputu albo z IdoSell `product_parameters` parameter_id=36890
- Polski opis produktu (z inputu) — musi zawierać widget HTML między `<style>...Sora...` a `</div>\n\n</div>`
- `WIDGET_TRANSLATIONS` dict (23 langs × 5 sekcji: bioshi_score, grade_A..E, tags{}, pillars{}, pillar_values{})
- Funkcje z `procesy/edycja-tresci/formatowanie/app/reformat_descriptions.py`: `extract_scoring_widget()`, `translate_widget()`
- Wzbogacone `translation_results`: każdy `description_html` ma na początku widget A-E w odpowiednim języku
- Counter: ile produktów dostało widget, ile pominięto (brak grade), ile pominięto (brak widgetu w polskim)
- Polski opis bez widgetu (`Bioshi Score` brak) → step skipped, widget się nie wstrzykuje (zalogowane jako warning per-EAN)
- Stare widgety z innymi etykietami niż w `WIDGET_TRANSLATIONS` → leak polskich słów (vide audyt step 1)
- Unsupported langs (lav/ltz/mlt/por/slv) — polski widget zostaje → KRYTYCZNE: skip lub dorób dict przed runem
- Produkt ma grade w bazie, ale widget nie był wcześniej wgrany do opisu pol → brak co wyciąć (rzadkie)
translation_results_{run_id}_with_widget.json- 5.1 Build verification JSONL: per (EAN, lang, translated)
- 5.2 Entry: base_rules + verification_prompt + {original_pl, translated, language}
- 5.3 Expected response: {ocena: int(1-10), status: 'OK'|'DO_POPRAWY', problemy: [str]}
- 5.4 Submit batch (jak step 2-3-4)
- 5.5 Auto-retry dla missing (up to 2×)
- 5.6 Aggregate per język
- Tłumaczenia z step 4
- Produkty oryginalne (EAN, nazwa PL, opis PL)
- Prompt: `config/translation_prompts/weryfikator_tlumaczen.txt`
- Verification: {ean: {lang_code: {ocena: 1-10, status: OK/DO_POPRAWY, problemy: []}}}
- Raport per język: count(ocena≥7) OK, count(ocena<7) DO_POPRAWY
- Verifier overly strict → false positives, niskie score mimo dobrych tłumaczeń
- Verification batch timeout → proceed bez (logged warning)
- Średni score <6 per język → Slack flag, operator review
verification_results_{run_id}.json- 6.1 Build CSV header: ['@code_producer'] + per lang ['/description/name[{code}]', '/description/short_desc[{code}]', '/description/long_desc[{code}]']
- 6.2 Build rows: per EAN, append translated values per lang
- 6.3 Empty cell jeśli translation missing
- 6.4 Write CSV → `data/output/final_translations_{run_id}.csv`
- 6.5 CSV → XLSX via openpyxl
- 6.6 Create `archiwum/{run_id}/JSON`
- 6.7 Copy JSON files: batch_job_info, translation_results, verification_results
- 6.8 Generate `info.txt`: run_id, date, mode, produkty, języki, elementy
- Tłumaczenia z step 4
- Lista języków (sorted by lang_code)
- Selected elements: [name, short_desc, long_desc]
- `final_translations_{run_id}.csv` (UTF-8-sig, `;`)
- `final_translations_{run_id}.xlsx`
- `archiwum/{run_id}/` (JSON/, info.txt)
- File permission error na archiwum
- openpyxl wolny dla CSV >100k rows
- Disk space exhausted
final_translations_{run_id}.csvfinal_translations_{run_id}.xlsxinfo.txtJSON/*.json- 7.1 (Optional) Drive: create folder, upload archiwum, get share link
- 7.2 (Optional) Sheets `tlumaczenia`: append CSV columns + data
- 7.3 (Optional) Sheets `weryfikacja`: EAN, język, ocena, status, problemy
- 7.4 (Optional) Slack: '🌍 Tłumaczenia zakończone! #RUN_ID | Produkty: N | Języki: K'
- Archiwum folder
- CSV final_translations
- Config: drive_folder_id, spreadsheet_id, slack_webhook (wszystkie optional)
- Google Drive folder z archiwum (optional)
- Google Sheets: zakładka `tlumaczenia` updated
- Google Sheets: zakładka `weryfikacja` updated
- Slack notification
- Drive/Sheets API error → skip, log warning, continue
- Slack webhook invalid → skip Slack, continue
- Duże CSV (>5MB) wolne dla Sheets append
Diagram przepływu danych
DFDTłumaczenia