- games.main_version_id, version visibility (draft/published) and kind
(original/translation/mod), version_history; migration 0002
- permissions translations.edit and versions.publish
- "Translate" creates a translation draft; publishing merges the changed
languages into the main version in place (players keep their saves), the
previous manifest goes to the history and can be restored
- translation API: overview with per-state stats, entries with filters,
search and scene context, save, glossary, locale font, PO/CSV export/import
- translation editor page: languages and progress, lines and strings,
filters, autosave, approve/outdated/fuzzy actions, glossary highlighting,
"play from here" with the scene restored
- player: ?lang= and ?at= from the editor, per-locale font
(models.py and permissions.py for this landed earlier in 9ee3e1f)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
3.2 KiB
CSS
80 lines
3.2 KiB
CSS
/* Translation editor */
|
|
.tr-langbar { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-4); }
|
|
.tr-langs { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
|
|
.tr-lang {
|
|
display: inline-flex;
|
|
align-items: baseline;
|
|
gap: 0.45rem;
|
|
padding: 0.4rem 0.8rem;
|
|
border-radius: var(--radius-s);
|
|
border: 1px solid var(--border);
|
|
background: var(--surface-2);
|
|
color: var(--text-2);
|
|
font: inherit;
|
|
cursor: pointer;
|
|
}
|
|
.tr-lang small { color: var(--muted); }
|
|
.tr-lang:hover { border-color: var(--border-strong); color: var(--text); }
|
|
.tr-lang.on { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }
|
|
.tr-tools { flex-wrap: wrap; }
|
|
|
|
.tr-progress { display: flex; flex-direction: column; gap: 0.35rem; }
|
|
.tr-bar { display: flex; height: 8px; border-radius: 99px; overflow: hidden; background: var(--surface-3); }
|
|
.tr-bar > div { height: 100%; }
|
|
.seg-approved { background: var(--ok); }
|
|
.seg-ok { background: color-mix(in srgb, var(--ok) 60%, var(--surface-3)); }
|
|
.seg-warn { background: var(--accent); }
|
|
.seg-error { background: var(--danger); }
|
|
|
|
.tr-tabs { margin-bottom: var(--space-4); }
|
|
.tr-filters { margin-bottom: var(--space-3); flex-wrap: wrap; }
|
|
.tr-filters select { height: 2.9rem; max-width: 18rem; }
|
|
.tr-saving { min-width: 8rem; }
|
|
.tr-saving.error { color: var(--danger); }
|
|
.tr-saving.saved { color: var(--ok); }
|
|
|
|
.tr-list { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
.tr-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: var(--space-4);
|
|
padding: var(--space-3) var(--space-4);
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-left: 3px solid var(--border-strong);
|
|
border-radius: var(--radius-s);
|
|
}
|
|
.tr-row.state-untranslated { border-left-color: var(--border-strong); }
|
|
.tr-row.state-outdated { border-left-color: var(--danger); }
|
|
.tr-row.state-fuzzy, .tr-row.state-machine { border-left-color: var(--accent); }
|
|
.tr-row.state-translated, .tr-row.state-approved { border-left-color: var(--ok); }
|
|
.tr-row:focus-within { border-color: var(--accent); }
|
|
|
|
.tr-meta { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; font-size: 0.85rem; }
|
|
.tr-meta .badge { margin-left: auto; }
|
|
.tr-text { margin: 0.35rem 0; white-space: pre-wrap; line-height: 1.5; }
|
|
.tr-text mark { background: var(--info-soft); color: inherit; border-bottom: 1px dotted var(--info); cursor: help; }
|
|
.tr-row-actions { display: flex; gap: 0.3rem; flex-wrap: wrap; }
|
|
.tr-dst { display: flex; flex-direction: column; gap: 0.35rem; }
|
|
.tr-dst textarea { width: 100%; resize: vertical; min-height: 3.2rem; line-height: 1.5; font: inherit; }
|
|
.tr-dst textarea[readonly] { opacity: 0.8; }
|
|
|
|
.tr-context {
|
|
display: flex;
|
|
gap: var(--space-3);
|
|
margin-top: var(--space-2);
|
|
padding: var(--space-2);
|
|
border-radius: var(--radius-s);
|
|
background: var(--surface-2);
|
|
}
|
|
.tr-context img { width: 180px; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 6px; flex-shrink: 0; }
|
|
.tr-context p { margin: 0.2rem 0; }
|
|
|
|
.tr-glossary input { width: 100%; }
|
|
|
|
@media (max-width: 860px) {
|
|
.tr-row { grid-template-columns: 1fr; gap: var(--space-2); }
|
|
.tr-context { flex-direction: column; }
|
|
.tr-context img { width: 100%; }
|
|
}
|