Files
RUWIO/src/i18n/messages.ts
T
thandClaude Opus 4.7 5af0cdb3d4 0.18.1: editor-hide cascades to groups + rename for layers & clip tracks
Track groups get the same editor-hide affordance as layer and clip
tracks: new eye button on the group header, setGroupEditorHidden
action (clears selection pointing inside the group), and a cascade
through the Konva stackItems + render.ts so every child track in a
hidden group vanishes in the editor.

Track renaming via right-click context menu now covers layers and
clip tracks too — setLayerName / setClipTrackName actions, new i18n
strings, and end-to-end UI tests driving the menu → promptDialog →
rename flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:55:22 +02:00

1766 lines
78 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export const en = {
app: {
undo: 'Undo (Ctrl+Z)',
redo: 'Redo (Ctrl+Shift+Z)',
jumpToStart: 'Jump to start',
pause: 'Pause (Space)',
play: 'Play (Space)',
stop: 'Stop',
recentProjects: 'Recent projects',
saveProject: 'Save project (Ctrl/Cmd+S)',
saveProjectDirty: 'Save project (unsaved changes, Ctrl/Cmd+S)',
documentSettings: 'Document settings',
export: 'Export',
mainTimeline: 'Main timeline',
dragResize: 'Drag to resize timeline',
loadFailed: 'Failed to load project.',
savedToast: '"{name}" saved',
saveDialog: {
title: 'Save project',
message: 'What should the project be called?',
confirm: 'Save',
defaultName: 'Project {date}',
},
renameProject: 'Rename project',
renameProjectHelp:
'Opens the document drawer where you can rename the project. Saved projects update in place; unsaved names apply at the next save.',
renameProjectDialog: {
title: 'Rename project',
message: 'Choose a new name for this project.',
confirm: 'Rename',
},
untitledProject: 'Untitled project',
saveProjectDirtyHelp:
'Your project has unsaved changes. Ctrl/Cmd+S writes them to browser storage.',
exportHelp:
'Save as runtime script, HTML, or video. MP4 needs the render server; WebM records in real time in the browser.',
replaceDialog: {
title: 'Replace project?',
message:
'The current project will be replaced by the loaded file. Unsaved changes will be lost.',
confirm: 'Continue',
},
},
welcome: {
heading: 'Welcome to Animatr',
subheading: 'Recent projects',
loading: 'Loading…',
empty: 'No saved projects yet.',
layerCount: '{n} layers',
today: 'today {time}',
deleteTooltip: 'Delete project',
newProject: 'New project',
importFile: 'Import file…',
exportCurrent: 'Export current',
deleteDialog: {
title: 'Delete project?',
message: '"{name}" will be permanently removed from browser storage.',
confirm: 'Delete',
},
},
sidebar: {
addFile: 'Add image or audio',
library: 'Library',
images: 'Images',
thumbnailsHide: 'Hide thumbnails',
thumbnailsShow: 'Show thumbnails',
thumbsToggle: 'Thumbs',
imageLabel: 'Image',
addLayerTooltip: 'Insert as layer (or drag to stage)',
noImages: 'No images imported yet',
clips: 'Clips',
createClipTooltip: 'Create new clip',
clipLayerCount: '{n} layers',
renameTooltip: 'Rename',
deleteTooltip: 'Delete',
noClips: 'No clips yet',
shape: {
rect: 'Rect',
circle: 'Circle',
text: 'Text',
polygon: 'Polygon',
freeform: 'Freeform (click to draw, Enter to finish, Esc to cancel)',
freeformActive: 'Drawing… (Enter to finish, Esc to cancel)',
button: 'Button (5-frame asset)',
},
buttons: 'Buttons',
noButtons: 'No buttons yet',
audio: 'Audio',
noAudio: 'No audio imported yet',
deleteAudioDialog: {
title: 'Delete audio?',
message: 'Remove "{name}" from the asset library. Any clips on the timeline that reference it will also be removed.',
confirm: 'Delete',
},
createButtonTooltip: 'Create new button asset',
hitModeDisabled: 'Switch back to button states to add assets.',
newButtonDialog: {
title: 'New button',
nameMessage: 'Button name:',
defaultName: 'Button',
next: 'Next',
sizeMessage: 'Canvas size (width × height):',
confirm: 'Create',
},
renameButtonDialog: {
title: 'Rename button',
confirm: 'Apply',
},
deleteButtonDialog: {
title: 'Delete button?',
message: 'The button asset will be removed. Any layers that reference it will no longer render.',
confirm: 'Delete',
},
newTextDialog: {
title: 'New text object',
message: 'Text:',
default: 'Hello',
confirm: 'Insert',
fallback: 'Text',
},
newPolygonDialog: {
title: 'New polygon',
message: 'Number of points (3–24):',
confirm: 'Create',
},
newClipDialog: {
title: 'New clip',
message: 'Clip name:',
default: 'Clip',
confirm: 'Create',
},
renameClipDialog: {
title: 'Rename clip',
confirm: 'Apply',
},
deleteClipDialog: {
title: 'Delete clip?',
message: 'The clip and all placed instances will be removed.',
confirm: 'Delete',
},
},
properties: {
keyframeLabel: 'Keyframe',
interpolated: 'Interpolated',
removeKeyframe: 'Remove keyframe here',
deleteLayer: 'Delete layer',
noLayer: 'No layer selected.',
lockedLayer:
'Selected layer is locked. Unlock the track (or its group) to edit properties.',
fill: 'Fill',
transparent: 'Transparent',
opacity: 'Opacity',
edgeBlur: 'Edge blur',
sections: {
transform: 'Transform',
text: 'Text',
polygon: 'Polygon',
appearance: 'Appearance',
button: 'On Click',
},
polygonPoints: 'Points',
polygonPointsHint: 'Changing the point count rewrites all keyframes; new points are inserted at the midpoint of the last edge.',
polygonClosed: 'Closed path (fill)',
stroke: 'Stroke',
strokeWidth: 'Stroke width',
shadow: 'Shadow',
shadowBlur: 'Shadow blur',
shadowOpacity: 'Shadow opacity',
shadowAngle: 'Shadow angle',
shadowDistance: 'Shadow distance',
shadowColor: 'Shadow color',
mask: 'Mask',
maskUse: 'Use as mask',
maskMode: 'Mask mode',
maskPositive: 'Positive',
maskNegative: 'Negative',
maskHint: 'Masks everything below it in the same group.',
maskActive: 'Mask is active — affects layers below it in its group.',
maskNeedsGroup: 'Add this layer to a track group to activate masking. Right-click a track and choose "Add group".',
},
fullscreen: {
pause: 'Pause',
play: 'Play',
jumpToStart: 'Jump to start',
close: 'Close (Esc)',
},
timeline: {
colors: {
none: 'None',
red: 'Red',
orange: 'Orange',
yellow: 'Yellow',
green: 'Green',
cyan: 'Cyan',
blue: 'Blue',
purple: 'Purple',
pink: 'Pink',
gray: 'Gray',
},
noTracks: 'No tracks yet.',
addClipTrack: 'Add clip track',
addClipTrackTooltip: 'Add another clip track',
dropNewTrackHint: 'Drop a clip here to add a new track',
addGroup: 'Add group',
addGroupTooltip: 'Create a new track group',
scriptTrackName: 'Script track',
addScriptTrackTooltip: 'Create a new script track',
scriptFrameTooltip: 'Script frame — {n} action(s)',
buttonEditLabel: 'Editing button:',
buttonEditExit: 'Exit',
emptyButtonState: 'Empty state — drop shapes or images from the sidebar.',
buttonModeStates: 'Edit button states (normal / hover / pressed / disabled)',
buttonModeStatesShort: 'States',
buttonModeHit: 'Edit the click hit area',
buttonModeHitShort: 'Hit area',
hitAreaHeader: 'Hit area',
hitAreaHint: 'Click the canvas tools to add shapes',
hitAreaEmpty: 'No hit shapes yet — add a rect or circle from the canvas.',
hitTrackDelete: 'Delete hit shape',
addKeyframeTooltip: 'Add keyframe for the selected track at the playhead',
groupChildCount: '{n} tracks',
groupEmpty: 'Drop tracks here',
renameGroupDialog: {
title: 'Rename group',
confirm: 'Apply',
},
renameScriptTrackDialog: {
title: 'Rename script track',
confirm: 'Apply',
},
renameAudioTrackDialog: {
title: 'Rename audio track',
confirm: 'Apply',
},
renameLayerDialog: {
title: 'Rename layer',
confirm: 'Apply',
},
renameClipTrackDialog: {
title: 'Rename clip track',
confirm: 'Apply',
},
removeAudioClipDialog: {
title: 'Remove audio clip?',
message: 'Remove this audio clip from the timeline.',
confirm: 'Remove',
},
audioTrackName: 'Audio track',
addAudioTrackTooltip: 'Create a new audio track',
dropNewAudioTrackHint: 'Drop an audio file here to add a track',
audioMuted: 'Muted — click to unmute',
muteAudioTrack: 'Mute track',
renameAudioTrack: 'Rename audio track',
renameLayer: 'Rename layer',
renameClipTrack: 'Rename clip track',
ungroup: 'Ungroup',
groupSelected: 'Group {n} selected layers',
convertToClip: 'Convert {n} selected to clip',
renameGroup: 'Rename group',
renameScriptTrack: 'Rename script track',
resizeTrackColumn: 'Drag to resize track name column',
clipTrackName: 'Clip track',
toggleTimeUnit: 'Toggle time unit',
snapOn: 'Snapping on (playhead & keyframes snap together)',
snapOff: 'Snapping off',
hideLocked: 'Hide locked tracks',
showLocked: 'Show locked tracks',
inPoint: 'In point — drag to set where playback and export start',
outPoint: 'Out point — drag to set where playback and export end',
renameMarkerTitle: 'Rename marker',
renameMarkerMessage: 'Pick a unique name — used as a jump target in scripts.',
renameMarkerConfirm: 'Rename',
renameMarkerCollision: 'A marker named "{name}" already exists.',
renameMarkerRetry: 'Try again',
stillFrameTooltip: 'Still frame {time} — holds until the next keyframe',
keyframeTooltip: 'Keyframe {time} — drag to move, right-click for options',
addKfFromPrev: 'Fill with nearest keyframe — copies the previous state\'s pose into this slot',
trimTooltip: '{name} · {duration}s{trims} · Drag edges to trim, double-click to open',
trimStart: 'Start -{value}s',
trimEnd: 'End -{value}s',
easing: 'Easing',
easingLinear: 'Linear',
easingEaseIn: 'Ease In',
easingEaseOut: 'Ease Out',
easingEaseInOut: 'Ease In-Out',
firstKfHint: 'First keyframe — easing has no effect',
stillFrame: 'Still frame',
duplicate: 'Duplicate',
delete: 'Delete',
color: 'Color',
lock: 'Lock',
unlock: 'Unlock',
hideInEditor: 'Hide in editor (still exported)',
showInEditor: 'Show in editor',
deleteLayer: 'Delete layer',
deleteClipTrack: 'Delete clip track',
deleteScriptTrack: 'Delete script track',
deleteAudioTrack: 'Delete audio track',
separate: 'Separate Position and Rotation',
rotation: 'Rotation',
position: 'Position',
splitSvg: 'Split SVG into paths',
removeInstanceDialog: {
title: 'Remove clip instance?',
message: 'Remove "{name}" at {time}s from the timeline.',
generic: 'Remove this clip instance from the timeline.',
confirm: 'Remove',
},
},
canvas: {
zoomIn: 'Zoom in',
zoomOut: 'Zoom out',
zoomOneToOne: 'Zoom 1:1',
fitToView: 'Fit to view',
cropOn: 'Show stage only (crop)',
cropOff: 'Show everything',
maskPreviewOn: 'Preview masks (render view)',
maskPreviewOff: 'Back to edit view',
stagePreviewOn: 'Turn stage preview on — buttons become interactive',
stagePreviewOff: 'Turn stage preview off — buttons behave as normal assets',
vertexCorner: 'Corner',
vertexCurve: 'Curve (smooth)',
vertexSplitCurve: 'Split curve',
hit: {
addRect: 'Add rect hit area',
addCircle: 'Add circle hit area',
addPolygon: 'Add polygon hit area',
remove: 'Remove selected hit area',
},
menu: {
documentSettings: 'Document settings',
stageResize: 'Resize stage',
},
resize: {
banner: 'Resizing stage',
hint: 'Drag a handle to resize — press Esc or click Done.',
done: 'Done',
},
},
settings: {
title: 'Settings',
tab: {
document: 'Document',
clipart: 'Media',
interface: 'Interface',
theme: 'UI Theme',
},
timing: 'Timing',
languageTitle: 'Language',
languageLabel: 'App language',
languageDesc:
'Choose the language for the interface and the help screen. The setting is saved in the browser.',
languageEnglish: 'English',
languageGerman: 'Deutsch',
appearanceTitle: 'Appearance',
tooltipsLabel: 'Show tooltips',
tooltipsDesc:
'Floating help text when you hover over a button or control. Turn off if you know the app well and find them distracting — screen readers still get the label.',
showLockedLabel: 'Show locked tracks',
showLockedDesc:
'When off, locked tracks disappear from the timeline and the stage. Use to focus on the layers you\'re still editing. Also togglable from the timeline toolbar.',
assetThumbsLabel: 'Asset thumbnails',
assetThumbsDesc:
'Render small previews in the Assets / Clips / Buttons panels. Turn off for a compact list when working with lots of items.',
bgVideoLabel: 'Video',
bgVideoNone: 'No background video',
bgVideoPickFile: 'Pick video file…',
bgVideoPickUrl: 'Set URL…',
bgVideoClear: 'Remove background video',
bgVideoLocalFile: 'Local video file',
bgVideoHint:
'The video plays behind all layers and tracks the playhead. For best results use MP4 (H.264). The source is stored with the project (data URL for local files) or referenced by URL. In the export the video is not embedded — the exported HTML references the same URL.',
bgVideoStageSizeTitle: 'Apply video dimensions?',
bgVideoStageSizeMessage:
'The video is {w}×{h}. Your canvas is currently {cw}×{ch}. Resize the canvas to match the video?',
bgVideoStageSizeConfirm: 'Resize canvas',
bgVideoStageSizeCancel: 'Keep current size',
bgVideoDurationTitle: 'Extend scene duration?',
bgVideoDurationMessage:
'The video is {d}s. Scene duration is {cd}s. Extend the scene to match the video length?',
bgVideoDurationConfirm: 'Extend duration',
bgVideoDurationCancel: 'Keep current duration',
bgVideoUrlTitle: 'Background video URL',
bgVideoUrlMessage: 'Paste the video URL (must be reachable by the browser):',
bgVideoUrlConfirm: 'Load',
bgVideoButton: 'Background video',
bgVideoPicker: 'Click to choose a video · right-click for URL / clear',
presetsLabel: 'Presets',
presetAddTitle: 'Add preset',
presetAddMessage: 'Enter dimensions as WxH (e.g. 1920x1080).',
presetAddPlaceholder: '1920x1080',
presetAddConfirm: 'Add',
presetAddInvalid: 'Please enter dimensions like 1920x1080.',
presetAddTooltip: 'Add preset',
presetRemoveTitle: 'Remove preset?',
presetRemoveMessage: 'Remove "{label}" from your presets?',
presetRemoveConfirm: 'Remove',
canvas: 'Canvas',
width: 'Width',
height: 'Height',
background: 'Background',
backgroundTransparent: 'Transparent',
backgroundTransparentTooltip: 'Transparent background (APNG exports keep alpha)',
duration: 'Duration (s)',
fps: 'FPS',
timelineHint: 'Timeline length sets the ruler scale. Playback stops at the last keyframe.',
themeHint: 'Saved in the browser and reused on next open.',
themeDark: 'Dark',
themeLight: 'Light',
done: 'Done',
},
export: {
title: 'Export',
runtimeBadge: '1× per page',
runtimeDesc: 'Engine — include once; all scenes on the page share the runtime',
sceneBadge: 'Scene',
sceneDesc: 'Scene data only — requires the runtime to be loaded',
exampleTitle: 'Example HTML',
exampleDesc: 'Standalone HTML — just open it in a browser',
mp4Badge: 'Server · Frame-accurate',
mp4Desc: 'H.264, any resolution — requires render server',
webmBadge: 'Browser · Realtime',
webmDesc: 'No server — recorded in real time',
webmNotSupported: 'Browser does not support MediaRecorder',
gifBadge: 'Browser · Frame-by-frame',
gifDesc: '256-colour palette, broadest compatibility',
apngBadge: 'Browser · Lossless',
apngDesc: 'Full RGBA with transparency — .png file',
apngPreviewTitle: 'APNG preview page',
apngPreviewBadge: 'HTML · Checkerboard',
apngPreviewDesc: 'Standalone HTML with a checkerboard backdrop so you can verify transparency',
cancel: 'Cancel',
failed: 'Export failed',
back: 'Back',
labelWebm: 'Rendering WebM (browser, realtime)…',
labelMp4: 'Rendering MP4 (server)…',
labelGif: 'Rendering GIF (browser, frame-by-frame)…',
labelApng: 'Rendering APNG (browser, frame-by-frame)…',
labelApngPreview: 'Rendering APNG + preview page…',
skipHidden: 'Skip hidden tracks',
skipHiddenDesc: 'Drop layers toggled off in the editor from every format below.',
skipHiddenNone: 'No editor-hidden tracks in the scene.',
},
tests: {
running: 'Tests running',
title: 'Test Runner',
runAgain: 'Run again',
close: 'Close',
copyTooltip: 'Copy report to clipboard',
copiedTooltip: 'Report copied',
copiedBanner: 'Error report copied to clipboard.',
copyFailedBanner: 'Copy failed — use the clipboard icon to trigger manually.',
emptyState: 'No results — click "Run again" or press {shortcut}.',
testsCountLabel: '{n} tests',
passedLabel: '{n} passed',
failedLabel: '{n} failed',
},
clipart: {
title: 'Media',
open: 'Open media',
close: 'Close media',
refresh: 'Reload folder',
empty: 'Add a folder with images and audio to populate the library.',
noFiles: 'No images or audio files in this folder.',
loading: 'Loading…',
unsupported: 'Your browser does not support folder access. Try Chrome or Edge.',
chooseFolder: 'Choose folder…',
clearFolder: 'Clear',
folderLabel: 'Folder',
noFolder: 'No folder selected',
addFolder: 'Add folder',
addFolderTooltip: 'Add another media folder',
backToIndex: 'Back to folder index',
removeFolder: 'Remove folder from library',
removeFolderDialog: {
title: 'Remove folder from library?',
message: '"{name}" will no longer show up in the Media drawer. The files themselves stay untouched on disk.',
confirm: 'Remove',
},
settingsHint:
'Add one or more local folders with your images and audio files. The app remembers them; permissions may be re-asked after browser restart.',
},
documentDrawer: {
open: 'Open document settings',
close: 'Close document settings',
projectName: 'Project',
},
scenes: {
title: 'Scenes',
open: 'Open scenes',
close: 'Close scenes',
add: 'Add scene',
rename: 'Rename scene',
renameDialog: {
title: 'Rename scene',
confirm: 'Rename',
},
duplicate: 'Duplicate',
delete: 'Delete',
deleteDialog: {
title: 'Delete scene?',
message: 'Remove "{name}" from this project. This cannot be undone.',
confirm: 'Delete',
},
count: '{n} scenes | {n} scene | {n} scenes',
locked: 'A project must have at least one scene.',
},
dialog: {
confirm: 'Confirm',
input: 'Input',
ok: 'OK',
cancel: 'Cancel',
close: 'Close',
},
about: {
openTooltip: 'About RUWIO',
title: 'About {name}',
tagline: 'A timeline-based animation editor that runs in your browser.',
versionLabel: 'Version',
buildLabel: 'Build',
close: 'Close',
credit: 'Made with love by Torsten Hoeltge',
shortcutsHeading: 'Shortcuts',
shortcut: {
undo: 'Ctrl+Z',
redo: 'Ctrl+Shift+Z',
playPause: 'Space',
fullscreen: 'Shift+Space',
tests: 'Ctrl+Shift+Alt+T',
panCanvas: 'Middle-click / Ctrl+Drag',
},
shortcutLabel: {
undo: 'Undo',
redo: 'Redo',
playPause: 'Play / Pause',
fullscreen: 'Fullscreen preview',
tests: 'Open test runner',
panCanvas: 'Pan canvas',
},
},
actions: {
empty: 'No actions yet. Add one below.',
add: 'Add action',
remove: 'Remove',
moveUp: 'Move up',
moveDown: 'Move down',
noMarkers: 'No markers defined',
noScenes: 'No other scenes defined',
noButtonLayers: 'No button layers defined',
type: {
play: 'Play',
pause: 'Pause',
rewindToLoop: 'Rewind to loop start',
jumpTo: 'Jump to time',
jumpToFrame: 'Jump to frame',
jumpToMarker: 'Jump to marker',
jumpToScene: 'Jump to scene',
setButtonEnabled: 'Enable / disable button',
script: 'Run script',
},
param: {
time: 'Seconds',
frame: 'Frame',
marker: 'Marker',
scene: 'Scene',
buttonLayer: 'Button',
enabled: 'Enabled',
source: 'Script source',
},
},
scriptFrameDialog: {
title: 'Script frame',
time: 'Time (s)',
label: 'Label (optional)',
labelPlaceholder: 'e.g. intro-end',
remove: 'Delete frame',
done: 'Done',
},
help: {
openTooltip: 'Help',
title: 'RUWIO: Help',
tocLabel: 'Help chapters',
chapters: {
gettingStarted: 'Getting started',
stage: 'Stage',
assets: 'Assets library',
layersTimeline: 'Layers & timeline',
clips: 'Clips',
buttons: 'Buttons',
scenes: 'Scenes',
audio: 'Audio',
markers: 'Markers',
scriptsActions: 'Script frames & actions',
scriptLanguage: 'Script language',
export: 'Preview & export',
shortcuts: 'Keyboard shortcuts',
},
shortcut: {
playPause: 'Play / Pause',
fullscreen: 'Fullscreen preview',
undo: 'Undo',
redo: 'Redo',
save: 'Save project',
inPoint: 'Set in-point at playhead',
outPoint: 'Set out-point at playhead',
rewind: 'Rewind to start',
addMarker: 'Add marker at playhead',
deleteSelected: 'Delete selected keyframe / marker / layer',
clearSelection: 'Clear selection / close menu',
panCanvas: 'Pan canvas',
zoomCanvas: 'Zoom canvas',
pasteImage: 'Paste image from clipboard',
tests: 'Open test runner',
},
table: {
syntax: 'Syntax',
effect: 'Effect',
},
content: {
gettingStarted: {
intro:
'RUWIO is a timeline editor that runs in your browser. You add assets, animate them with keyframes, preview everything, and export the result as runtime, HTML, or video.',
modelTitle: 'Mental model',
modelBody:
'A <strong>project</strong> can contain one or more <strong>scenes</strong>. All scenes share the same canvas size, duration, and fps. Inside each scene you place <strong>layers</strong> on the stage. Layers point to reusable <strong>assets</strong> like images, shapes, buttons, clips, or audio.',
whereTitle: 'Where things are',
where: {
topBar: {
label: 'Top bar',
body:
'Transport controls, undo and redo, open and save, settings, and export live here. The logo opens the About dialog. The <strong>?</strong> button opens this help.',
},
assets: {
label: 'Assets sidebar',
body:
'This is your shared library for images, clips, buttons, and audio. Use the <strong>+</strong> buttons to create things quickly, then drag them onto the active scene.',
},
stage: {
label: 'Stage',
body:
'This is the live canvas. Click layers to select them, drag them around, and use the handles to transform them. In preview mode, buttons react to hover and click.',
},
properties: {
label: 'Properties panel',
body:
'This panel shows the settings for the selected layer: position, rotation, opacity, fill, masking, button wiring, clip trims, and more.',
},
timeline: {
label: 'Timeline',
body:
'This is where keyframes, clip instances, script frames, and audio clips live. You can resize it by dragging the top edge.',
},
scenes: {
label: 'Scenes drawer',
body:
'Open the drawer on the right to switch scenes, create new ones, duplicate them, or clean them up.',
},
},
quickStartTitle: 'First animation: quick start',
quickStart: {
step1: 'Click <strong>+</strong> next to a shape in the Assets sidebar, for example rect, circle, text, or polygon.',
step2: 'Drag the new shape on the stage to place it.',
step3: 'Move the playhead, drag the shape again, and RUWIO creates a new keyframe for you.',
step4: 'Press <kbd>Space</kbd> to play it back.',
},
},
stage: {
intro:
'The stage shows the active scene at the current playhead position. In edit mode it behaves like a canvas with handles and selection. In fullscreen preview it behaves like the exported runtime: buttons can hover, press, and fire actions.',
viewTitle: 'View controls',
view: {
zoom:
'<strong>Zoom:</strong> Use <kbd>Ctrl/Cmd</kbd> plus mouse wheel, or the zoom controls at the bottom-right corner of the stage.',
pan:
'<strong>Pan:</strong> Middle-drag, or hold <kbd>Ctrl</kbd> while dragging.',
fit:
'<strong>Fit:</strong> Use the Fit button to bring the whole canvas back into view.',
},
selectionTitle: 'Selection',
selection: {
pick:
'Click a layer to select it. Use <kbd>Shift</kbd> plus click to add or remove layers from the selection.',
clear: 'Click empty canvas space to clear the selection.',
handles:
'With one layer selected, the transformer handles appear. Drag them to scale or rotate.',
},
lockingTitle: 'Locking and editor-hide',
lockingBody:
'Right-click a track in the timeline to lock it or hide it in the editor. <strong>Locked</strong> layers stay visible but cannot be edited on the stage. <strong>Editor-hidden</strong> layers disappear from the editor, but they still render in preview and export unless you explicitly skip them.',
drawingTitle: 'Drawing polygons',
drawingBody:
'Start a polygon from the Assets sidebar and the stage switches into drawing mode. Click to place points, press <kbd>Enter</kbd> to finish, or <kbd>Escape</kbd> to cancel.',
},
assets: {
intro:
'Assets are templates. Layers are placements. If you delete a layer, the asset stays in the library so you can use it again later. If you delete an asset, all layers that reference it go away too.',
sectionsTitle: 'What you can keep there',
sections: {
shapes:
'<strong>Shapes:</strong> Quick buttons for rect, circle, text, and polygon. One click creates the asset and places a layer on the active scene.',
images:
'<strong>Images:</strong> Import them with the upload button or paste them with <kbd>Ctrl/Cmd+V</kbd>. The Media drawer can browse local folders without adding every file to the project right away.',
clips:
'<strong>Clips:</strong> Reusable mini-timelines you can place again and again.',
buttons:
'<strong>Buttons:</strong> Interactive assets with states and click actions.',
audio:
'<strong>Audio:</strong> Shared audio assets that you place on per-scene audio tracks.',
},
thumbsTitle: 'Thumbs toggle',
thumbsBody:
'The <em>Thumbs</em> switch shows or hides previews in the asset lists. Turn it off if you want a tighter, faster list. Turn it on if you prefer visual scanning.',
foldTitle: 'Folding sections',
foldBody:
'Each section header can be folded. That makes it easier to focus when the library gets crowded.',
},
layersTimeline: {
intro:
'The timeline stacks tracks vertically and arranges time from left to right. That is where you scrub, retime, group things, and wire clips, scripts, and audio together.',
keyframesTitle: 'Keyframes',
keyframes: {
add: 'Click an empty spot on a layer lane to add a keyframe at that time.',
drag: 'Drag a keyframe horizontally to retime it.',
jump: 'Double-click a keyframe to move the playhead right onto it.',
menu:
'Right-click a keyframe for options like easing, still-frame, duplicate, and delete.',
},
holdsTitle: 'Still-frames',
holdsBody:
'A held keyframe freezes the current value until the next keyframe. That is handy when you want a snap instead of an interpolation.',
separateTitle: 'Separate position and rotation',
separateBody:
'By default, a keyframe stores all animatable values together. Use <em>Separate</em> on a track if you want position and rotation on their own sub-tracks so you can retime them independently.',
groupsTitle: 'Groups',
groupsBody:
'Select several tracks, right-click, and choose <em>Group selected</em>. Groups can be collapsed, coloured, locked, and reordered as one unit.',
snappingTitle: 'Snapping',
snappingBody:
'While you drag keyframes or clips, the timeline can snap to nearby times and markers. Use the magnet toggle when you want free movement instead.',
},
clips: {
intro:
'A <strong>clip</strong> is a reusable group of layers with its own local timeline. A placed clip becomes a <strong>clip instance</strong> on the main timeline.',
createTitle: 'Create a clip',
create: {
step1: 'Select one or more layers in the timeline.',
step2: 'Right-click and choose <em>Convert to clip</em>.',
step3: 'RUWIO creates the clip asset and swaps the original layers for a clip instance on a clip track.',
},
editTitle: 'Edit clip internals',
editBody:
'Double-click a clip instance or click the clip in the Assets sidebar. The editor switches into clip scope, so everything you add there belongs to the clip, not the scene.',
trimTitle: 'Trim instances',
trimBody:
'Drag the left or right edge of a clip instance to trim what portion of the internal animation is visible. Other instances of the same clip keep their own trims.',
transformTitle: 'Transform on stage',
transformBody:
'A clip instance can also have its own transform on the stage. That means you can reuse the same clip animation in different positions, scales, or rotations.',
},
buttons: {
intro:
'Buttons are interactive assets with four visual states: <code>normal</code>, <code>hover</code>, <code>pressed</code>, and <code>disabled</code>. They can also carry hit shapes and onClick actions.',
editingTitle: 'Edit a button',
editingBody:
'Click a button asset in the sidebar to enter button-edit mode. The stage and timeline now show the button internals. Use the state switcher to jump between <em>normal</em>, <em>hover</em>, <em>pressed</em>, and <em>disabled</em>.',
stateKeyframesTitle: 'Per-state keyframes',
stateKeyframesBody:
'Buttons use four fixed time slots, one per state. Empty slots show a <strong>+</strong> button. Clicking it seeds a keyframe from the nearest existing state so you can keep moving quickly.',
stillFramesTitle: 'Still-frames inside buttons',
stillFramesBody:
'A held keyframe only shows on that button state. A normal keyframe can interpolate across states. Mixing both approaches is great for glows, outlines, or tiny one-state extras.',
hitShapesTitle: 'Hit shapes',
hitShapesBody:
'Switch to <em>Hit</em> mode when you want to draw the clickable area. You can combine rects, circles, and polygons. If you do not define any hit shape, the whole button canvas is clickable.',
actionsTitle: 'On click actions',
actionsBody:
'With a button layer selected, open the <em>On Click</em> section in the Properties panel. Actions run in order when the press starts and ends inside the hit area.',
disabledTitle: 'Disabled state',
disabledBody:
'Buttons can start disabled, and scripts or actions can toggle that later with <code>setButtonEnabled</code>.',
},
scenes: {
intro:
'Scenes are like pages or chapters. They all share the project canvas, fps, duration, and asset library, but each scene has its own layers, markers, audio, and scripts.',
drawerTitle: 'Scene drawer',
drawerBody:
'Use the drawer on the right to switch scenes, add a new one, duplicate one, rename one, or delete one.',
switchingTitle: 'Switching behaviour',
switchingBody:
'Switching from the drawer is regular navigation: the playhead stays where it is. Jumping with <code>jumpToScene</code> feels more like a transition: the target scene rewinds to the project in-point and starts playing from there.',
referencesTitle: 'Cross-scene references',
referencesBody:
'Scene actions use stable scene ids. That means renaming a scene does not break existing jumps.',
},
audio: {
intro:
'RUWIO separates <strong>audio assets</strong> from <strong>audio clips</strong>. Assets live in the shared library. Clips are placements on scene-specific audio tracks.',
addTitle: 'Add audio',
add: {
step1: 'Open the Audio section in the Assets sidebar and click the upload button.',
step2: 'Pick one or more mp3 files. Each file becomes an audio asset.',
step3: 'Drag an audio asset onto the timeline, or drop it into empty space to create a track automatically.',
},
tracksTitle: 'Audio tracks',
tracksBody:
'Each audio track can hold multiple clips. Right-click a track to rename, mute, colour, lock, or delete it.',
movingTitle: 'Move audio clips',
movingBody:
'Drag a clip left or right to retime it. Drag it to another audio track to move it. If clips would overlap on the same track, RUWIO pushes them forward instead of stacking them.',
waveformsTitle: 'Waveforms',
waveformsBody:
'Waveforms are decoded in the background. Right after import, a clip may look flat for a moment before the waveform appears.',
urlsTitle: 'Data URLs and external URLs',
urlsBody:
'Files picked locally are stored as data URLs so editing works offline. For production exports, it is better to point audio to a normal <code>https://</code> URL.',
},
markers: {
intro:
'Markers are named times on the ruler. They are useful for navigation, syncing, and script jumps like <code>jump @intro</code>.',
editingTitle: 'Add and edit markers',
editing: {
add: 'Press <kbd>M</kbd> to drop a marker at the playhead.',
drag: 'Drag a marker horizontally to retime it.',
rename: 'Click a marker to select it, then double-click to rename it.',
delete:
'Selected markers turn orange. Press <kbd>Delete</kbd> or <kbd>Backspace</kbd> to remove them.',
clear: 'Click outside a selected marker to clear the selection.',
},
sceneTitle: 'Per scene',
sceneBody:
'Markers belong to a scene, not the whole project. If a script tries to jump to a missing marker, the runtime logs a warning.',
},
scriptsActions: {
intro:
'A <strong>script frame</strong> is a timeline event that fires one or more actions when playback crosses it. Script frames live on dedicated script tracks.',
addTitle: 'Add a script frame',
add: {
step1: 'Click an empty spot on a script track. The script-frame dialog opens right away.',
step2: 'Set the time, optionally give it a label, and add the actions you want.',
step3: 'Click <em>Done</em>. The frame appears as a dot on the track.',
},
firingTitle: 'When actions fire',
firing: {
forward:
'Only forward playback crossings fire actions. Scrubbing and direct playhead jumps do not.',
exact:
'A script exactly at the current playhead still fires on the first tick after you press <em>Play</em>.',
order:
'If multiple script frames land in the same tick window, they fire in time order.',
replay:
'If you press play again from the same position, frames at that exact position can fire again.',
},
menuTitle: 'Right-click menu',
menuBody:
'Right-click a script frame to duplicate it or delete it. Duplicate copies the label and action list into the next free slot.',
typesTitle: 'Available action types',
types: {
transport:
'<code>play</code>, <code>pause</code>, <code>rewindToLoop</code>: transport control.',
jump:
'<code>jumpTo</code>, <code>jumpToFrame</code>, <code>jumpToMarker</code>: move the playhead without stopping.',
scene:
'<code>jumpToScene</code>: switch scenes and rewind to the in-point.',
button:
'<code>setButtonEnabled</code>: enable or disable a specific button layer.',
script:
'<code>script</code>: run a small line-based DSL. That is explained in the next chapter.',
},
},
scriptLanguage: {
intro:
'The <code>script</code> action accepts a small line-based DSL. The editor, preview, and exported runtime all use the same parser, so behaviour stays consistent.',
syntaxTitle: 'Syntax rules',
syntax: {
onePerLine: 'Write one command per line. Empty lines are ignored.',
comments: 'Everything after <code>//</code> on a line counts as a comment.',
tokens:
'Tokens are split by whitespace. The first token is the command name.',
case: 'Commands are case-insensitive.',
},
commandsTitle: 'Commands',
commands: {
play: {
syntax: '<code>play</code>',
effect: 'Starts or resumes playback.',
},
pause: {
syntax: '<code>pause</code>',
effect: 'Stops playback and leaves the playhead where it is.',
},
rewind: {
syntax: '<code>rewind</code>',
effect:
'Moves the playhead to the playback in-point: project start or the current <em>I</em> marker.',
},
jumpSeconds: {
syntax: '<code>jump &lt;seconds&gt;</code>',
effect:
'Jumps to a floating-point time value, for example <code>jump 2.5</code>.',
},
jumpFrame: {
syntax: '<code>jump #&lt;frame&gt;</code>',
effect:
'Jumps to a frame index at the current scene fps, for example <code>jump #60</code>.',
},
jumpMarker: {
syntax: '<code>jump @&lt;marker&gt;</code>',
effect:
'Jumps to a named marker on the active scene, for example <code>jump @intro</code>.',
},
},
exampleTitle: 'Example',
exampleBody: `// Loop the intro, then wait for a click.
jump @intro-start
pause // wait for a button or another script frame
play
// A later script frame can send playback back:
// jump @intro-start`,
errorsTitle: 'Errors',
errorsBody:
'Unknown commands, missing arguments, and invalid jump targets show up in the browser console as <code>[ruwio] Script line N: message</code>. One broken line does not cancel the others.',
notInDslTitle: 'What the DSL does not cover',
notInDslBody:
'The DSL is intentionally small. It does not handle <code>jumpToScene</code>, <code>setButtonEnabled</code>, or nested <code>script</code> blocks. Use the structured action editor for those.',
},
export: {
intro:
'You can preview inside the editor, test the interactive runtime in fullscreen, or export the project once everything feels right.',
editorPreviewTitle: 'In-editor preview',
editorPreviewBody:
'The regular stage preview plays the active scene without interactive button hover or click behaviour. Press <kbd>Space</kbd> to play or pause.',
fullscreenPreviewTitle: 'Fullscreen preview',
fullscreenPreviewBody:
'Press <kbd>Shift</kbd>+<kbd>Space</kbd> to open the runtime-style fullscreen preview. Buttons work there, audio plays, and scene jumps are live. Press <kbd>Escape</kbd> to close it.',
formatsTitle: 'Export formats',
formats: {
runtime:
'<strong>ruwio-runtime.js:</strong> The runtime engine. Load it once on a page and mount projects into it.',
scene:
'<strong>animation.js:</strong> Project data only. It expects the runtime to already be loaded.',
html:
'<strong>HTML example:</strong> A quick standalone page with everything wired up.',
mp4:
'<strong>MP4 video:</strong> Frame-accurate server render. This needs the render server configured in Settings.',
webm:
'<strong>WebM video:</strong> Browser-side realtime recording. This depends on <code>MediaRecorder</code> support.',
},
skipHiddenTitle: 'Skip hidden layers',
skipHiddenBody:
'If you turn on <em>Skip editor-hidden layers</em>, layers hidden in the editor are removed from the export output. That is useful for drafts, guides, or temporary work.',
assetsTitle: 'Assets and URLs',
assetsBody:
'Images and audio stored as data URLs stay embedded in exports. Background videos and external media URLs stay linked, so the exported project still needs those files to be reachable.',
},
shortcuts: {
intro:
'Most shortcuts pause automatically while a text input, textarea, or number field has focus, so typing stays safe.',
},
},
},
} as const
type Widen<T> =
T extends string ? string
: T extends object ? { [K in keyof T]: Widen<T[K]> }
: T
export type Messages = Widen<typeof en>
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
export const de: DeepPartial<Messages> = {
app: {
undo: 'Rückgängig (Ctrl+Z)',
redo: 'Wiederholen (Ctrl+Shift+Z)',
jumpToStart: 'Zum Anfang springen',
pause: 'Pause (Leertaste)',
play: 'Abspielen (Leertaste)',
stop: 'Stop',
recentProjects: 'Letzte Projekte',
saveProject: 'Projekt speichern (Ctrl/Cmd+S)',
saveProjectDirty: 'Projekt speichern (ungespeicherte Änderungen, Ctrl/Cmd+S)',
documentSettings: 'Dokumenteinstellungen',
export: 'Export',
mainTimeline: 'Haupt-Timeline',
dragResize: 'Ziehen, um die Timeline zu vergrößern oder zu verkleinern',
loadFailed: 'Projekt konnte nicht geladen werden.',
savedToast: '"{name}" gespeichert',
saveDialog: {
title: 'Projekt speichern',
message: 'Wie soll das Projekt heißen?',
confirm: 'Speichern',
defaultName: 'Projekt {date}',
},
renameProject: 'Projekt umbenennen',
renameProjectHelp:
'Öffnet die Dokument-Schublade, in der das Projekt umbenannt werden kann. Gespeicherte Projekte werden direkt aktualisiert, ungespeicherte Namen gelten beim nächsten Speichern.',
renameProjectDialog: {
title: 'Projekt umbenennen',
message: 'Wähle einen neuen Namen für dieses Projekt.',
confirm: 'Umbenennen',
},
untitledProject: 'Unbenanntes Projekt',
saveProjectDirtyHelp:
'Dein Projekt hat ungespeicherte Änderungen. Mit Ctrl/Cmd+S wird es im Browser gespeichert.',
exportHelp:
'Als Runtime, HTML oder Video exportieren. MP4 braucht den Render-Server. WebM wird direkt im Browser in Echtzeit aufgenommen.',
replaceDialog: {
title: 'Projekt ersetzen?',
message:
'Das aktuelle Projekt wird durch die geladene Datei ersetzt. Ungespeicherte Änderungen gehen verloren.',
confirm: 'Weiter',
},
},
welcome: {
heading: 'Willkommen bei Animatr',
subheading: 'Letzte Projekte',
loading: 'Lädt…',
empty: 'Noch keine gespeicherten Projekte.',
layerCount: '{n} Ebenen',
today: 'heute {time}',
deleteTooltip: 'Projekt löschen',
newProject: 'Neues Projekt',
importFile: 'Datei importieren…',
exportCurrent: 'Aktuelles exportieren',
deleteDialog: {
title: 'Projekt löschen?',
message: '"{name}" wird dauerhaft aus dem Browser-Speicher entfernt.',
confirm: 'Löschen',
},
},
sidebar: {
addFile: 'Bild oder Audio hinzufügen',
library: 'Bibliothek',
images: 'Bilder',
thumbnailsHide: 'Vorschaubilder ausblenden',
thumbnailsShow: 'Vorschaubilder anzeigen',
thumbsToggle: 'Thumbs',
imageLabel: 'Bild',
addLayerTooltip: 'Als Ebene einfügen oder auf die Bühne ziehen',
noImages: 'Noch keine Bilder importiert',
clips: 'Clips',
createClipTooltip: 'Neuen Clip anlegen',
clipLayerCount: '{n} Ebenen',
renameTooltip: 'Umbenennen',
deleteTooltip: 'Löschen',
noClips: 'Noch keine Clips',
shape: {
rect: 'Rechteck',
circle: 'Kreis',
text: 'Text',
polygon: 'Polygon',
freeform: 'Freiform: Klicken zum Zeichnen, Enter zum Beenden, Esc zum Abbrechen',
freeformActive: 'Zeichnen läuft: Enter beendet, Esc bricht ab',
button: 'Button (5-Frame-Asset)',
},
buttons: 'Buttons',
noButtons: 'Noch keine Buttons',
audio: 'Audio',
noAudio: 'Noch kein Audio importiert',
deleteAudioDialog: {
title: 'Audio löschen?',
message:
'„{name}“ aus der Asset-Bibliothek entfernen. Alle Clips auf der Timeline, die darauf verweisen, werden ebenfalls entfernt.',
confirm: 'Löschen',
},
createButtonTooltip: 'Neues Button-Asset anlegen',
hitModeDisabled: 'Wechsle zurück zu den Button-States, um Assets hinzuzufügen.',
newButtonDialog: {
title: 'Neuer Button',
nameMessage: 'Button-Name:',
defaultName: 'Button',
next: 'Weiter',
sizeMessage: 'Canvas-Größe (Breite × Höhe):',
confirm: 'Erstellen',
},
renameButtonDialog: {
title: 'Button umbenennen',
confirm: 'Übernehmen',
},
deleteButtonDialog: {
title: 'Button löschen?',
message:
'Das Button-Asset wird entfernt. Ebenen, die darauf verweisen, werden danach nicht mehr gerendert.',
confirm: 'Löschen',
},
newTextDialog: {
title: 'Neues Textobjekt',
message: 'Text:',
default: 'Hallo',
confirm: 'Einfügen',
fallback: 'Text',
},
newPolygonDialog: {
title: 'Neues Polygon',
message: 'Anzahl der Punkte (3–24):',
confirm: 'Erstellen',
},
newClipDialog: {
title: 'Neuer Clip',
message: 'Clip-Name:',
default: 'Clip',
confirm: 'Erstellen',
},
renameClipDialog: {
title: 'Clip umbenennen',
confirm: 'Übernehmen',
},
deleteClipDialog: {
title: 'Clip löschen?',
message: 'Der Clip und alle platzierten Instanzen werden entfernt.',
confirm: 'Löschen',
},
},
fullscreen: {
pause: 'Pause',
play: 'Abspielen',
jumpToStart: 'Zum Anfang springen',
close: 'Schließen (Esc)',
},
settings: {
title: 'Einstellungen',
tab: {
document: 'Dokument',
clipart: 'Medien',
interface: 'Oberfläche',
theme: 'Theme',
},
timing: 'Timing',
languageTitle: 'Sprache',
languageLabel: 'App-Sprache',
languageDesc:
'Wähle die Sprache für Oberfläche und Hilfe. Die Einstellung wird im Browser gespeichert.',
languageEnglish: 'English',
languageGerman: 'Deutsch',
appearanceTitle: 'Darstellung',
tooltipsLabel: 'Tooltips anzeigen',
tooltipsDesc:
'Kleine Hilfetexte beim Hover über Buttons und Controls. Kannst du abschalten, wenn du die App schon gut kennst.',
showLockedLabel: 'Gesperrte Tracks anzeigen',
showLockedDesc:
'Wenn aus, verschwinden gesperrte Tracks aus Timeline und Bühne. Praktisch, wenn du dich nur auf aktive Ebenen konzentrieren willst.',
assetThumbsLabel: 'Asset-Vorschaubilder',
assetThumbsDesc:
'Zeigt kleine Vorschauen in Assets, Clips und Buttons. Aus ist kompakter, an ist übersichtlicher.',
bgVideoLabel: 'Video',
bgVideoNone: 'Kein Hintergrundvideo',
bgVideoPickFile: 'Videodatei wählen…',
bgVideoPickUrl: 'URL setzen…',
bgVideoClear: 'Hintergrundvideo entfernen',
bgVideoLocalFile: 'Lokale Videodatei',
bgVideoHint:
'Das Video läuft hinter allen Ebenen und folgt dem Playhead. Am besten funktioniert MP4 (H.264). Lokale Dateien werden mit dem Projekt gespeichert, URLs bleiben verlinkt.',
bgVideoStageSizeTitle: 'Videogröße übernehmen?',
bgVideoStageSizeMessage:
'Das Video ist {w}×{h} groß. Deine Canvas ist aktuell {cw}×{ch}. Soll die Canvas auf die Videogröße angepasst werden?',
bgVideoStageSizeConfirm: 'Canvas anpassen',
bgVideoStageSizeCancel: 'Aktuelle Größe behalten',
bgVideoDurationTitle: 'Szenendauer verlängern?',
bgVideoDurationMessage:
'Das Video ist {d}s lang. Die Szene ist aktuell {cd}s lang. Soll die Szene auf die Videolänge verlängert werden?',
bgVideoDurationConfirm: 'Dauer verlängern',
bgVideoDurationCancel: 'Aktuelle Dauer behalten',
bgVideoUrlTitle: 'Hintergrundvideo-URL',
bgVideoUrlMessage: 'Video-URL einfügen. Sie muss im Browser erreichbar sein:',
bgVideoUrlConfirm: 'Laden',
bgVideoButton: 'Hintergrundvideo',
bgVideoPicker: 'Klick für Videoauswahl · Rechtsklick für URL / Entfernen',
presetsLabel: 'Presets',
presetAddTitle: 'Preset hinzufügen',
presetAddMessage: 'Maße als BxH eingeben (z. B. 1920x1080).',
presetAddPlaceholder: '1920x1080',
presetAddConfirm: 'Hinzufügen',
presetAddInvalid: 'Bitte Maße wie 1920x1080 eingeben.',
presetAddTooltip: 'Preset hinzufügen',
presetRemoveTitle: 'Preset entfernen?',
presetRemoveMessage: '„{label}" aus den Presets entfernen?',
presetRemoveConfirm: 'Entfernen',
canvas: 'Canvas',
width: 'Breite',
height: 'Höhe',
background: 'Hintergrund',
backgroundTransparent: 'Transparent',
backgroundTransparentTooltip: 'Transparenter Hintergrund (beim APNG-Export bleibt Alpha erhalten)',
duration: 'Dauer (s)',
fps: 'FPS',
timelineHint: 'Die Timeline-Länge bestimmt die Skala im Lineal. Die Wiedergabe stoppt am Ende.',
themeHint: 'Wird im Browser gespeichert und beim nächsten Öffnen wiederverwendet.',
themeDark: 'Dunkel',
themeLight: 'Hell',
done: 'Fertig',
},
export: {
title: 'Export',
runtimeBadge: '1× pro Seite',
runtimeDesc: 'Engine: Einmal pro Seite laden. Alle Szenen teilen sich dieselbe Runtime.',
sceneBadge: 'Szene',
sceneDesc: 'Nur Projektdaten: Die Runtime muss schon geladen sein.',
exampleTitle: 'HTML-Beispiel',
exampleDesc: 'Eigenständige HTML-Datei: Einfach im Browser öffnen.',
mp4Badge: 'Server · Frame-genau',
mp4Desc: 'H.264 in jeder Auflösung: braucht den Render-Server.',
webmBadge: 'Browser · Echtzeit',
webmDesc: 'Ohne Server: direkt im Browser aufgenommen.',
webmNotSupported: 'Dieser Browser unterstützt MediaRecorder nicht.',
gifBadge: 'Browser · Frame-für-Frame',
gifDesc: '256-Farben-Palette, maximale Kompatibilität.',
apngBadge: 'Browser · Verlustfrei',
apngDesc: 'Volles RGBA mit Transparenz — .png-Datei.',
apngPreviewTitle: 'APNG-Vorschauseite',
apngPreviewBadge: 'HTML · Schachbrett',
apngPreviewDesc: 'Eigenständige HTML-Datei mit Schachbrett-Hintergrund zum Transparenz-Check.',
cancel: 'Abbrechen',
failed: 'Export fehlgeschlagen',
back: 'Zurück',
labelWebm: 'WebM wird gerendert (Browser, Echtzeit)…',
labelMp4: 'MP4 wird gerendert (Server)…',
labelGif: 'GIF wird gerendert (Browser, frame-für-frame)…',
labelApng: 'APNG wird gerendert (Browser, frame-für-frame)…',
labelApngPreview: 'APNG + Vorschau werden gerendert…',
skipHidden: 'Versteckte Tracks überspringen',
skipHiddenDesc: 'Lässt editor-versteckte Ebenen in allen Exportformaten weg.',
skipHiddenNone: 'In dieser Szene gibt es keine editor-versteckten Tracks.',
},
tests: {
running: 'Tests laufen',
title: 'Test Runner',
runAgain: 'Neu starten',
close: 'Schließen',
copyTooltip: 'Bericht in die Zwischenablage kopieren',
copiedTooltip: 'Bericht kopiert',
copiedBanner: 'Fehlerbericht wurde in die Zwischenablage kopiert.',
copyFailedBanner:
'Kopieren fehlgeschlagen. Nutze das Zwischenablage-Icon, um es manuell auszulösen.',
emptyState: 'Noch keine Ergebnisse. Klicke auf „Neu starten“ oder drücke {shortcut}.',
testsCountLabel: '{n} Tests',
passedLabel: '{n} bestanden',
failedLabel: '{n} fehlgeschlagen',
},
clipart: {
title: 'Medien',
open: 'Medien öffnen',
close: 'Medien schließen',
refresh: 'Ordner neu laden',
empty: 'Füge einen Ordner mit Bildern und Audiodateien hinzu, um die Bibliothek zu füllen.',
noFiles: 'In diesem Ordner gibt es keine Bilder oder Audiodateien.',
loading: 'Lädt…',
unsupported:
'Dein Browser unterstützt keinen Ordnerzugriff. Probier am besten Chrome oder Edge.',
chooseFolder: 'Ordner wählen…',
clearFolder: 'Leeren',
folderLabel: 'Ordner',
noFolder: 'Kein Ordner ausgewählt',
addFolder: 'Ordner hinzufügen',
addFolderTooltip: 'Weiteren Medien-Ordner hinzufügen',
backToIndex: 'Zur Ordnerübersicht',
removeFolder: 'Ordner aus der Bibliothek entfernen',
removeFolderDialog: {
title: 'Ordner entfernen?',
message: '„{name}" erscheint nicht mehr im Medien-Drawer. Die Dateien auf der Festplatte bleiben unangetastet.',
confirm: 'Entfernen',
},
settingsHint:
'Binde einen oder mehrere lokale Ordner mit Bildern und Audio an. Die App merkt sich die Auswahl. Nach einem Browser-Neustart kann die Berechtigung erneut abgefragt werden.',
},
documentDrawer: {
open: 'Dokument-Einstellungen öffnen',
close: 'Dokument-Einstellungen schließen',
projectName: 'Projekt',
},
scenes: {
title: 'Szenen',
open: 'Szenen öffnen',
close: 'Szenen schließen',
add: 'Szene hinzufügen',
rename: 'Szene umbenennen',
renameDialog: {
title: 'Szene umbenennen',
confirm: 'Umbenennen',
},
duplicate: 'Duplizieren',
delete: 'Löschen',
deleteDialog: {
title: 'Szene löschen?',
message: '„{name}“ aus diesem Projekt entfernen. Das kann nicht rückgängig gemacht werden.',
confirm: 'Löschen',
},
count: '{n} Szenen | {n} Szene | {n} Szenen',
locked: 'Ein Projekt muss mindestens eine Szene haben.',
},
dialog: {
confirm: 'Bestätigen',
input: 'Eingabe',
ok: 'OK',
cancel: 'Abbrechen',
close: 'Schließen',
},
about: {
openTooltip: 'Über RUWIO',
title: 'Über {name}',
tagline: 'Ein zeitleistenbasierter Animationseditor, der direkt im Browser läuft.',
versionLabel: 'Version',
buildLabel: 'Build',
close: 'Schließen',
credit: 'Mit Liebe gemacht von Torsten Hoeltge',
shortcutsHeading: 'Shortcuts',
shortcut: {
undo: 'Ctrl+Z',
redo: 'Ctrl+Shift+Z',
playPause: 'Leertaste',
fullscreen: 'Shift+Leertaste',
tests: 'Ctrl+Shift+Alt+T',
panCanvas: 'Mittelklick / Ctrl+Ziehen',
},
shortcutLabel: {
undo: 'Rückgängig',
redo: 'Wiederholen',
playPause: 'Play / Pause',
fullscreen: 'Fullscreen-Vorschau',
tests: 'Test Runner öffnen',
panCanvas: 'Canvas verschieben',
},
},
actions: {
empty: 'Noch keine Aktionen. Füge unten eine hinzu.',
add: 'Aktion hinzufügen',
remove: 'Entfernen',
moveUp: 'Nach oben',
moveDown: 'Nach unten',
noMarkers: 'Keine Marker vorhanden',
noScenes: 'Keine anderen Szenen vorhanden',
noButtonLayers: 'Keine Button-Ebenen vorhanden',
type: {
play: 'Abspielen',
pause: 'Pause',
rewindToLoop: 'Zum Loop-Start zurück',
jumpTo: 'Zu Zeit springen',
jumpToFrame: 'Zu Frame springen',
jumpToMarker: 'Zu Marker springen',
jumpToScene: 'Zu Szene springen',
setButtonEnabled: 'Button aktivieren / deaktivieren',
script: 'Script ausführen',
},
param: {
time: 'Sekunden',
frame: 'Frame',
marker: 'Marker',
scene: 'Szene',
buttonLayer: 'Button',
enabled: 'Aktiviert',
source: 'Script-Quelle',
},
},
scriptFrameDialog: {
title: 'Script-Frame',
time: 'Zeit (s)',
label: 'Label (optional)',
labelPlaceholder: 'z. B. intro-end',
remove: 'Frame löschen',
done: 'Fertig',
},
help: {
openTooltip: 'Hilfe',
title: 'RUWIO: Hilfe',
tocLabel: 'Hilfekapitel',
chapters: {
gettingStarted: 'Erste Schritte',
stage: 'Bühne',
assets: 'Asset-Bibliothek',
layersTimeline: 'Ebenen und Timeline',
clips: 'Clips',
buttons: 'Buttons',
scenes: 'Szenen',
audio: 'Audio',
markers: 'Marker',
scriptsActions: 'Script-Frames und Aktionen',
scriptLanguage: 'Scriptsprache',
export: 'Vorschau und Export',
shortcuts: 'Tastenkürzel',
},
shortcut: {
playPause: 'Play / Pause',
fullscreen: 'Fullscreen-Vorschau',
undo: 'Rückgängig',
redo: 'Wiederholen',
save: 'Projekt speichern',
inPoint: 'In-Point am Playhead setzen',
outPoint: 'Out-Point am Playhead setzen',
rewind: 'Zum Start zurück',
addMarker: 'Marker am Playhead setzen',
deleteSelected: 'Ausgewähltes Keyframe, Marker oder Ebene löschen',
clearSelection: 'Auswahl löschen oder Menü schließen',
panCanvas: 'Canvas verschieben',
zoomCanvas: 'Canvas zoomen',
pasteImage: 'Bild aus Zwischenablage einfügen',
tests: 'Test Runner öffnen',
},
table: {
syntax: 'Syntax',
effect: 'Wirkung',
},
content: {
gettingStarted: {
intro:
'RUWIO ist ein Timeline-Editor im Browser. Du legst Assets an, animierst sie mit Keyframes, prüfst das Ergebnis in der Vorschau und exportierst es als Runtime, HTML oder Video.',
modelTitle: 'So denkt die App',
modelBody:
'Ein <strong>Projekt</strong> kann aus mehreren <strong>Szenen</strong> bestehen. Alle Szenen teilen sich Canvas-Größe, Dauer und fps. In jeder Szene platzierst du <strong>Ebenen</strong>. Ebenen verweisen auf wiederverwendbare <strong>Assets</strong> wie Bilder, Shapes, Buttons, Clips oder Audio.',
whereTitle: 'Wo du was findest',
where: {
topBar: {
label: 'Top-Bar',
body:
'Hier sitzen Transport, Undo und Redo, Öffnen und Speichern, Einstellungen und Export. Das Logo öffnet den About-Dialog. Der <strong>?</strong>-Button öffnet diese Hilfe.',
},
assets: {
label: 'Assets-Seite',
body:
'Das ist deine gemeinsame Bibliothek für Bilder, Clips, Buttons und Audio. Mit den <strong>+</strong>-Buttons legst du schnell Dinge an und ziehst sie danach in die aktive Szene.',
},
stage: {
label: 'Bühne',
body:
'Hier siehst du die Live-Canvas. Du kannst Ebenen auswählen, verschieben und transformieren. In der Vorschau reagieren Buttons auf Hover und Klick.',
},
properties: {
label: 'Properties',
body:
'Hier bearbeitest du die Einstellungen der ausgewählten Ebene: Position, Rotation, Deckkraft, Fill, Masking, Button-Verknüpfungen, Clip-Trims und mehr.',
},
timeline: {
label: 'Timeline',
body:
'Hier liegen Keyframes, Clip-Instanzen, Script-Frames und Audio-Clips. Die Höhe kannst du über die obere Kante ändern.',
},
scenes: {
label: 'Szenen-Schublade',
body:
'Rechts findest du die Szenenliste. Dort kannst du wechseln, neue Szenen anlegen, duplizieren oder aufräumen.',
},
},
quickStartTitle: 'Erste Animation: Schnellstart',
quickStart: {
step1:
'Klicke auf <strong>+</strong> neben einer Form im Assets-Bereich, zum Beispiel Rechteck, Kreis, Text oder Polygon.',
step2: 'Ziehe die neue Form auf die Bühne und platziere sie.',
step3:
'Bewege den Playhead, ziehe die Form noch einmal und RUWIO legt automatisch ein neues Keyframe an.',
step4: 'Drücke <kbd>Leertaste</kbd>, um die Animation abzuspielen.',
},
},
stage: {
intro:
'Die Bühne zeigt die aktive Szene an der aktuellen Playhead-Position. Im Edit-Modus ist sie deine Arbeitsfläche mit Auswahl und Griffen. In der Fullscreen-Vorschau verhält sie sich wie die exportierte Runtime: Buttons können hovern, drücken und Aktionen auslösen.',
viewTitle: 'Ansicht steuern',
view: {
zoom:
'<strong>Zoom:</strong> Mit <kbd>Ctrl/Cmd</kbd> plus Mausrad oder über die Zoom-Controls unten rechts auf der Bühne.',
pan:
'<strong>Verschieben:</strong> Mittelklick ziehen oder beim Ziehen <kbd>Ctrl</kbd> halten.',
fit:
'<strong>Fit:</strong> Mit dem Fit-Button holst du die ganze Canvas wieder in den sichtbaren Bereich.',
},
selectionTitle: 'Auswahl',
selection: {
pick:
'Klicke eine Ebene an, um sie auszuwählen. Mit <kbd>Shift</kbd> plus Klick kannst du weitere Ebenen hinzufügen oder entfernen.',
clear: 'Klicke in leeren Bühnenbereich, um die Auswahl zu löschen.',
handles:
'Wenn genau eine Ebene ausgewählt ist, erscheinen die Transform-Griffe. Damit kannst du skalieren oder rotieren.',
},
lockingTitle: 'Sperren und im Editor verstecken',
lockingBody:
'Über den Rechtsklick auf einen Track kannst du Ebenen sperren oder im Editor ausblenden. <strong>Gesperrte</strong> Ebenen bleiben sichtbar, lassen sich aber nicht anfassen. <strong>Editor-versteckte</strong> Ebenen verschwinden nur im Editor, nicht automatisch aus Vorschau oder Export.',
drawingTitle: 'Polygone zeichnen',
drawingBody:
'Starte ein Polygon über die Assets-Seite, dann wechselt die Bühne in den Zeichenmodus. Mit Klick setzt du Punkte. <kbd>Enter</kbd> schließt ab. <kbd>Escape</kbd> bricht ab.',
},
assets: {
intro:
'Assets sind Vorlagen. Ebenen sind Platzierungen. Wenn du eine Ebene löschst, bleibt das Asset in der Bibliothek. Wenn du ein Asset löschst, verschwinden auch alle Ebenen, die darauf verweisen.',
sectionsTitle: 'Was dort liegen kann',
sections: {
shapes:
'<strong>Shapes:</strong> Schnelle Buttons für Rechteck, Kreis, Text und Polygon. Ein Klick legt Asset und Ebene direkt in der aktiven Szene an.',
images:
'<strong>Bilder:</strong> Über den Upload-Button importieren oder mit <kbd>Ctrl/Cmd+V</kbd> einfügen. Über den Medien-Ordner kannst du Dateien durchsuchen, ohne sie sofort alle ins Projekt zu ziehen.',
clips: '<strong>Clips:</strong> Wiederverwendbare Mini-Timelines.',
buttons: '<strong>Buttons:</strong> Interaktive Assets mit States und Klick-Aktionen.',
audio: '<strong>Audio:</strong> Gemeinsame Audio-Assets, die du auf Szenen-Tracks platzierst.',
},
thumbsTitle: 'Thumbs-Schalter',
thumbsBody:
'Mit <em>Thumbs</em> blendest du Vorschaubilder in den Asset-Listen ein oder aus. Aus ist kompakter. An ist oft leichter zu scannen.',
foldTitle: 'Bereiche einklappen',
foldBody:
'Jeder Abschnitt kann eingeklappt werden. Das hilft, wenn die Bibliothek mit der Zeit voller wird.',
},
layersTimeline: {
intro:
'Die Timeline stapelt Tracks untereinander und ordnet die Zeit von links nach rechts. Hier scrubbst du, retimest Dinge, gruppierst Tracks und verbindest Clips, Scripts und Audio.',
keyframesTitle: 'Keyframes',
keyframes: {
add: 'Klicke in einen leeren Bereich einer Layer-Spur, um dort ein Keyframe anzulegen.',
drag: 'Ziehe ein Keyframe horizontal, um es zeitlich zu verschieben.',
jump: 'Doppelklick auf ein Keyframe setzt den Playhead direkt darauf.',
menu:
'Per Rechtsklick bekommst du Optionen wie Easing, Still-Frame, Duplizieren und Löschen.',
},
holdsTitle: 'Still-Frames',
holdsBody:
'Ein gehaltenes Keyframe friert den aktuellen Wert bis zum nächsten Keyframe ein. Das ist praktisch, wenn du einen harten Sprung statt einer weichen Interpolation willst.',
separateTitle: 'Position und Rotation trennen',
separateBody:
'Standardmäßig speichert ein Keyframe alle animierbaren Werte zusammen. Mit <em>Separate</em> kannst du Position und Rotation auf eigene Sub-Tracks legen und getrennt retimen.',
groupsTitle: 'Gruppen',
groupsBody:
'Mehrere Tracks auswählen, rechtsklicken und <em>Group selected</em> wählen. Gruppen lassen sich einklappen, färben, sperren und gemeinsam verschieben.',
snappingTitle: 'Snapping',
snappingBody:
'Beim Ziehen von Keyframes oder Clips kann die Timeline an nahen Zeiten und Markern einrasten. Mit dem Magneten schaltest du das bei Bedarf aus.',
},
clips: {
intro:
'Ein <strong>Clip</strong> ist eine wiederverwendbare Gruppe aus Ebenen mit eigener lokaler Timeline. Auf der Haupt-Timeline wird daraus eine <strong>Clip-Instanz</strong>.',
createTitle: 'Clip anlegen',
create: {
step1: 'Wähle eine oder mehrere Ebenen in der Timeline aus.',
step2: 'Rechtsklick und <em>Convert to clip</em> wählen.',
step3:
'RUWIO erstellt das Clip-Asset und ersetzt die ursprünglichen Ebenen durch eine Clip-Instanz auf einer Clip-Spur.',
},
editTitle: 'Clip-Inhalt bearbeiten',
editBody:
'Doppelklick auf eine Clip-Instanz oder Klick auf den Clip in der Assets-Seite: Danach wechselst du in den Clip-Kontext. Alles, was du dort hinzufügst, gehört zum Clip, nicht zur Szene.',
trimTitle: 'Instanzen trimmen',
trimBody:
'Ziehe die linke oder rechte Kante einer Clip-Instanz, um festzulegen, welcher Teil der internen Animation sichtbar ist. Andere Instanzen desselben Clips behalten ihre eigenen Trims.',
transformTitle: 'Auf der Bühne transformieren',
transformBody:
'Eine Clip-Instanz kann auf der Bühne zusätzlich ihren eigenen Transform haben. So kannst du dieselbe Animation an mehreren Positionen, Größen oder Rotationen wiederverwenden.',
},
buttons: {
intro:
'Buttons sind interaktive Assets mit vier visuellen States: <code>normal</code>, <code>hover</code>, <code>pressed</code> und <code>disabled</code>. Dazu kommen optionale Hit-Shapes und On-Click-Aktionen.',
editingTitle: 'Button bearbeiten',
editingBody:
'Klicke einen Button in der Sidebar an, um in den Button-Edit-Modus zu wechseln. Bühne und Timeline zeigen dann den Button-Inhalt. Über den State-Switcher springst du zwischen <em>normal</em>, <em>hover</em>, <em>pressed</em> und <em>disabled</em>.',
stateKeyframesTitle: 'Keyframes pro State',
stateKeyframesBody:
'Buttons arbeiten mit vier festen Zeit-Slots, einer pro State. Leere Slots zeigen ein <strong>+</strong>. Ein Klick darauf erzeugt ein Start-Keyframe aus dem nächsten vorhandenen State.',
stillFramesTitle: 'Still-Frames in Buttons',
stillFramesBody:
'Ein gehaltenes Keyframe erscheint nur im jeweiligen Button-State. Normale Keyframes können über mehrere States interpolieren. Das ist ideal für kleine Extras wie Glow oder Outline in genau einem State.',
hitShapesTitle: 'Hit-Shapes',
hitShapesBody:
'Wechsle in den <em>Hit</em>-Modus, wenn du die klickbare Fläche zeichnen willst. Rechtecke, Kreise und Polygone lassen sich kombinieren. Ohne Hit-Shape ist die ganze Button-Canvas klickbar.',
actionsTitle: 'On-Click-Aktionen',
actionsBody:
'Mit ausgewählter Button-Ebene öffnest du im Properties-Panel den Bereich <em>On Click</em>. Die Aktionen laufen der Reihe nach, wenn der Klick im Hit-Bereich startet und endet.',
disabledTitle: 'Disabled-State',
disabledBody:
'Buttons können deaktiviert starten. Später lassen sie sich per Script oder Aktion mit <code>setButtonEnabled</code> an- oder ausschalten.',
},
scenes: {
intro:
'Szenen sind wie Seiten oder Kapitel. Sie teilen sich Canvas, fps, Dauer und Asset-Bibliothek, haben aber jeweils eigene Ebenen, Marker, Audio und Scripts.',
drawerTitle: 'Szenen-Schublade',
drawerBody:
'Über die Liste rechts kannst du Szenen wechseln, neue anlegen, duplizieren, umbenennen oder löschen.',
switchingTitle: 'Wie Szenenwechsel funktionieren',
switchingBody:
'Ein Wechsel über die Schublade ist normale Navigation: Der Playhead bleibt stehen. Ein Sprung mit <code>jumpToScene</code> wirkt eher wie ein Übergang: Die Zielszene startet wieder am In-Point.',
referencesTitle: 'Verweise zwischen Szenen',
referencesBody:
'Szenen-Aktionen arbeiten mit stabilen IDs. Darum brechen bestehende Sprünge nicht, wenn du eine Szene umbenennst.',
},
audio: {
intro:
'RUWIO trennt <strong>Audio-Assets</strong> von <strong>Audio-Clips</strong>. Assets liegen in der gemeinsamen Bibliothek. Clips sind Platzierungen auf Audio-Tracks innerhalb einer Szene.',
addTitle: 'Audio hinzufügen',
add: {
step1: 'Öffne den Audio-Bereich in der Sidebar und klicke auf den Upload-Button.',
step2: 'Wähle eine oder mehrere mp3-Dateien. Jede Datei wird zu einem Audio-Asset.',
step3:
'Ziehe ein Audio-Asset auf die Timeline oder in eine leere Stelle, damit automatisch ein Track angelegt wird.',
},
tracksTitle: 'Audio-Tracks',
tracksBody:
'Jeder Audio-Track kann mehrere Clips enthalten. Mit Rechtsklick kannst du ihn umbenennen, muten, färben, sperren oder löschen.',
movingTitle: 'Audio-Clips verschieben',
movingBody:
'Ziehe Clips nach links oder rechts, um sie zeitlich zu verschieben. Ziehe sie auf einen anderen Track, um sie umzulegen. Wenn Clips sich auf demselben Track überschneiden würden, schiebt RUWIO sie nach vorn.',
waveformsTitle: 'Waveforms',
waveformsBody:
'Waveforms werden im Hintergrund dekodiert. Direkt nach dem Import kann ein Clip kurz flach aussehen, bis die Vorschau geladen ist.',
urlsTitle: 'Data-URLs und externe URLs',
urlsBody:
'Lokal gewählte Dateien werden als Data-URL gespeichert, damit das Projekt offline bearbeitbar bleibt. Für produktive Exporte ist eine normale <code>https://</code>-URL oft die bessere Wahl.',
},
markers: {
intro:
'Marker sind benannte Zeitpunkte im Lineal. Sie helfen beim Navigieren, beim Timing und bei Script-Sprüngen wie <code>jump @intro</code>.',
editingTitle: 'Marker anlegen und bearbeiten',
editing: {
add: 'Mit <kbd>M</kbd> legst du einen Marker an der aktuellen Playhead-Position an.',
drag: 'Ziehe einen Marker horizontal, um ihn zeitlich zu verschieben.',
rename:
'Klicke einen Marker an, um ihn auszuwählen, und doppelklicke dann zum Umbenennen.',
delete:
'Ausgewählte Marker werden orange. Mit <kbd>Delete</kbd> oder <kbd>Backspace</kbd> löschst du sie.',
clear: 'Klick neben den Marker entfernt die Auswahl.',
},
sceneTitle: 'Pro Szene',
sceneBody:
'Marker gehören immer zu einer Szene, nicht zum ganzen Projekt. Wenn ein Script zu einem fehlenden Marker springen will, erscheint eine Warnung in der Runtime-Konsole.',
},
scriptsActions: {
intro:
'Ein <strong>Script-Frame</strong> ist ein Timeline-Event, das beim Überqueren durch die Wiedergabe eine oder mehrere Aktionen auslöst. Script-Frames liegen auf eigenen Script-Spuren.',
addTitle: 'Script-Frame anlegen',
add: {
step1:
'Klicke in einen leeren Bereich einer Script-Spur. Der Script-Frame-Dialog öffnet sich sofort.',
step2: 'Setze Zeit und optional ein Label und füge die gewünschten Aktionen hinzu.',
step3: 'Mit <em>Done</em> erscheint der Frame als Punkt auf der Spur.',
},
firingTitle: 'Wann Aktionen ausgelöst werden',
firing: {
forward:
'Nur Vorwärtsbewegung während der Wiedergabe löst Aktionen aus. Scrubbing und direkte Playhead-Sprünge tun das nicht.',
exact:
'Ein Script genau auf der aktuellen Playhead-Position feuert trotzdem beim ersten Tick nach dem Start von <em>Play</em>.',
order:
'Wenn mehrere Script-Frames im selben Tick-Fenster liegen, laufen sie in zeitlicher Reihenfolge.',
replay:
'Wenn du von derselben Position erneut startest, können Frames an genau dieser Position erneut auslösen.',
},
menuTitle: 'Rechtsklick-Menü',
menuBody:
'Per Rechtsklick auf einen Script-Frame kannst du ihn duplizieren oder löschen. Beim Duplizieren werden Label und Aktionsliste übernommen.',
typesTitle: 'Verfügbare Aktionstypen',
types: {
transport:
'<code>play</code>, <code>pause</code>, <code>rewindToLoop</code>: Steuerung der Wiedergabe.',
jump:
'<code>jumpTo</code>, <code>jumpToFrame</code>, <code>jumpToMarker</code>: bewegen den Playhead, ohne die Wiedergabe zu stoppen.',
scene:
'<code>jumpToScene</code>: wechselt die Szene und springt zurück zum In-Point.',
button:
'<code>setButtonEnabled</code>: aktiviert oder deaktiviert eine bestimmte Button-Ebene.',
script:
'<code>script</code>: führt eine kleine zeilenbasierte DSL aus. Die kommt im nächsten Kapitel.',
},
},
scriptLanguage: {
intro:
'Die <code>script</code>-Aktion akzeptiert eine kleine zeilenbasierte DSL. Editor, Vorschau und exportierte Runtime nutzen denselben Parser, deshalb bleibt das Verhalten konsistent.',
syntaxTitle: 'Syntax-Regeln',
syntax: {
onePerLine: 'Ein Befehl pro Zeile. Leere Zeilen werden ignoriert.',
comments: 'Alles hinter <code>//</code> in einer Zeile gilt als Kommentar.',
tokens:
'Tokens werden über Leerzeichen getrennt. Das erste Token ist immer der Befehlsname.',
case: 'Befehle sind nicht case-sensitiv.',
},
commandsTitle: 'Befehle',
commands: {
play: {
syntax: '<code>play</code>',
effect: 'Startet oder setzt die Wiedergabe fort.',
},
pause: {
syntax: '<code>pause</code>',
effect: 'Hält die Wiedergabe an und lässt den Playhead stehen.',
},
rewind: {
syntax: '<code>rewind</code>',
effect:
'Setzt den Playhead auf den Wiedergabe-In-Point: Projektstart oder aktueller <em>I</em>-Marker.',
},
jumpSeconds: {
syntax: '<code>jump &lt;seconds&gt;</code>',
effect:
'Springt zu einer Zeit in Sekunden, zum Beispiel <code>jump 2.5</code>.',
},
jumpFrame: {
syntax: '<code>jump #&lt;frame&gt;</code>',
effect:
'Springt zu einem Frame-Index bei der aktuellen Szenen-fps, zum Beispiel <code>jump #60</code>.',
},
jumpMarker: {
syntax: '<code>jump @&lt;marker&gt;</code>',
effect:
'Springt zu einem benannten Marker in der aktiven Szene, zum Beispiel <code>jump @intro</code>.',
},
},
exampleTitle: 'Beispiel',
exampleBody: `// Intro loopen und dann auf einen Klick warten.
jump @intro-start
pause // wartet auf einen Button oder einen anderen Script-Frame
play
// Ein späterer Script-Frame kann hierhin zurückspringen:
// jump @intro-start`,
errorsTitle: 'Fehler',
errorsBody:
'Unbekannte Befehle, fehlende Argumente und ungültige Jump-Ziele erscheinen in der Browser-Konsole als <code>[ruwio] Script line N: message</code>. Eine kaputte Zeile stoppt die anderen nicht.',
notInDslTitle: 'Was die DSL nicht kann',
notInDslBody:
'Die DSL ist bewusst klein gehalten. Dinge wie <code>jumpToScene</code>, <code>setButtonEnabled</code> oder verschachtelte <code>script</code>-Blöcke gehören in den strukturierten Action-Editor.',
},
export: {
intro:
'Du kannst direkt im Editor vorprüfen, die interaktive Runtime im Fullscreen testen oder das Projekt exportieren, sobald alles passt.',
editorPreviewTitle: 'Vorschau im Editor',
editorPreviewBody:
'Die normale Bühnenvorschau spielt die aktive Szene ab, aber ohne interaktive Button-Hover- oder Klick-States. Mit <kbd>Leertaste</kbd> startest oder pausierst du.',
fullscreenPreviewTitle: 'Fullscreen-Vorschau',
fullscreenPreviewBody:
'Mit <kbd>Shift</kbd>+<kbd>Leertaste</kbd> öffnest du die runtime-nahe Fullscreen-Vorschau. Dort funktionieren Buttons, Audio läuft und Szenensprünge sind live. Mit <kbd>Escape</kbd> schließt du sie.',
formatsTitle: 'Exportformate',
formats: {
runtime:
'<strong>ruwio-runtime.js:</strong> Die Runtime-Engine. Einmal auf der Seite laden und Projekte daran mounten.',
scene:
'<strong>animation.js:</strong> Nur Projektdaten. Die Runtime muss schon vorhanden sein.',
html:
'<strong>HTML-Beispiel:</strong> Eine schnelle eigenständige Seite mit allem fertig verdrahtet.',
mp4:
'<strong>MP4-Video:</strong> Frame-genauer Server-Render. Dafür muss der Render-Server in den Einstellungen konfiguriert sein.',
webm:
'<strong>WebM-Video:</strong> Browser-seitige Echtzeit-Aufnahme. Das hängt von <code>MediaRecorder</code> ab.',
},
skipHiddenTitle: 'Versteckte Ebenen auslassen',
skipHiddenBody:
'Wenn <em>Skip editor-hidden layers</em> aktiv ist, werden im Editor versteckte Ebenen im Export weggelassen. Das ist praktisch für Hilfsebenen, Skizzen oder temporäre Elemente.',
assetsTitle: 'Assets und URLs',
assetsBody:
'Bilder und Audio als Data-URL bleiben im Export eingebettet. Hintergrundvideos und externe Medien-URLs bleiben verlinkt. Die Dateien müssen also weiterhin erreichbar sein.',
},
shortcuts: {
intro:
'Die meisten Shortcuts pausieren automatisch, sobald ein Textfeld, eine Textarea oder ein Zahlenfeld den Fokus hat. So kannst du sicher tippen.',
},
},
},
}