Lern-Shorts im Hochformat: „Was frisst ein Igel?“ und „Wer ist am größten?“ (DE/ES/FR, Stimmen erzeugt)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6eddc6614d
commit
3ac783b0f5
13 changed files with 697 additions and 0 deletions
|
|
@ -13,6 +13,7 @@ import {F02} from './folge02/Folge02';
|
||||||
import {F03} from './folge03/Folge03';
|
import {F03} from './folge03/Folge03';
|
||||||
import {F08} from './folge08/Folge08';
|
import {F08} from './folge08/Folge08';
|
||||||
import {F09} from './folge09/Folge09';
|
import {F09} from './folge09/Folge09';
|
||||||
|
import {LernShorts} from './folge90/Folge90';
|
||||||
import {Banner, BannerRahmen, Profilbild} from './kanal/KanalBilder';
|
import {Banner, BannerRahmen, Profilbild} from './kanal/KanalBilder';
|
||||||
import {VorschauF01, VorschauF09} from './kanal/Vorschaubild';
|
import {VorschauF01, VorschauF09} from './kanal/Vorschaubild';
|
||||||
import {ShortKompositionen} from './shorts/Short';
|
import {ShortKompositionen} from './shorts/Short';
|
||||||
|
|
@ -39,6 +40,7 @@ export const Root: React.FC = () => (
|
||||||
<FolgenKompositionen folge={F03} />
|
<FolgenKompositionen folge={F03} />
|
||||||
<FolgenKompositionen folge={F08} />
|
<FolgenKompositionen folge={F08} />
|
||||||
<FolgenKompositionen folge={F09} />
|
<FolgenKompositionen folge={F09} />
|
||||||
|
<LernShorts />
|
||||||
<ShortKompositionen szenen={SZENEN_F01} shorts={SHORTS_F01} />
|
<ShortKompositionen szenen={SZENEN_F01} shorts={SHORTS_F01} />
|
||||||
<Composition id="Kanal-Profilbild" component={Profilbild} durationInFrames={1} fps={30} width={800} height={800} />
|
<Composition id="Kanal-Profilbild" component={Profilbild} durationInFrames={1} fps={30} width={800} height={800} />
|
||||||
<Composition id="Vorschau-F01" component={VorschauF01} durationInFrames={1} {...BILD} />
|
<Composition id="Vorschau-F01" component={VorschauF01} durationInFrames={1} {...BILD} />
|
||||||
|
|
|
||||||
39
remotion/src/folge90/Folge90.tsx
Normal file
39
remotion/src/folge90/Folge90.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {AbsoluteFill, Composition} from 'remotion';
|
||||||
|
import {baueFolge, type SzenenEintrag} from '../baukasten/folge';
|
||||||
|
import {mitKlang} from '../baukasten/geraeusche';
|
||||||
|
import {Musik} from '../baukasten/musik';
|
||||||
|
import {S_GIRAFFE, Giraffe} from './giraffe';
|
||||||
|
import {H, W} from './hochformat';
|
||||||
|
import {S_IGEL, Igel} from './igel';
|
||||||
|
|
||||||
|
// Lern-Shorts „Pinos Wissen“ (Hochformat, je ca. 30 s): kurze Sachfragen mit Pino, Noa und Lulu.
|
||||||
|
// Kürzel F90 nur für die Werkzeuge (Stimmen: npx tsx werkzeuge/stimmen.mts F90 --sprache de).
|
||||||
|
// Rendern: npx remotion render src/index.ts L01-igel out.mp4 --props='{"sprache":"de"}'
|
||||||
|
|
||||||
|
const garten = {atmo: 'garten_tag', atmoLaut: 0.08};
|
||||||
|
const musik = {datei: 'musik/folge01/lied.mp3', lautstaerke: 0.12, ducken: 0.5, rein: 0.5, raus: 1.2};
|
||||||
|
|
||||||
|
export const SZENEN: SzenenEintrag[] = [
|
||||||
|
{name: 'Igel', datei: 'igel.tsx', plan: S_IGEL, K: mitKlang(Igel, S_IGEL, garten), musik},
|
||||||
|
{name: 'Giraffe', datei: 'giraffe.tsx', plan: S_GIRAFFE, K: mitKlang(Giraffe, S_GIRAFFE, garten), musik},
|
||||||
|
];
|
||||||
|
|
||||||
|
// für werkzeuge/szenenliste.mts (Stimmen); nicht als Querformat-Folge registriert
|
||||||
|
export const F90 = baueFolge('F90', SZENEN);
|
||||||
|
|
||||||
|
const IDS = ['L01-igel', 'L02-giraffe'];
|
||||||
|
|
||||||
|
export const LernShorts: React.FC = () => (
|
||||||
|
<>
|
||||||
|
{SZENEN.map((sz, i) => {
|
||||||
|
const K: React.FC = () => (
|
||||||
|
<AbsoluteFill style={{background: '#f4ead8'}}>
|
||||||
|
<sz.K />
|
||||||
|
{sz.musik && <Musik {...sz.musik} dauer={sz.plan.dauer} zeilen={sz.plan.liste} />}
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
return <Composition key={IDS[i]} id={IDS[i]} component={K} durationInFrames={sz.plan.dauer} fps={30} width={W} height={H} />;
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
74
remotion/src/folge90/bilder.tsx
Normal file
74
remotion/src/folge90/bilder.tsx
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// Kleine Bilder für die Lern-Shorts, im Stil von Apfel und Blume (baukasten/natur.tsx):
|
||||||
|
// flache Formen, warme Farben, ca. 200 px groß, Mitte bei 0/0.
|
||||||
|
|
||||||
|
const Rahmen: React.FC<{children: React.ReactNode; s?: number}> = ({children, s = 1}) => (
|
||||||
|
<svg width={220 * s} height={220 * s} viewBox="-110 -110 220 220" style={{overflow: 'visible'}}>
|
||||||
|
{children}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Kaefer: React.FC = () => (
|
||||||
|
<Rahmen>
|
||||||
|
{/* Beinchen */}
|
||||||
|
{[-1, 1].flatMap((seite) =>
|
||||||
|
[-28, 0, 28].map((y) => <path key={`${seite}${y}`} d={`M ${seite * 34} ${y} q ${seite * 26} ${-6} ${seite * 40} ${10}`} stroke="#3b2a22" strokeWidth={7} fill="none" strokeLinecap="round" />),
|
||||||
|
)}
|
||||||
|
{/* Fühler */}
|
||||||
|
<path d="M -12 -66 q -14 -26 -30 -30 M 12 -66 q 14 -26 30 -30" stroke="#3b2a22" strokeWidth={6} fill="none" strokeLinecap="round" />
|
||||||
|
<circle cx={0} cy={-56} r={22} fill="#3b2a22" />
|
||||||
|
<ellipse cx={0} cy={8} rx={48} ry={60} fill="#2f6b4a" />
|
||||||
|
<path d="M 0 -50 L 0 66" stroke="#1f4a33" strokeWidth={5} />
|
||||||
|
<ellipse cx={-20} cy={-18} rx={10} ry={16} fill="#5fa77a" opacity={0.7} />
|
||||||
|
</Rahmen>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Wurm: React.FC = () => (
|
||||||
|
<Rahmen>
|
||||||
|
<path d="M -80 40 C -60 -20, -20 -20, -10 20 S 40 60, 60 0" stroke="#d98a7a" strokeWidth={34} fill="none" strokeLinecap="round" />
|
||||||
|
{[-62, -38, -14, 12, 36].map((x, i) => (
|
||||||
|
<path key={i} d={`M ${x} ${[18, -6, 4, 30, 20][i] - 14} l 6 26`} stroke="#b9675a" strokeWidth={4} strokeLinecap="round" />
|
||||||
|
))}
|
||||||
|
<circle cx={66} cy={-6} r={4.5} fill="#3b2a22" />
|
||||||
|
</Rahmen>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Schnecke: React.FC = () => (
|
||||||
|
<Rahmen>
|
||||||
|
<path d="M -90 50 C -60 40, 30 40, 70 46 C 86 48, 92 30, 84 10 L 78 -20" stroke="#c9a06a" strokeWidth={26} fill="none" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M 72 -18 l -6 -26 M 84 -16 l 8 -26" stroke="#c9a06a" strokeWidth={6} strokeLinecap="round" />
|
||||||
|
<circle cx={66} cy={-46} r={6} fill="#3b2a22" />
|
||||||
|
<circle cx={93} cy={-44} r={6} fill="#3b2a22" />
|
||||||
|
<circle cx={-10} cy={-6} r={56} fill="#b0662e" />
|
||||||
|
<path d="M -10 -6 m 0 -40 a 40 40 0 1 1 -36 22 a 28 28 0 1 1 30 -4 a 14 14 0 1 1 -10 8" stroke="#7a3f1c" strokeWidth={7} fill="none" strokeLinecap="round" />
|
||||||
|
</Rahmen>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Milch: React.FC = () => (
|
||||||
|
<Rahmen>
|
||||||
|
<path d="M -36 -50 L -36 -76 L 36 -76 L 36 -50 L 50 -22 L 50 78 Q 50 88 40 88 L -40 88 Q -50 88 -50 78 L -50 -22 Z" fill="#f8f6f0" stroke="#8a8fa0" strokeWidth={5} />
|
||||||
|
<rect x={-38} y={-92} width={76} height={20} rx={6} fill="#5b8fd1" />
|
||||||
|
<rect x={-50} y={10} width={100} height={46} fill="#5b8fd1" opacity={0.85} />
|
||||||
|
<circle cx={0} cy={33} r={13} fill="#f8f6f0" />
|
||||||
|
</Rahmen>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ApfelBild: React.FC = () => (
|
||||||
|
<Rahmen>
|
||||||
|
<g transform="scale(2.4)">
|
||||||
|
<path d="M 0 -18 C -8 -26, -30 -24, -28 0 C -27 22, -12 32, 0 26 C 12 32, 27 22, 28 0 C 30 -24, 8 -26, 0 -18 Z" fill="#c8362c" />
|
||||||
|
<path d="M -16 -10 C -20 -2, -18 8, -12 14" stroke="#e8786a" strokeWidth={5} strokeLinecap="round" fill="none" opacity={0.7} />
|
||||||
|
<path d="M 0 -18 C 1 -26, 3 -32, 6 -36" stroke="#5a3a24" strokeWidth={4} strokeLinecap="round" fill="none" />
|
||||||
|
<path d="M 5 -30 C 14 -40, 26 -36, 28 -30 C 20 -26, 12 -26, 5 -30 Z" fill="#6f9a3e" />
|
||||||
|
</g>
|
||||||
|
</Rahmen>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sanftes „lieber nicht“: roter Ring mit Querstrich über dem Bild
|
||||||
|
export const Nein: React.FC = () => (
|
||||||
|
<svg width={300} height={300} viewBox="-150 -150 300 300" style={{position: 'absolute', inset: 0, margin: 'auto', overflow: 'visible'}}>
|
||||||
|
<circle r={128} stroke="#d0473a" strokeWidth={18} fill="none" opacity={0.9} />
|
||||||
|
<path d="M -90 90 L 90 -90" stroke="#d0473a" strokeWidth={18} strokeLinecap="round" opacity={0.9} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
62
remotion/src/folge90/giraffe.tsx
Normal file
62
remotion/src/folge90/giraffe.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {useCurrentFrame} from 'remotion';
|
||||||
|
import {Figur} from '../baukasten/figur';
|
||||||
|
import {Blende, atmen, huepfer} from '../baukasten/helfer';
|
||||||
|
import {Blaetterwind} from '../baukasten/natur';
|
||||||
|
import {Ort} from '../baukasten/orte';
|
||||||
|
import {Buehne, FrageKarte, HochUntertitel, Hochformat, Wort} from './hochformat';
|
||||||
|
import {TEXTE, plane} from './plan';
|
||||||
|
|
||||||
|
// Lern-Short „Wer ist am größten?“: Pino, Noa und Lulu nebeneinander – klein, mittel, groß.
|
||||||
|
// Jede Figur hüpft bzw. wiegt sich, wenn sie genannt wird, darüber erscheint das Wort.
|
||||||
|
// Zum Schluss sagen die Kinder die drei Wörter mit (Pause, die Wörter pulsieren nacheinander).
|
||||||
|
|
||||||
|
export const S_GIRAFFE = plane('giraffe', [
|
||||||
|
{id: 'bild0', dauer: 1.0},
|
||||||
|
{id: 'e1', wer: 'ERZ', pause: 3},
|
||||||
|
{id: 'e2', wer: 'ERZ', pause: 0.4},
|
||||||
|
{id: 'e3', wer: 'ERZ', pause: 0.4},
|
||||||
|
{id: 'e4', wer: 'ERZ'},
|
||||||
|
{id: 'l1', wer: 'LULU'},
|
||||||
|
{id: 'e5', wer: 'ERZ'},
|
||||||
|
{id: 'e6', wer: 'ERZ', pause: 3.5},
|
||||||
|
{id: 'bild1', dauer: 1.0},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const Z = 1.5;
|
||||||
|
const MITTE = 960;
|
||||||
|
const Y = 815; // Fußlinie etwas weiter hinten auf der Wiese, damit die Füße über den Untertiteln bleiben
|
||||||
|
const PLATZ = {pino: 700, noa: 870, lulu: 1110};
|
||||||
|
// Bühne → Bildschirm (Hochformat), für die Wörter über den Figuren
|
||||||
|
const sx = (x: number) => 540 + (x - MITTE) * Z;
|
||||||
|
const sy = (y: number) => 960 + (y - 540) * Z;
|
||||||
|
|
||||||
|
export const Giraffe: React.FC = () => {
|
||||||
|
const f = useCurrentFrame();
|
||||||
|
const {z, dauer} = S_GIRAFFE;
|
||||||
|
const mitsagen = z.e6.ende + 6;
|
||||||
|
const w = (n: 1 | 2 | 3) => TEXTE.zeilen[`giraffe/w${n}`];
|
||||||
|
return (
|
||||||
|
<Hochformat hintergrund="linear-gradient(#e9e3d2 0 50%, #93ad5c 50% 100%)">
|
||||||
|
<Buehne x={MITTE} z={Z}>
|
||||||
|
<Ort name="garten_herbst" />
|
||||||
|
<Blaetterwind anzahl={8} seed="giraffe" tempo={0.6} yBereich={[300, 800]} />
|
||||||
|
<Figur
|
||||||
|
pose={[[0, 'lulu_A6_blick-rechts'], [z.e4.start, 'lulu_A3_lachen']]}
|
||||||
|
x={PLATZ.lulu}
|
||||||
|
y={Y}
|
||||||
|
drehung={f >= z.e4.start && f < z.l1.ende ? Math.sin((f - z.e4.start) * 0.15) * 1.5 : 0}
|
||||||
|
stauchY={atmen(f, 3)}
|
||||||
|
/>
|
||||||
|
<Figur pose={[[0, 'noa_A5_blick-links'], [z.e3.start, 'noa_A3_lachen']]} x={PLATZ.noa} y={Y + huepfer(f, z.e3.start, 18, 10)} stauchY={atmen(f, 2)} />
|
||||||
|
<Figur pose={[[0, 'pino_A6_blick-rechts'], [z.e2.start, 'pino_A3_lachen']]} x={PLATZ.pino} y={Y + huepfer(f, z.e2.start, 18, 10) + huepfer(f, mitsagen, 14, 10)} stauchY={atmen(f, 1, 0.015)} />
|
||||||
|
</Buehne>
|
||||||
|
<FrageKarte text={TEXTE.zeilen['giraffe/frage']} />
|
||||||
|
<Wort text={w(1)} x={sx(PLATZ.pino)} y={sy(Y - 200)} ab={z.e2.start + 4} puls={[mitsagen]} groesse={70} farbe="#5b8fd1" />
|
||||||
|
<Wort text={w(2)} x={sx(PLATZ.noa)} y={sy(Y - 400)} ab={z.e3.start + 4} puls={[mitsagen + 30]} groesse={84} farbe="#7fb069" />
|
||||||
|
<Wort text={w(3)} x={870} y={sy(Y - 470)} ab={z.e4.start + 4} puls={[mitsagen + 60]} groesse={104} farbe="#c8502c" />
|
||||||
|
<HochUntertitel zeilen={S_GIRAFFE.liste} />
|
||||||
|
<Blende dauer={dauer} rein={8} raus={14} />
|
||||||
|
</Hochformat>
|
||||||
|
);
|
||||||
|
};
|
||||||
105
remotion/src/folge90/hochformat.tsx
Normal file
105
remotion/src/folge90/hochformat.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion';
|
||||||
|
import {SCHRIFT, Untertitel} from '../baukasten/untertitel';
|
||||||
|
import type {Geplant} from '../baukasten/zeit';
|
||||||
|
import {TEXTE} from './plan';
|
||||||
|
|
||||||
|
// Lern-Shorts im Hochformat (1080 × 1920). Die Szene spielt auf der gewohnten Bühne (1920 × 1080,
|
||||||
|
// Figuren und Orte wie in den Folgen); `Buehne` schneidet daraus den Hochformat-Ausschnitt.
|
||||||
|
// Frage oben, Untertitel über dem Bereich, den YouTube unten mit Titel und Knöpfen verdeckt.
|
||||||
|
|
||||||
|
export const W = 1080;
|
||||||
|
export const H = 1920;
|
||||||
|
export const VOLL = H / 1080; // kleinste Vergrößerung, bei der die Bühne das Hochformat füllt
|
||||||
|
|
||||||
|
export const Buehne: React.FC<{x: number; y?: number; z?: number; children: React.ReactNode}> = ({x, y = 540, z = VOLL, children}) => {
|
||||||
|
const halbB = W / 2 / z;
|
||||||
|
const halbH = H / 2 / z;
|
||||||
|
// Ist der Ausschnitt größer als die Bühne, bleibt sie mittig (oben/unten Papierrand)
|
||||||
|
const bx = halbB * 2 >= 1920 ? 960 : Math.min(1920 - halbB, Math.max(halbB, x));
|
||||||
|
const by = halbH * 2 >= 1080 ? 540 : Math.min(1080 - halbH, Math.max(halbH, y));
|
||||||
|
return (
|
||||||
|
<div style={{position: 'absolute', width: 1920, height: 1080, transformOrigin: '0 0', transform: `translate(${W / 2}px, ${H / 2}px) scale(${z}) translate(${-bx}px, ${-by}px)`}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const KARTE = {
|
||||||
|
background: 'rgba(255, 250, 240, 0.92)',
|
||||||
|
borderRadius: 40,
|
||||||
|
boxShadow: '0 8px 30px rgba(60, 40, 20, 0.22)',
|
||||||
|
fontFamily: SCHRIFT,
|
||||||
|
textAlign: 'center',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Frage oben, darunter klein der Kanalname
|
||||||
|
export const FrageKarte: React.FC<{text: string}> = ({text}) => {
|
||||||
|
const f = useCurrentFrame();
|
||||||
|
const {fps} = useVideoConfig();
|
||||||
|
const s = spring({frame: f - 4, fps, config: {damping: 14}});
|
||||||
|
return (
|
||||||
|
<div style={{position: 'absolute', top: 150, left: 60, right: 60, display: 'flex', justifyContent: 'center', transform: `scale(${0.8 + 0.2 * s})`, opacity: s}}>
|
||||||
|
<div style={{...KARTE, padding: '26px 44px 22px'}}>
|
||||||
|
<div style={{fontSize: 74, fontWeight: 700, color: '#3f5a8a', lineHeight: 1.1}}>{text}</div>
|
||||||
|
<div style={{fontSize: 32, fontWeight: 600, color: '#c8502c', marginTop: 8}}>{TEXTE.zeilen.kanal}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Untertitel der Folgen, nach oben versetzt (unten liegen im Shorts-Player Titel und Knöpfe)
|
||||||
|
export const HochUntertitel: React.FC<{zeilen: Geplant[]}> = ({zeilen}) => (
|
||||||
|
<div style={{position: 'absolute', left: 40, right: 40, top: 0, bottom: 340}}>
|
||||||
|
<Untertitel zeilen={zeilen} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runde Bildkarte, die bei `ab` aufploppt (Bildschirm-Koordinaten des Hochformats)
|
||||||
|
export const BildKarte: React.FC<{x: number; y: number; ab: number; r?: number; aus?: number; children: React.ReactNode}> = ({x, y, ab, r = 150, aus = 1e9, children}) => {
|
||||||
|
const f = useCurrentFrame();
|
||||||
|
const {fps} = useVideoConfig();
|
||||||
|
const s = spring({frame: f - ab, fps, config: {damping: 11}});
|
||||||
|
const weg = interpolate(f, [aus, aus + 10], [1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||||
|
if (f < ab) return null;
|
||||||
|
return (
|
||||||
|
<div style={{position: 'absolute', left: x - r, top: y - r, width: 2 * r, height: 2 * r, ...KARTE, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', transform: `scale(${s * weg})`}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Großes Wort zum Mitsagen (klein – mittel – groß)
|
||||||
|
// `puls`: Zeitpunkte, an denen das Wort noch einmal kurz groß wird (beim Mitsagen)
|
||||||
|
export const Wort: React.FC<{text: string; x: number; y: number; ab: number; puls?: number[]; groesse?: number; farbe?: string}> = ({text, x, y, ab, puls = [], groesse = 90, farbe = '#c8502c'}) => {
|
||||||
|
const f = useCurrentFrame();
|
||||||
|
const {fps} = useVideoConfig();
|
||||||
|
if (f < ab) return null;
|
||||||
|
const pulsen = puls.reduce((m, p) => m + 0.35 * Math.max(0, Math.sin(Math.PI * Math.min(1, Math.max(0, (f - p) / 18)))), 0);
|
||||||
|
const s = spring({frame: f - ab, fps, config: {damping: 10}}) + pulsen;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: x - 300,
|
||||||
|
width: 600,
|
||||||
|
top: y - groesse / 2,
|
||||||
|
textAlign: 'center',
|
||||||
|
fontFamily: SCHRIFT,
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: groesse,
|
||||||
|
color: farbe,
|
||||||
|
WebkitTextStroke: '10px #fbf4e4',
|
||||||
|
paintOrder: 'stroke fill',
|
||||||
|
transform: `scale(${s})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `hintergrund`: füllt oben und unten, wenn die Bühne das Hochformat nicht ganz ausfüllt
|
||||||
|
export const Hochformat: React.FC<{hintergrund?: string; children: React.ReactNode}> = ({hintergrund = '#f4ead8', children}) => (
|
||||||
|
<AbsoluteFill style={{background: hintergrund, overflow: 'hidden'}}>{children}</AbsoluteFill>
|
||||||
|
);
|
||||||
77
remotion/src/folge90/igel.tsx
Normal file
77
remotion/src/folge90/igel.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {useCurrentFrame} from 'remotion';
|
||||||
|
import {Figur} from '../baukasten/figur';
|
||||||
|
import {Blende, atmen, huepfer} from '../baukasten/helfer';
|
||||||
|
import {Blaetterwind} from '../baukasten/natur';
|
||||||
|
import {Ort} from '../baukasten/orte';
|
||||||
|
import {ApfelBild, Kaefer, Milch, Nein, Schnecke, Wurm} from './bilder';
|
||||||
|
import {BildKarte, Buehne, FrageKarte, HochUntertitel, Hochformat} from './hochformat';
|
||||||
|
import {TEXTE, plane} from './plan';
|
||||||
|
|
||||||
|
// Lern-Short „Was frisst ein Igel?“: Pino groß in der Mitte, darüber ploppen Käfer, Wurm und Schnecke
|
||||||
|
// auf; Milch und Obst sind nichts für Igel (sanft durchgestrichen). Passt zum Elternhinweis aus Folge 1.
|
||||||
|
|
||||||
|
export const S_IGEL = plane('igel', [
|
||||||
|
{id: 'bild0', dauer: 1.0},
|
||||||
|
{id: 'e1', wer: 'ERZ', pause: 3},
|
||||||
|
{id: 'p1', wer: 'PINO'},
|
||||||
|
{id: 'e2', wer: 'ERZ', pause: 0.3},
|
||||||
|
{id: 'e3', wer: 'ERZ', pause: 0.3},
|
||||||
|
{id: 'e4', wer: 'ERZ'},
|
||||||
|
{id: 'p2', wer: 'PINO'},
|
||||||
|
{id: 'e5', wer: 'ERZ'},
|
||||||
|
{id: 'p3', wer: 'PINO'},
|
||||||
|
{id: 'bild1', dauer: 1.2},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const PINO = {x: 960, y: 790, hoehe: 300};
|
||||||
|
|
||||||
|
export const Igel: React.FC = () => {
|
||||||
|
const f = useCurrentFrame();
|
||||||
|
const {z, dauer} = S_IGEL;
|
||||||
|
const hops = huepfer(f, z.p1.start, 16, 10) + huepfer(f, z.p2.start, 16, 10) + huepfer(f, z.p3.start, 16, 10);
|
||||||
|
return (
|
||||||
|
<Hochformat>
|
||||||
|
<Buehne x={PINO.x}>
|
||||||
|
<Ort name="garten_herbst" />
|
||||||
|
<Blaetterwind anzahl={8} seed="igel" tempo={0.6} yBereich={[400, 900]} />
|
||||||
|
<Figur
|
||||||
|
pose={[
|
||||||
|
[0, 'pino_A4_staunen'],
|
||||||
|
[z.p1.start - 4, 'pino_A3_lachen'],
|
||||||
|
[z.e2.start, 'pino_B_blick-nach-oben'],
|
||||||
|
[z.p2.start - 4, 'pino_A3_lachen'],
|
||||||
|
[z.e5.start, 'pino_A4_staunen'],
|
||||||
|
[z.p3.start - 4, 'pino_A3_lachen'],
|
||||||
|
]}
|
||||||
|
x={PINO.x}
|
||||||
|
y={PINO.y + hops}
|
||||||
|
hoehe={PINO.hoehe}
|
||||||
|
stauchY={atmen(f, 1, 0.015)}
|
||||||
|
/>
|
||||||
|
</Buehne>
|
||||||
|
<FrageKarte text={TEXTE.zeilen['igel/frage']} />
|
||||||
|
{/* Das fressen Igel */}
|
||||||
|
<BildKarte x={230} y={660} ab={z.e2.start} aus={z.e5.start}>
|
||||||
|
<Kaefer />
|
||||||
|
</BildKarte>
|
||||||
|
<BildKarte x={540} y={610} ab={z.e3.start} aus={z.e5.start}>
|
||||||
|
<Wurm />
|
||||||
|
</BildKarte>
|
||||||
|
<BildKarte x={850} y={660} ab={z.e4.start} aus={z.e5.start}>
|
||||||
|
<Schnecke />
|
||||||
|
</BildKarte>
|
||||||
|
{/* Das lieber nicht */}
|
||||||
|
<BildKarte x={330} y={640} ab={z.e5.start + 12} aus={z.p3.start}>
|
||||||
|
<Milch />
|
||||||
|
<Nein />
|
||||||
|
</BildKarte>
|
||||||
|
<BildKarte x={750} y={640} ab={z.e5.start + 22} aus={z.p3.start}>
|
||||||
|
<ApfelBild />
|
||||||
|
<Nein />
|
||||||
|
</BildKarte>
|
||||||
|
<HochUntertitel zeilen={S_IGEL.liste} />
|
||||||
|
<Blende dauer={dauer} rein={8} raus={14} />
|
||||||
|
</Hochformat>
|
||||||
|
);
|
||||||
|
};
|
||||||
29
remotion/src/folge90/plan.ts
Normal file
29
remotion/src/folge90/plan.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import {SPRACHE, type FolgenTexte} from '../baukasten/sprache';
|
||||||
|
import {plane as basis, type PlanOptionen, type Zeile} from '../baukasten/zeit';
|
||||||
|
import DE from './texte/de.json';
|
||||||
|
import ES from './texte/es.json';
|
||||||
|
import FR from './texte/fr.json';
|
||||||
|
import STIMMEN_DE from './stimmen/de.json';
|
||||||
|
import STIMMEN_ES from './stimmen/es.json';
|
||||||
|
import STIMMEN_FR from './stimmen/fr.json';
|
||||||
|
|
||||||
|
// Sprechtexte stehen nur in texte/<sprache>.json (Schlüssel „szene/zeile“),
|
||||||
|
// die Szenen enthalten nur Ablauf und Bewegung. Aufnahmen je Sprache in
|
||||||
|
// stimmen/<sprache>.json bzw. public/stimmen/folge90/<sprache>/ (erzeugt von
|
||||||
|
// werkzeuge/stimmen.mts).
|
||||||
|
|
||||||
|
export const TEXTE: FolgenTexte = {de: DE, es: ES, fr: FR}[SPRACHE];
|
||||||
|
const AUFNAHMEN = {de: STIMMEN_DE, es: STIMMEN_ES, fr: STIMMEN_FR}[SPRACHE];
|
||||||
|
|
||||||
|
export const TITEL = TEXTE.titel;
|
||||||
|
|
||||||
|
// Zeitplan einer Szene dieser Folge, mit den Texten der gewählten Sprache
|
||||||
|
export const plane = (szene: string, zeilen: Zeile[], opt: PlanOptionen = {}) =>
|
||||||
|
basis(
|
||||||
|
zeilen.map((z) => {
|
||||||
|
if (z.text !== undefined || !z.wer) return z;
|
||||||
|
const text = TEXTE.zeilen[`${szene}/${z.id}`];
|
||||||
|
return {...z, text: text ?? `[fehlt: ${szene}/${z.id}]`};
|
||||||
|
}),
|
||||||
|
{...opt, szene, aufnahmen: AUFNAHMEN, ordner: `stimmen/folge90/${SPRACHE}`},
|
||||||
|
);
|
||||||
77
remotion/src/folge90/stimmen/de.json
Normal file
77
remotion/src/folge90/stimmen/de.json
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"igel/e1": {
|
||||||
|
"datei": "igel-e1.mp3",
|
||||||
|
"dauer": 3,
|
||||||
|
"text": "Was frisst ein Igel? Was meinst du?"
|
||||||
|
},
|
||||||
|
"igel/p1": {
|
||||||
|
"datei": "igel-p1.mp3",
|
||||||
|
"dauer": 1.65,
|
||||||
|
"text": "Ich verrate es dir!"
|
||||||
|
},
|
||||||
|
"igel/e2": {
|
||||||
|
"datei": "igel-e2.mp3",
|
||||||
|
"dauer": 2.04,
|
||||||
|
"text": "Igel fressen Käfer,"
|
||||||
|
},
|
||||||
|
"igel/e3": {
|
||||||
|
"datei": "igel-e3.mp3",
|
||||||
|
"dauer": 1.07,
|
||||||
|
"text": "Würmer"
|
||||||
|
},
|
||||||
|
"igel/e4": {
|
||||||
|
"datei": "igel-e4.mp3",
|
||||||
|
"dauer": 1.49,
|
||||||
|
"text": "und Schnecken."
|
||||||
|
},
|
||||||
|
"igel/p2": {
|
||||||
|
"datei": "igel-p2.mp3",
|
||||||
|
"dauer": 2.12,
|
||||||
|
"text": "Mmmh, lecker!"
|
||||||
|
},
|
||||||
|
"igel/e5": {
|
||||||
|
"datei": "igel-e5.mp3",
|
||||||
|
"dauer": 4.91,
|
||||||
|
"text": "Milch und Obst sind nichts für Igel. Davon bekommen sie Bauchweh."
|
||||||
|
},
|
||||||
|
"igel/p3": {
|
||||||
|
"datei": "igel-p3.mp3",
|
||||||
|
"dauer": 2.27,
|
||||||
|
"text": "Den Apfel schenke ich Noa!"
|
||||||
|
},
|
||||||
|
"giraffe/e1": {
|
||||||
|
"datei": "giraffe-e1.mp3",
|
||||||
|
"dauer": 4.36,
|
||||||
|
"text": "Wer ist am größten? Pino, Noa oder Lulu?"
|
||||||
|
},
|
||||||
|
"giraffe/e2": {
|
||||||
|
"datei": "giraffe-e2.mp3",
|
||||||
|
"dauer": 1.72,
|
||||||
|
"text": "Pino ist klein."
|
||||||
|
},
|
||||||
|
"giraffe/e3": {
|
||||||
|
"datei": "giraffe-e3.mp3",
|
||||||
|
"dauer": 1.8,
|
||||||
|
"text": "Noa ist mittel."
|
||||||
|
},
|
||||||
|
"giraffe/e4": {
|
||||||
|
"datei": "giraffe-e4.mp3",
|
||||||
|
"dauer": 2.69,
|
||||||
|
"text": "Und Lulu ist ganz groß!"
|
||||||
|
},
|
||||||
|
"giraffe/l1": {
|
||||||
|
"datei": "giraffe-l1.mp3",
|
||||||
|
"dauer": 1.96,
|
||||||
|
"text": "Ich bin eine Giraffe!"
|
||||||
|
},
|
||||||
|
"giraffe/e5": {
|
||||||
|
"datei": "giraffe-e5.mp3",
|
||||||
|
"dauer": 5.88,
|
||||||
|
"text": "Kein Tier auf der Welt ist so hoch wie eine Giraffe. So hoch wie ein Haus!"
|
||||||
|
},
|
||||||
|
"giraffe/e6": {
|
||||||
|
"datei": "giraffe-e6.mp3",
|
||||||
|
"dauer": 3.47,
|
||||||
|
"text": "Sag mit: klein, mittel, groß!"
|
||||||
|
}
|
||||||
|
}
|
||||||
77
remotion/src/folge90/stimmen/es.json
Normal file
77
remotion/src/folge90/stimmen/es.json
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"igel/e1": {
|
||||||
|
"datei": "igel-e1.mp3",
|
||||||
|
"dauer": 2.85,
|
||||||
|
"text": "¿Qué come un erizo? ¿Tú qué crees?"
|
||||||
|
},
|
||||||
|
"igel/p1": {
|
||||||
|
"datei": "igel-p1.mp3",
|
||||||
|
"dauer": 1.15,
|
||||||
|
"text": "¡Te lo cuento!"
|
||||||
|
},
|
||||||
|
"igel/e2": {
|
||||||
|
"datei": "igel-e2.mp3",
|
||||||
|
"dauer": 2.77,
|
||||||
|
"text": "Los erizos comen escarabajos,"
|
||||||
|
},
|
||||||
|
"igel/e3": {
|
||||||
|
"datei": "igel-e3.mp3",
|
||||||
|
"dauer": 1.23,
|
||||||
|
"text": "gusanos"
|
||||||
|
},
|
||||||
|
"igel/e4": {
|
||||||
|
"datei": "igel-e4.mp3",
|
||||||
|
"dauer": 1.41,
|
||||||
|
"text": "y caracoles."
|
||||||
|
},
|
||||||
|
"igel/p2": {
|
||||||
|
"datei": "igel-p2.mp3",
|
||||||
|
"dauer": 2.12,
|
||||||
|
"text": "¡Mmm, qué rico!"
|
||||||
|
},
|
||||||
|
"igel/e5": {
|
||||||
|
"datei": "igel-e5.mp3",
|
||||||
|
"dauer": 4.91,
|
||||||
|
"text": "La leche y la fruta no son para erizos. Les duele la tripa."
|
||||||
|
},
|
||||||
|
"igel/p3": {
|
||||||
|
"datei": "igel-p3.mp3",
|
||||||
|
"dauer": 2.51,
|
||||||
|
"text": "¡La manzana se la regalo a Noa!"
|
||||||
|
},
|
||||||
|
"giraffe/e1": {
|
||||||
|
"datei": "giraffe-e1.mp3",
|
||||||
|
"dauer": 3.97,
|
||||||
|
"text": "¿Quién es el más alto? ¿Pino, Noa o Lulu?"
|
||||||
|
},
|
||||||
|
"giraffe/e2": {
|
||||||
|
"datei": "giraffe-e2.mp3",
|
||||||
|
"dauer": 1.72,
|
||||||
|
"text": "Pino es pequeño."
|
||||||
|
},
|
||||||
|
"giraffe/e3": {
|
||||||
|
"datei": "giraffe-e3.mp3",
|
||||||
|
"dauer": 1.8,
|
||||||
|
"text": "Noa es mediana."
|
||||||
|
},
|
||||||
|
"giraffe/e4": {
|
||||||
|
"datei": "giraffe-e4.mp3",
|
||||||
|
"dauer": 2.51,
|
||||||
|
"text": "¡Y Lulu es muy alta!"
|
||||||
|
},
|
||||||
|
"giraffe/l1": {
|
||||||
|
"datei": "giraffe-l1.mp3",
|
||||||
|
"dauer": 1.96,
|
||||||
|
"text": "¡Soy una jirafa!"
|
||||||
|
},
|
||||||
|
"giraffe/e5": {
|
||||||
|
"datei": "giraffe-e5.mp3",
|
||||||
|
"dauer": 5.88,
|
||||||
|
"text": "Ningún animal del mundo es tan alto como una jirafa. ¡Tan alto como una casa!"
|
||||||
|
},
|
||||||
|
"giraffe/e6": {
|
||||||
|
"datei": "giraffe-e6.mp3",
|
||||||
|
"dauer": 4.36,
|
||||||
|
"text": "Dilo conmigo: pequeño, mediano, grande."
|
||||||
|
}
|
||||||
|
}
|
||||||
77
remotion/src/folge90/stimmen/fr.json
Normal file
77
remotion/src/folge90/stimmen/fr.json
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"igel/e1": {
|
||||||
|
"datei": "igel-e1.mp3",
|
||||||
|
"dauer": 2.59,
|
||||||
|
"text": "Que mange un hérisson ? Qu'est-ce que tu crois ?"
|
||||||
|
},
|
||||||
|
"igel/p1": {
|
||||||
|
"datei": "igel-p1.mp3",
|
||||||
|
"dauer": 1.57,
|
||||||
|
"text": "Je vais te le dire !"
|
||||||
|
},
|
||||||
|
"igel/e2": {
|
||||||
|
"datei": "igel-e2.mp3",
|
||||||
|
"dauer": 2.59,
|
||||||
|
"text": "Les hérissons mangent des scarabées,"
|
||||||
|
},
|
||||||
|
"igel/e3": {
|
||||||
|
"datei": "igel-e3.mp3",
|
||||||
|
"dauer": 1.8,
|
||||||
|
"text": "des vers de terre"
|
||||||
|
},
|
||||||
|
"igel/e4": {
|
||||||
|
"datei": "igel-e4.mp3",
|
||||||
|
"dauer": 1.49,
|
||||||
|
"text": "et des escargots."
|
||||||
|
},
|
||||||
|
"igel/p2": {
|
||||||
|
"datei": "igel-p2.mp3",
|
||||||
|
"dauer": 1.96,
|
||||||
|
"text": "Miam, c'est bon !"
|
||||||
|
},
|
||||||
|
"igel/e5": {
|
||||||
|
"datei": "igel-e5.mp3",
|
||||||
|
"dauer": 4.83,
|
||||||
|
"text": "Le lait et les fruits, ce n'est pas pour les hérissons. Ça leur donne mal au ventre."
|
||||||
|
},
|
||||||
|
"igel/p3": {
|
||||||
|
"datei": "igel-p3.mp3",
|
||||||
|
"dauer": 2.27,
|
||||||
|
"text": "La pomme, je la donne à Noa !"
|
||||||
|
},
|
||||||
|
"giraffe/e1": {
|
||||||
|
"datei": "giraffe-e1.mp3",
|
||||||
|
"dauer": 3.79,
|
||||||
|
"text": "Qui est le plus grand ? Pino, Noa ou Lulu ?"
|
||||||
|
},
|
||||||
|
"giraffe/e2": {
|
||||||
|
"datei": "giraffe-e2.mp3",
|
||||||
|
"dauer": 1.65,
|
||||||
|
"text": "Pino est petit."
|
||||||
|
},
|
||||||
|
"giraffe/e3": {
|
||||||
|
"datei": "giraffe-e3.mp3",
|
||||||
|
"dauer": 1.72,
|
||||||
|
"text": "Noa est moyenne."
|
||||||
|
},
|
||||||
|
"giraffe/e4": {
|
||||||
|
"datei": "giraffe-e4.mp3",
|
||||||
|
"dauer": 2.43,
|
||||||
|
"text": "Et Lulu est très grande !"
|
||||||
|
},
|
||||||
|
"giraffe/l1": {
|
||||||
|
"datei": "giraffe-l1.mp3",
|
||||||
|
"dauer": 1.8,
|
||||||
|
"text": "Je suis une girafe !"
|
||||||
|
},
|
||||||
|
"giraffe/e5": {
|
||||||
|
"datei": "giraffe-e5.mp3",
|
||||||
|
"dauer": 5.25,
|
||||||
|
"text": "Aucun animal au monde n'est aussi haut qu'une girafe. Aussi haut qu'une maison !"
|
||||||
|
},
|
||||||
|
"giraffe/e6": {
|
||||||
|
"datei": "giraffe-e6.mp3",
|
||||||
|
"dauer": 3.63,
|
||||||
|
"text": "Dis avec moi : petit, moyen, grand !"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
remotion/src/folge90/texte/de.json
Normal file
26
remotion/src/folge90/texte/de.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"titel": "Pinos Wissen",
|
||||||
|
"zeilen": {
|
||||||
|
"igel/e1": "Was frisst ein Igel? Was meinst du?",
|
||||||
|
"igel/p1": "Ich verrate es dir!",
|
||||||
|
"igel/e2": "Igel fressen Käfer,",
|
||||||
|
"igel/e3": "Würmer",
|
||||||
|
"igel/e4": "und Schnecken.",
|
||||||
|
"igel/p2": "Mmmh, lecker!",
|
||||||
|
"igel/e5": "Milch und Obst sind nichts für Igel. Davon bekommen sie Bauchweh.",
|
||||||
|
"igel/p3": "Den Apfel schenke ich Noa!",
|
||||||
|
"giraffe/e1": "Wer ist am größten? Pino, Noa oder Lulu?",
|
||||||
|
"giraffe/e2": "Pino ist klein.",
|
||||||
|
"giraffe/e3": "Noa ist mittel.",
|
||||||
|
"giraffe/e4": "Und Lulu ist ganz groß!",
|
||||||
|
"giraffe/l1": "Ich bin eine Giraffe!",
|
||||||
|
"giraffe/e5": "Kein Tier auf der Welt ist so hoch wie eine Giraffe. So hoch wie ein Haus!",
|
||||||
|
"giraffe/e6": "Sag mit: klein, mittel, groß!",
|
||||||
|
"igel/frage": "Was frisst ein Igel?",
|
||||||
|
"giraffe/frage": "Wer ist am größten?",
|
||||||
|
"giraffe/w1": "klein",
|
||||||
|
"giraffe/w2": "mittel",
|
||||||
|
"giraffe/w3": "groß",
|
||||||
|
"kanal": "Pinos kleine Welt"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
remotion/src/folge90/texte/es.json
Normal file
26
remotion/src/folge90/texte/es.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"titel": "Pino sabe",
|
||||||
|
"zeilen": {
|
||||||
|
"igel/e1": "¿Qué come un erizo? ¿Tú qué crees?",
|
||||||
|
"igel/p1": "¡Te lo cuento!",
|
||||||
|
"igel/e2": "Los erizos comen escarabajos,",
|
||||||
|
"igel/e3": "gusanos",
|
||||||
|
"igel/e4": "y caracoles.",
|
||||||
|
"igel/p2": "¡Mmm, qué rico!",
|
||||||
|
"igel/e5": "La leche y la fruta no son para erizos. Les duele la tripa.",
|
||||||
|
"igel/p3": "¡La manzana se la regalo a Noa!",
|
||||||
|
"giraffe/e1": "¿Quién es el más alto? ¿Pino, Noa o Lulu?",
|
||||||
|
"giraffe/e2": "Pino es pequeño.",
|
||||||
|
"giraffe/e3": "Noa es mediana.",
|
||||||
|
"giraffe/e4": "¡Y Lulu es muy alta!",
|
||||||
|
"giraffe/l1": "¡Soy una jirafa!",
|
||||||
|
"giraffe/e5": "Ningún animal del mundo es tan alto como una jirafa. ¡Tan alto como una casa!",
|
||||||
|
"giraffe/e6": "Dilo conmigo: pequeño, mediano, grande.",
|
||||||
|
"igel/frage": "¿Qué come un erizo?",
|
||||||
|
"giraffe/frage": "¿Quién es el más alto?",
|
||||||
|
"giraffe/w1": "pequeño",
|
||||||
|
"giraffe/w2": "mediano",
|
||||||
|
"giraffe/w3": "grande",
|
||||||
|
"kanal": "El pequeño mundo de Pino"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
remotion/src/folge90/texte/fr.json
Normal file
26
remotion/src/folge90/texte/fr.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"titel": "Pino sait tout",
|
||||||
|
"zeilen": {
|
||||||
|
"igel/e1": "Que mange un hérisson ? Qu'est-ce que tu crois ?",
|
||||||
|
"igel/p1": "Je vais te le dire !",
|
||||||
|
"igel/e2": "Les hérissons mangent des scarabées,",
|
||||||
|
"igel/e3": "des vers de terre",
|
||||||
|
"igel/e4": "et des escargots.",
|
||||||
|
"igel/p2": "Miam, c'est bon !",
|
||||||
|
"igel/e5": "Le lait et les fruits, ce n'est pas pour les hérissons. Ça leur donne mal au ventre.",
|
||||||
|
"igel/p3": "La pomme, je la donne à Noa !",
|
||||||
|
"giraffe/e1": "Qui est le plus grand ? Pino, Noa ou Lulu ?",
|
||||||
|
"giraffe/e2": "Pino est petit.",
|
||||||
|
"giraffe/e3": "Noa est moyenne.",
|
||||||
|
"giraffe/e4": "Et Lulu est très grande !",
|
||||||
|
"giraffe/l1": "Je suis une girafe !",
|
||||||
|
"giraffe/e5": "Aucun animal au monde n'est aussi haut qu'une girafe. Aussi haut qu'une maison !",
|
||||||
|
"giraffe/e6": "Dis avec moi : petit, moyen, grand !",
|
||||||
|
"igel/frage": "Que mange un hérisson ?",
|
||||||
|
"giraffe/frage": "Qui est le plus grand ?",
|
||||||
|
"giraffe/w1": "petit",
|
||||||
|
"giraffe/w2": "moyen",
|
||||||
|
"giraffe/w3": "grand",
|
||||||
|
"kanal": "Le petit monde de Pino"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue