diff --git a/common.php b/common.php index d539292..b5be4fa 100644 --- a/common.php +++ b/common.php @@ -279,10 +279,11 @@ function qdb_app_ui_string_entries(): array { $order = 0; foreach ($catalog['keys'] ?? [] as $key) { $entries[] = [ - 'key' => $key, - 'type' => 'app_string', - 'entityId' => $key, - 'sortOrder' => $order++, + 'key' => $key, + 'displayKey' => $key, + 'type' => 'app_string', + 'entityId' => $key, + 'sortOrder' => $order++, 'germanDefault' => $defaults[$key] ?? $key, ]; } @@ -382,6 +383,24 @@ function qdb_put_translation(PDO $pdo, string $type, string $id, string $lang, s } } +/** Short label for translation UI (Key column); full text stays in `key` for API lookup. */ +function qdb_translation_display_key(array $e): string { + if (!empty($e['displayKey'])) { + return $e['displayKey']; + } + if (($e['type'] ?? '') === 'question' || ($e['type'] ?? '') === 'answer_option') { + $id = $e['entityId'] ?? ''; + $pos = strrpos($id, '__'); + return $pos !== false ? substr($id, $pos + 2) : $id; + } + return $e['key'] ?? ''; +} + +function qdb_short_entity_label(string $entityId): string { + $pos = strrpos($entityId, '__'); + return $pos !== false ? substr($entityId, $pos + 2) : $entityId; +} + function qdb_require_non_empty_german(string $text, string $label = 'German text'): void { if ($text === '') { json_error('MISSING_FIELDS', "$label is required", 400); @@ -407,10 +426,11 @@ function qdb_translation_entry_lists(PDO $pdo, string $qnID): array { foreach ($dbQuestions as $dbQ) { $qOrder = (int)($dbQ['orderIndex'] ?? 0); $contentEntries[] = [ - 'key' => $dbQ['defaultText'], - 'type' => 'question', - 'entityId' => $dbQ['questionID'], - 'sortOrder' => $qOrder * 1000, + 'key' => $dbQ['defaultText'], + 'displayKey' => qdb_short_entity_label($dbQ['questionID']), + 'type' => 'question', + 'entityId' => $dbQ['questionID'], + 'sortOrder' => $qOrder * 1000, ]; $aoStmt = $pdo->prepare(" @@ -420,10 +440,11 @@ function qdb_translation_entry_lists(PDO $pdo, string $qnID): array { $aoStmt->execute([':qid' => $dbQ['questionID']]); foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { $contentEntries[] = [ - 'key' => $ao['defaultText'], - 'type' => 'answer_option', - 'entityId' => $ao['answerOptionID'], - 'sortOrder' => $qOrder * 1000 + 10 + (int)($ao['orderIndex'] ?? 0), + 'key' => $ao['defaultText'], + 'displayKey' => qdb_short_entity_label($ao['answerOptionID']), + 'type' => 'answer_option', + 'entityId' => $ao['answerOptionID'], + 'sortOrder' => $qOrder * 1000 + 10 + (int)($ao['orderIndex'] ?? 0), ]; } @@ -449,10 +470,11 @@ function qdb_translation_entry_lists(PDO $pdo, string $qnID): array { continue; } $stringEntries[] = [ - 'key' => $sk, - 'type' => 'string', - 'entityId' => $sk, - 'sortOrder' => $stringOrder++, + 'key' => $sk, + 'displayKey' => $sk, + 'type' => 'string', + 'entityId' => $sk, + 'sortOrder' => $stringOrder++, ]; } diff --git a/website/js/pages/editor.js b/website/js/pages/editor.js index 7418dec..d867a24 100644 --- a/website/js/pages/editor.js +++ b/website/js/pages/editor.js @@ -1103,12 +1103,13 @@ function bindEditorTransFiltering(entries) { let visibleMissing = 0; root.querySelectorAll('.trans-list-item').forEach(row => { const key = row.dataset.key?.toLowerCase() || ''; + const label = row.dataset.label?.toLowerCase() || ''; const rowType = row.dataset.type || ''; const german = row.querySelector('.trans-source-text')?.textContent?.toLowerCase() || row.querySelector('[data-field="german"]')?.value?.toLowerCase() || ''; const val = row.querySelector('[data-field="translation"]')?.value?.toLowerCase() || ''; const show = (!type || rowType === type) && - (!text || key.includes(text) || german.includes(text) || val.includes(text)); + (!text || key.includes(text) || label.includes(text) || german.includes(text) || val.includes(text)); row.classList.toggle('trans-row-hidden', !show); if (show) { visible++; diff --git a/website/js/pages/translations.js b/website/js/pages/translations.js index e72258c..3a07330 100644 --- a/website/js/pages/translations.js +++ b/website/js/pages/translations.js @@ -350,14 +350,15 @@ function applyFilters() { let visibleMissing = 0; items.forEach(row => { - const key = row.dataset.key?.toLowerCase() + const key = row.dataset.key?.toLowerCase() || ''; + const label = row.dataset.label?.toLowerCase() || row.querySelector('.trans-key-text')?.textContent?.toLowerCase() || ''; const rowType = row.dataset.type || ''; const german = row.querySelector('.trans-source-text')?.textContent?.toLowerCase() || row.querySelector('[data-field="german"]')?.value?.toLowerCase() || ''; const val = row.querySelector('[data-field="translation"]')?.value?.toLowerCase() || ''; const show = (!type || rowType === type) && - (!text || key.includes(text) || german.includes(text) || val.includes(text)); + (!text || key.includes(text) || label.includes(text) || german.includes(text) || val.includes(text)); row.classList.toggle('trans-row-hidden', !show); if (show) { visible++; diff --git a/website/js/translations-helpers.js b/website/js/translations-helpers.js index 33b9544..7ca10aa 100644 --- a/website/js/translations-helpers.js +++ b/website/js/translations-helpers.js @@ -25,6 +25,17 @@ export function germanFor(entry) { return entry.germanText || normalizeTranslations(entry.translations)[SOURCE_LANG] || entry.key || ''; } +/** Key column label (short id for questions/options; catalog key for UI strings). */ +export function entryDisplayKey(entry) { + if (entry.displayKey) return entry.displayKey; + if (entry.type === 'question' || entry.type === 'answer_option') { + const id = entry.entityId || ''; + const sep = id.lastIndexOf('__'); + return sep >= 0 ? id.slice(sep + 2) : id; + } + return entry.key || ''; +} + export function translationFor(entry, lang) { return normalizeTranslations(entry.translations)[lang] || ''; } @@ -188,7 +199,8 @@ export function translationListHeaderHTML(targetLangLabel, sourceLabel = 'German } export function translationRowHTML(entry, idx, { targetLang, editable = true }) { - const key = entry.key || ''; + const lookupKey = entry.key || ''; + const label = entryDisplayKey(entry); const german = germanFor(entry); const val = translationFor(entry, targetLang); const missing = !val.trim(); @@ -201,10 +213,10 @@ export function translationRowHTML(entry, idx, { targetLang, editable = true }) return `