Complete architectural rewrite with ultra-modular system
Major Changes: - Moved legacy system to Legacy/ folder for archival - Built new modular architecture with strict separation of concerns - Created core system: Module, EventBus, ModuleLoader, Router - Added Application bootstrap with auto-start functionality - Implemented development server with ES6 modules support - Created comprehensive documentation and project context - Converted SBS-7-8 content to JSON format - Copied all legacy games and content to new structure New Architecture Features: - Sealed modules with WeakMap private data - Strict dependency injection system - Event-driven communication only - Inviolable responsibility patterns - Auto-initialization without commands - Component-based UI foundation ready Technical Stack: - Vanilla JS/HTML/CSS only - ES6 modules with proper imports/exports - HTTP development server (no file:// protocol) - Modular CSS with component scoping - Comprehensive error handling and debugging Ready for Phase 2: Converting legacy modules to new architecture 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
79a2cd3778
commit
38920cc858
804
Legacy/CLAUDE.md
Normal file
804
Legacy/CLAUDE.md
Normal file
@ -0,0 +1,804 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## 🎯 IMPORTANT: Check TODO.md First!
|
||||
|
||||
**ALWAYS check `TODO.md` for the current project tasks and priorities before making any changes.**
|
||||
|
||||
The `TODO.md` file contains:
|
||||
- 🔥 Current tasks in progress
|
||||
- 📋 Pending features to implement
|
||||
- 🚨 Known issues and blockers
|
||||
- ✅ Completed work for reference
|
||||
|
||||
**Make sure to update TODO.md when:**
|
||||
- Starting a new task
|
||||
- Completing a task
|
||||
- Discovering new issues
|
||||
- Planning future improvements
|
||||
|
||||
## Project Overview
|
||||
|
||||
Interactive English learning platform for children (8-9 years old) built as a modular Single Page Application. The system provides 9 different educational games that work with various content modules through a flexible architecture.
|
||||
|
||||
## Key Architecture Patterns
|
||||
|
||||
### Core System Flow
|
||||
1. **AppNavigation** (`js/core/navigation.js`) - Central SPA navigation controller
|
||||
2. **ContentScanner** (`js/core/content-scanner.js`) - Auto-discovers available content modules
|
||||
3. **GameLoader** (`js/core/game-loader.js`) - Dynamically loads game and content modules
|
||||
4. **Content Engine** (`js/core/content-engine.js`) - Processes and adapts content for games
|
||||
|
||||
### Module Loading System
|
||||
- Games and content are loaded dynamically via `GameLoader.loadGame(gameType, contentType)`
|
||||
- All modules register themselves on global objects: `window.GameModules` and `window.ContentModules`
|
||||
- Content is discovered automatically by `ContentScanner` scanning `js/content/` directory
|
||||
- **JSON Content Support**: New JSON-first architecture with backward compatibility to JS modules
|
||||
- **JSON Content Loader**: `js/core/json-content-loader.js` transforms JSON content to legacy game format
|
||||
- **Offline-First Loading**: Content loads from local files first, with DigitalOcean Spaces fallback
|
||||
- Games follow consistent constructor pattern: `new GameClass({ container, content, onScoreUpdate, onGameEnd })`
|
||||
|
||||
### URL-Based Navigation
|
||||
- Single HTML file (`index.html`) handles all navigation via URL parameters
|
||||
- Routes: `?page=home|games|levels|play&game=<gameType>&content=<contentType>`
|
||||
- Browser back/forward supported through `popstate` events
|
||||
- Navigation history maintained in `AppNavigation.navigationHistory`
|
||||
- Breadcrumb navigation with clickable path elements
|
||||
- **Top Bar**: Fixed header with app title and permanent network status indicator
|
||||
- **Network Status**: Real-time connectivity indicator (🟢 Online / 🟠 Connecting / 🔴 Offline)
|
||||
- Keyboard shortcuts (ESC = go back)
|
||||
|
||||
## Content Module Format
|
||||
|
||||
### Rich Content Schema (New Architecture)
|
||||
|
||||
Content modules support rich, multimedia educational content with optional properties. The system adapts games and exercises based on available content features:
|
||||
|
||||
```javascript
|
||||
window.ContentModules.ModuleName = {
|
||||
name: "Display Name",
|
||||
description: "Description text",
|
||||
difficulty: "easy|medium|hard|beginner|intermediate|advanced",
|
||||
language: "chinese|english|french|spanish", // Target learning language
|
||||
hskLevel: "HSK1|HSK2|HSK3|HSK4|HSK5|HSK6", // Chinese proficiency level
|
||||
|
||||
// Rich vocabulary with optional multimedia
|
||||
vocabulary: {
|
||||
"word_or_character": {
|
||||
translation: "English translation",
|
||||
prononciation: "pronunciation guide", // Optional: pronunciation guide
|
||||
type: "noun|verb|adjective|greeting|number", // Word classification
|
||||
pronunciation: "audio/word.mp3", // Optional: audio file
|
||||
difficulty: "HSK1|HSK2|...", // Optional: individual word difficulty
|
||||
strokeOrder: ["stroke1", "stroke2"], // Optional: character writing order
|
||||
examples: ["example sentence 1"], // Optional: usage examples
|
||||
grammarNotes: "special usage rules" // Optional: grammar context
|
||||
}
|
||||
// OR simple format for basic content:
|
||||
// "word": "simple translation"
|
||||
},
|
||||
|
||||
// Grammar rules and explanations
|
||||
grammar: {
|
||||
topic_name: {
|
||||
title: "Grammar Rule Title",
|
||||
explanation: "Detailed explanation",
|
||||
examples: [
|
||||
{ chinese: "中文例子", english: "English example", prononciation: "zhōng wén lì zi" }
|
||||
],
|
||||
exercises: [/* grammar-specific exercises */]
|
||||
}
|
||||
},
|
||||
|
||||
// Audio content with/without text
|
||||
audio: {
|
||||
withText: [
|
||||
{
|
||||
title: "Audio Lesson Title",
|
||||
audioFile: "audio/lesson1.mp3",
|
||||
transcript: "Full text transcript",
|
||||
translation: "English translation",
|
||||
timestamps: [{ time: 5.2, text: "specific segment" }] // Optional
|
||||
}
|
||||
],
|
||||
withoutText: [
|
||||
{
|
||||
title: "Listening Challenge",
|
||||
audioFile: "audio/challenge1.mp3",
|
||||
questions: [
|
||||
{ question: "What did they say?", type: "ai_interpreted" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Poetry and cultural content
|
||||
poems: [
|
||||
{
|
||||
title: "Poem Title",
|
||||
content: "Full poem text",
|
||||
translation: "English translation",
|
||||
audioFile: "audio/poem1.mp3", // Optional
|
||||
culturalContext: "Historical background"
|
||||
}
|
||||
],
|
||||
|
||||
// Fill-in-the-blank exercises
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "I _____ to school every day",
|
||||
options: ["go", "goes", "going", "went"], // Multiple choice options
|
||||
correctAnswer: "go",
|
||||
explanation: "Present tense with 'I'"
|
||||
},
|
||||
{
|
||||
sentence: "The weather is _____ today",
|
||||
type: "open_ended", // AI-interpreted answers
|
||||
acceptedAnswers: ["nice", "good", "beautiful", "sunny"],
|
||||
aiPrompt: "Evaluate if answer describes weather positively"
|
||||
}
|
||||
],
|
||||
|
||||
// Sentence correction exercises
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "I are happy today",
|
||||
correct: "I am happy today",
|
||||
explanation: "Use 'am' with pronoun 'I'",
|
||||
type: "grammar_correction"
|
||||
}
|
||||
],
|
||||
|
||||
// Reading comprehension with AI evaluation
|
||||
comprehension: [
|
||||
{
|
||||
text: "Long reading passage...",
|
||||
questions: [
|
||||
{
|
||||
question: "What is the main idea?",
|
||||
type: "ai_interpreted",
|
||||
evaluationPrompt: "Check if answer captures main theme"
|
||||
},
|
||||
{
|
||||
question: "Multiple choice question?",
|
||||
type: "multiple_choice",
|
||||
options: ["A", "B", "C", "D"],
|
||||
correctAnswer: "B"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
// Matching exercises (connect lines between columns)
|
||||
matching: [
|
||||
{
|
||||
title: "Match Words to Meanings",
|
||||
leftColumn: ["apple", "book", "car"],
|
||||
rightColumn: ["苹果", "书", "车"],
|
||||
correctPairs: [
|
||||
{ left: "apple", right: "苹果" },
|
||||
{ left: "book", right: "书" },
|
||||
{ left: "car", right: "车" }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
// Standard content (backward compatibility)
|
||||
sentences: [{ english: "...", chinese: "...", prononciation: "..." }],
|
||||
texts: [{ title: "...", content: "...", translation: "..." }],
|
||||
dialogues: [{ conversation: [...] }]
|
||||
};
|
||||
```
|
||||
|
||||
### JSON Content Format (New Architecture)
|
||||
|
||||
The platform now supports JSON content files for easier editing and maintenance:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Content Name",
|
||||
"description": "Content description",
|
||||
"difficulty": "easy|medium|hard",
|
||||
"vocabulary": {
|
||||
"word": {
|
||||
"translation": "French translation",
|
||||
"prononciation": "pronunciation guide",
|
||||
"type": "noun|verb|adjective"
|
||||
}
|
||||
},
|
||||
"sentences": [
|
||||
{
|
||||
"english": "English sentence",
|
||||
"chinese": "Chinese translation",
|
||||
"prononciation": "pronunciation"
|
||||
}
|
||||
],
|
||||
"grammar": { /* grammar rules */ },
|
||||
"audio": { /* audio content */ },
|
||||
"exercises": { /* exercise definitions */ }
|
||||
}
|
||||
```
|
||||
|
||||
**JSON Content Loader Features:**
|
||||
- Automatic transformation from JSON to legacy game format
|
||||
- Backward compatibility with existing JavaScript content modules
|
||||
- Support for all rich content features (vocabulary, grammar, audio, exercises)
|
||||
- Offline-first loading with cloud fallback
|
||||
|
||||
### Content Adaptivity System
|
||||
|
||||
The platform automatically adapts available games and exercises based on content richness:
|
||||
|
||||
**Content Analysis:**
|
||||
- System scans each content module for available features
|
||||
- Generates compatibility scores for each game type
|
||||
- Recommends optimal learning activities
|
||||
- Handles graceful degradation when content is incomplete
|
||||
|
||||
**Adaptive Game Selection:**
|
||||
- **Rich vocabulary** → Enable advanced matching games, pronunciation practice
|
||||
- **Audio files present** → Enable listening exercises, pronunciation challenges
|
||||
- **Grammar rules** → Enable correction exercises, structured lessons
|
||||
- **Fill-in-blanks data** → Enable cloze tests with multiple choice or AI evaluation
|
||||
- **Minimal content** → Fall back to basic vocabulary games
|
||||
|
||||
**Missing Content Handling:**
|
||||
- Display helpful messages: "Add audio files to enable pronunciation practice"
|
||||
- Suggest content enrichment opportunities
|
||||
- Gracefully disable incompatible game modes
|
||||
- Provide content creation tools for missing elements
|
||||
|
||||
**Example Adaptive Behavior:**
|
||||
```javascript
|
||||
// Content with only basic vocabulary
|
||||
{ vocabulary: { "hello": "你好" } }
|
||||
→ Enable: Basic matching, simple quiz
|
||||
→ Disable: Audio practice, grammar exercises
|
||||
→ Suggest: "Add pronunciation guide and audio for pronunciation practice"
|
||||
|
||||
// Rich multimedia content
|
||||
{ vocabulary: { "hello": { translation: "你好", prononciation: "nǐ hǎo", pronunciation: "audio/hello.mp3" } } }
|
||||
→ Enable: All vocabulary games, audio practice, pronunciation scoring
|
||||
→ Unlock: Advanced difficulty levels, speed challenges
|
||||
```
|
||||
|
||||
## 🚨 CRITICAL ARCHITECTURE GUIDELINES
|
||||
|
||||
**NEVER violate these principles to maintain system modularity and maintainability:**
|
||||
|
||||
### 🎨 CSS Architecture Rules
|
||||
|
||||
1. **NEVER modify `css/games.css` for game-specific styles**
|
||||
- Global CSS is only for shared, reusable components
|
||||
- Game-specific styles MUST be injected by the game itself
|
||||
|
||||
2. **USE the Global CSS Base System:**
|
||||
```javascript
|
||||
// ✅ CORRECT: Use global classes with specific overrides
|
||||
<div class="game-wrapper compact"> // Global base class
|
||||
<div class="game-hud"> // Global HUD structure
|
||||
<div class="game-area"> // Global game area
|
||||
<div class="answer-panel"> // Global answer panel
|
||||
|
||||
// ✅ CORRECT: Inject game-specific CSS
|
||||
injectCSS() {
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = `
|
||||
.my-game-specific-element { /* Game-only styles */ }
|
||||
`;
|
||||
document.head.appendChild(styleSheet);
|
||||
}
|
||||
```
|
||||
|
||||
3. **CSS Classes Hierarchy:**
|
||||
- `.game-wrapper` - Base container (full screen)
|
||||
- `.game-wrapper.compact` - Smaller viewport variant
|
||||
- `.game-hud` - Top information bar
|
||||
- `.game-area` - Main play zone
|
||||
- `.answer-panel` - Bottom interaction zone
|
||||
- `.answer-btn` - Interactive buttons
|
||||
|
||||
4. **❌ FORBIDDEN PATTERNS:**
|
||||
```css
|
||||
/* ❌ NEVER add game-specific classes to games.css */
|
||||
.word-storm-wrapper { }
|
||||
.my-game-specific-class { }
|
||||
|
||||
/* ❌ NEVER hardcode game-specific dimensions in global CSS */
|
||||
.game-area { height: 600px; } /* This breaks other games */
|
||||
```
|
||||
|
||||
### 🎮 Game Development Standards
|
||||
|
||||
1. **Self-Contained Games:**
|
||||
- Each game MUST inject its own CSS via `injectCSS()`
|
||||
- Games MUST use global base classes where possible
|
||||
- Game-specific elements get their own CSS only
|
||||
|
||||
2. **Constructor Pattern (REQUIRED):**
|
||||
```javascript
|
||||
class MyGame {
|
||||
constructor({ container, content, onScoreUpdate, onGameEnd }) {
|
||||
this.injectCSS(); // Inject game-specific styles
|
||||
this.init(); // Setup interface
|
||||
}
|
||||
|
||||
start() { /* Start game logic */ }
|
||||
destroy() { /* Cleanup */ }
|
||||
}
|
||||
```
|
||||
|
||||
3. **Module Registration (REQUIRED):**
|
||||
```javascript
|
||||
// MUST be at end of game file
|
||||
window.GameModules = window.GameModules || {};
|
||||
window.GameModules.MyGame = MyGame;
|
||||
```
|
||||
|
||||
4. **GameLoader Integration:**
|
||||
- Add game mapping in `game-loader.js` → `getModuleName()`
|
||||
- Add compatibility rules in `content-game-compatibility.js`
|
||||
- Add game config in `navigation.js` → game configuration
|
||||
|
||||
### 🔧 Modification Guidelines
|
||||
|
||||
**When adding new games:**
|
||||
1. ✅ Use existing global CSS classes
|
||||
2. ✅ Inject only game-specific CSS
|
||||
3. ✅ Follow constructor pattern
|
||||
4. ✅ Add to GameLoader mapping
|
||||
|
||||
**When modifying existing games:**
|
||||
1. ✅ Keep changes within the game file
|
||||
2. ✅ Don't break global CSS compatibility
|
||||
3. ✅ Test with multiple content types
|
||||
|
||||
**When adding global features:**
|
||||
1. ✅ Add to global CSS base classes
|
||||
2. ✅ Ensure backward compatibility
|
||||
3. ✅ Update this documentation
|
||||
|
||||
### 🚀 Benefits of This Architecture
|
||||
|
||||
- **Modularity**: Each game is self-contained
|
||||
- **Reusability**: Base classes work for all games
|
||||
- **Maintainability**: Changes don't break other games
|
||||
- **Performance**: Only load CSS when game is used
|
||||
- **Scalability**: Easy to add new games
|
||||
|
||||
### ⚠️ Common Mistakes to Avoid
|
||||
|
||||
**CSS Architecture Violations:**
|
||||
```css
|
||||
/* ❌ DON'T: Adding game-specific styles to games.css */
|
||||
.word-storm-wrapper { height: 80vh; width: 90vw; }
|
||||
|
||||
/* ✅ DO: Use global classes with game-specific injection */
|
||||
// In game file:
|
||||
injectCSS() {
|
||||
// Game-specific overrides only
|
||||
styleSheet.textContent = `.falling-word { animation: wordGlow 2s; }`;
|
||||
}
|
||||
```
|
||||
|
||||
**Module Loading Issues:**
|
||||
```javascript
|
||||
// ❌ DON'T: Forget GameLoader mapping
|
||||
// Game won't load because getModuleName() doesn't know about it
|
||||
|
||||
// ✅ DO: Add to game-loader.js
|
||||
const names = {
|
||||
'my-game': 'MyGame' // Add this mapping
|
||||
};
|
||||
```
|
||||
|
||||
**HTML Structure Violations:**
|
||||
```html
|
||||
<!-- ❌ DON'T: Create custom wrapper classes -->
|
||||
<div class="my-custom-game-wrapper">
|
||||
|
||||
<!-- ✅ DO: Use standard global structure -->
|
||||
<div class="game-wrapper compact">
|
||||
<div class="game-hud">
|
||||
<div class="game-area">
|
||||
<div class="answer-panel">
|
||||
```
|
||||
|
||||
**Integration Checklist:**
|
||||
- [ ] Game CSS injected via `injectCSS()`
|
||||
- [ ] Global classes used for structure
|
||||
- [ ] GameLoader mapping added
|
||||
- [ ] Compatibility rules defined
|
||||
- [ ] Constructor pattern followed
|
||||
- [ ] Module registration at file end
|
||||
|
||||
## Game Module Format
|
||||
|
||||
Game modules must export to `window.GameModules` with this pattern:
|
||||
```javascript
|
||||
class GameName {
|
||||
constructor({ container, content, onScoreUpdate, onGameEnd }) {
|
||||
this.container = container;
|
||||
this.content = content;
|
||||
this.onScoreUpdate = onScoreUpdate;
|
||||
this.onGameEnd = onGameEnd;
|
||||
}
|
||||
|
||||
start() { /* Initialize game */ }
|
||||
destroy() { /* Cleanup */ }
|
||||
restart() { /* Reset game state */ }
|
||||
}
|
||||
|
||||
window.GameModules = window.GameModules || {};
|
||||
window.GameModules.GameName = GameName;
|
||||
```
|
||||
|
||||
## Configuration System
|
||||
|
||||
- **Main config**: `config/games-config.json` - defines available games and content
|
||||
- **Environment config**: `js/core/env-config.js` - DigitalOcean Spaces configuration and offline settings
|
||||
- **Content discovery**: Automatic scanning of both `.js` and `.json` content files
|
||||
- Games can be enabled/disabled via `games.{gameType}.enabled`
|
||||
- **Cloud Integration**: DigitalOcean Spaces endpoint configuration for remote content
|
||||
- **Offline-First Strategy**: Local content prioritized, remote fallback with timeout protection
|
||||
- UI settings, scoring rules, and feature flags also in main config
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Running the Application
|
||||
Open `index.html` in a web browser - no build process required. All modules load dynamically.
|
||||
|
||||
### Adding New Games
|
||||
1. Create `js/games/{game-name}.js` with proper module export
|
||||
2. Add game configuration to `config/games-config.json`
|
||||
3. Update `AppNavigation.getDefaultConfig()` if needed
|
||||
|
||||
### Adding New Content
|
||||
**Option 1: JSON Format (Recommended)**
|
||||
1. Create `js/content/{content-name}.json` with proper JSON structure
|
||||
2. Content will be auto-discovered and loaded via JSON Content Loader
|
||||
3. Easier to edit and maintain than JavaScript files
|
||||
|
||||
**Option 2: JavaScript Format (Legacy)**
|
||||
1. Create `js/content/{content-name}.js` with proper module export
|
||||
2. Add filename to `ContentScanner.contentFiles` array
|
||||
3. Content will be auto-discovered on next app load
|
||||
|
||||
### Content Creation Tool
|
||||
- Built-in content creator at `js/tools/content-creator.js`
|
||||
- Accessible via "Créateur de Contenu" button on home page
|
||||
- Generates properly formatted content modules
|
||||
|
||||
## Key Files by Function
|
||||
|
||||
**Navigation & Loading:**
|
||||
- `js/core/navigation.js` - SPA navigation controller (452 lines)
|
||||
- `js/core/game-loader.js` - Dynamic module loading (336 lines)
|
||||
- `js/core/content-scanner.js` - Auto content discovery (376 lines)
|
||||
|
||||
**Content Processing:**
|
||||
- `js/core/content-engine.js` - Content processing engine (484 lines)
|
||||
- `js/core/content-factory.js` - Exercise generation (553 lines)
|
||||
- `js/core/content-parsers.js` - Content parsing utilities (484 lines)
|
||||
- `js/core/json-content-loader.js` - JSON to legacy format transformation
|
||||
- `js/core/env-config.js` - Environment and cloud configuration
|
||||
|
||||
**Game Implementations:**
|
||||
- `js/games/whack-a-mole.js` - Standard version (623 lines)
|
||||
- `js/games/whack-a-mole-hard.js` - Difficult version (643 lines)
|
||||
- `js/games/memory-match.js` - Memory pairs game (403 lines)
|
||||
- `js/games/quiz-game.js` - Quiz system (354 lines)
|
||||
- `js/games/fill-the-blank.js` - Sentence completion (418 lines)
|
||||
- `js/games/text-reader.js` - Guided text reading (366 lines)
|
||||
- `js/games/adventure-reader.js` - RPG-style adventure (949 lines)
|
||||
|
||||
## Important Implementation Details
|
||||
|
||||
### Scoring System
|
||||
- Games call `this.onScoreUpdate(score)` to update display
|
||||
- Final scores saved to localStorage with key pattern: `score_{gameType}_{contentType}`
|
||||
- Best scores tracked and displayed in game-end modal
|
||||
- Points per correct answer, malus per error, speed bonus
|
||||
- Score history and achievement badges
|
||||
|
||||
### Content Compatibility
|
||||
- `ContentScanner` evaluates content compatibility with each game type
|
||||
- Compatibility scoring helps recommend best content for each game
|
||||
- Games should handle various content formats gracefully
|
||||
|
||||
### Memory Management
|
||||
- `GameLoader.cleanup()` called before loading new games
|
||||
- Games should implement `destroy()` method for proper cleanup
|
||||
- Previous game instances must be cleaned up to prevent memory leaks
|
||||
|
||||
### Error Handling
|
||||
- Content loading errors logged but don't crash the application
|
||||
- Fallback mechanisms for missing content or games
|
||||
- User-friendly error messages via `Utils.showToast()`
|
||||
|
||||
## Design Guidelines
|
||||
|
||||
### Visual Design Principles
|
||||
- Modern, clean design optimized for children (8-9 years old)
|
||||
- Large, tactile buttons (minimum 44px for touch interfaces)
|
||||
- High contrast colors for accessibility
|
||||
- Smooth, non-aggressive animations
|
||||
- Emoji icons combined with text labels
|
||||
|
||||
### Color Palette
|
||||
- **Primary**: Blue (#3B82F6) - Trust, learning
|
||||
- **Secondary**: Green (#10B981) - Success, validation
|
||||
- **Accent**: Orange (#F59E0B) - Energy, attention
|
||||
- **Error**: Red (#EF4444) - Clear error indication
|
||||
- **Neutral**: Gray (#6B7280) - Text, backgrounds
|
||||
|
||||
### Accessibility Features
|
||||
- Full keyboard navigation support
|
||||
- Alternative text for all images
|
||||
- Adjustable font sizes
|
||||
- High contrast mode compatibility
|
||||
- Screen reader friendly markup
|
||||
|
||||
### Responsive Design
|
||||
- Mobile/tablet adaptation
|
||||
- Touch-friendly interface
|
||||
- Portrait/landscape orientation support
|
||||
- Fluid layouts that work on various screen sizes
|
||||
- **Fixed Top Bar**: App title and network status always visible
|
||||
- **Network Status**: Automatic hiding of status text on mobile devices
|
||||
- **Content Margin**: Proper spacing to accommodate fixed header
|
||||
|
||||
## Git Configuration
|
||||
|
||||
### Repository
|
||||
- **Remote**: Bitbucket repository at `AlexisTrouve/class-generator-system`
|
||||
- **Port 443 Configuration**: Git is configured to use SSH over port 443 for network restrictions
|
||||
- **Remote URL**: `ssh://git@altssh.bitbucket.org:443/AlexisTrouve/class-generator-system.git`
|
||||
|
||||
### SSH Configuration
|
||||
To push to the repository through port 443, the following SSH configuration is required in `~/.ssh/config`:
|
||||
```
|
||||
Host altssh.bitbucket.org
|
||||
HostName altssh.bitbucket.org
|
||||
Port 443
|
||||
User git
|
||||
IdentityFile ~/.ssh/bitbucket_key
|
||||
```
|
||||
|
||||
### Push Commands
|
||||
- Standard push: `git push`
|
||||
- Set upstream: `git push --set-upstream origin master`
|
||||
|
||||
## 🚨 **Developer Guidelines & Common Pitfalls**
|
||||
|
||||
**Critical information for future developers to avoid common mistakes and maintain code quality.**
|
||||
|
||||
### **🔥 Template Literals Syntax Errors**
|
||||
|
||||
**MOST COMMON BUG - Always check this first:**
|
||||
|
||||
```javascript
|
||||
// ❌ FATAL ERROR - Will break entire module
|
||||
styleSheet.textContent = \`css here\`; // Backslash = SyntaxError
|
||||
|
||||
// ✅ CORRECT
|
||||
styleSheet.textContent = `css here`; // Backtick (grave accent)
|
||||
```
|
||||
|
||||
**How to debug:**
|
||||
```bash
|
||||
# Test syntax before browser testing
|
||||
node -c js/games/your-game.js
|
||||
|
||||
# Look for "Invalid or unexpected token" errors
|
||||
# Usually points to template literal issues
|
||||
```
|
||||
|
||||
### **🎮 Game Development Best Practices**
|
||||
|
||||
#### **Required Game Structure:**
|
||||
```javascript
|
||||
class NewGame {
|
||||
constructor({ container, content, onScoreUpdate, onGameEnd }) {
|
||||
this.injectCSS(); // CSS injection FIRST
|
||||
this.extractContent(); // Content processing
|
||||
this.init(); // UI initialization
|
||||
}
|
||||
|
||||
start() {
|
||||
// Separate start method - NOT in constructor
|
||||
this.startGameLogic();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// Cleanup intervals, event listeners, injected CSS
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
// REQUIRED: Global module registration
|
||||
window.GameModules = window.GameModules || {};
|
||||
window.GameModules.NewGame = NewGame;
|
||||
```
|
||||
|
||||
#### **CSS Architecture - Zero Tolerance Policy:**
|
||||
|
||||
```javascript
|
||||
// ✅ CORRECT: Inject game-specific CSS
|
||||
injectCSS() {
|
||||
if (document.getElementById('my-game-styles')) return; // Prevent duplicates
|
||||
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.id = 'my-game-styles';
|
||||
styleSheet.textContent = `
|
||||
.my-game-element { color: red; }
|
||||
.falling-word { animation: myCustomAnimation 2s; }
|
||||
`;
|
||||
document.head.appendChild(styleSheet);
|
||||
}
|
||||
|
||||
// ❌ FORBIDDEN: Modifying css/games.css for game-specific styles
|
||||
// css/games.css should ONLY contain global reusable classes
|
||||
```
|
||||
|
||||
### **🔍 Debug Templates for Quick Testing**
|
||||
|
||||
#### **Isolated Game Testing:**
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="UTF-8"></head>
|
||||
<body>
|
||||
<div id="container" style="width:800px; height:600px; border:1px solid #ccc;"></div>
|
||||
|
||||
<script>
|
||||
window.logSh = (msg, level) => console.log(`[${level}] ${msg}`);
|
||||
window.Utils = { storage: { get: () => [], set: () => {} } };
|
||||
window.GameModules = {};
|
||||
window.ContentModules = {};
|
||||
</script>
|
||||
|
||||
<script src="js/content/your-content.js"></script>
|
||||
<script src="js/games/your-game.js"></script>
|
||||
|
||||
<script>
|
||||
try {
|
||||
const game = new window.GameModules.YourGame({
|
||||
container: document.getElementById('container'),
|
||||
content: window.ContentModules.YourContent,
|
||||
onScoreUpdate: score => console.log('Score:', score),
|
||||
onGameEnd: score => console.log('Game ended:', score)
|
||||
});
|
||||
if (game.start) game.start();
|
||||
console.log('✅ Game loaded successfully!');
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### **🌐 Interface Language Standards**
|
||||
|
||||
**All UI text must be in English:**
|
||||
|
||||
```javascript
|
||||
// ✅ CORRECT
|
||||
"Score: ${score}"
|
||||
"Lives: ${lives}"
|
||||
"Level Up!"
|
||||
"Game Over"
|
||||
"Back to Games"
|
||||
|
||||
// ❌ FORBIDDEN
|
||||
"Score: ${score} points" // French
|
||||
"Vies: ${lives}" // French
|
||||
"Niveau supérieur!" // French
|
||||
```
|
||||
|
||||
### **📋 Integration Checklist**
|
||||
|
||||
**Before committing any new game:**
|
||||
|
||||
- [ ] ✅ Syntax check: `node -c js/games/your-game.js`
|
||||
- [ ] ✅ CSS injected via `injectCSS()` method
|
||||
- [ ] ✅ No modifications to `css/games.css`
|
||||
- [ ] ✅ Uses global classes: `.game-wrapper`, `.game-hud`, `.game-area`, `.answer-panel`
|
||||
- [ ] ✅ GameLoader mapping added in `getModuleName()`
|
||||
- [ ] ✅ Navigation config updated in `navigation.js`
|
||||
- [ ] ✅ Constructor pattern followed exactly
|
||||
- [ ] ✅ Module export at end of file
|
||||
- [ ] ✅ English-only interface text
|
||||
- [ ] ✅ Isolated test file created and working
|
||||
|
||||
### **⚡ Performance & Architecture Notes**
|
||||
|
||||
**Current System Architecture:**
|
||||
- **CSS:** Global base classes + per-game injection
|
||||
- **Content:** Auto-discovery + JSON/JS dual support
|
||||
- **Navigation:** URL-based SPA with dynamic loading
|
||||
- **Modules:** Dynamic import + backward compatibility
|
||||
|
||||
**Critical Files (DO NOT BREAK):**
|
||||
- `js/core/game-loader.js` - Module name mapping
|
||||
- `css/games.css` - Global CSS base (game-agnostic only)
|
||||
- `js/core/navigation.js` - Game configuration and routing
|
||||
|
||||
### **🔧 Common Debugging Commands**
|
||||
|
||||
```bash
|
||||
# Check file syntax
|
||||
node -c js/games/your-game.js
|
||||
|
||||
# Check for non-ASCII characters (encoding issues)
|
||||
grep -P '[^\x00-\x7F]' js/games/your-game.js
|
||||
|
||||
# Find template literal issues
|
||||
grep -n "\\\`" js/games/your-game.js
|
||||
|
||||
# Test local server
|
||||
python3 -m http.server 8000
|
||||
# Then: http://localhost:8000/?page=play&game=your-game&content=available-content
|
||||
```
|
||||
|
||||
### **🚀 Quick Win Tips**
|
||||
|
||||
1. **Copy working game structure** - Use existing games as templates
|
||||
2. **Test isolated first** - Don't debug through the full app initially
|
||||
3. **Check browser console** - JavaScript errors are usually obvious
|
||||
4. **Verify content compatibility** - Make sure your content has the data your game needs
|
||||
5. **Use global CSS classes** - Don't reinvent layout, build on existing structure
|
||||
|
||||
**Remember: Most bugs are simple syntax errors (especially template literals) or missing module registrations. Check these first!** 🎯
|
||||
|
||||
## 🤝 **Collaborative Development Best Practices**
|
||||
|
||||
**Critical lesson learned from real debugging sessions:**
|
||||
|
||||
### **✅ Always Test Before Committing**
|
||||
|
||||
**❌ BAD WORKFLOW:**
|
||||
1. Write code
|
||||
2. Immediately commit
|
||||
3. Discover it doesn't work
|
||||
4. Debug on committed broken code
|
||||
|
||||
**✅ GOOD WORKFLOW:**
|
||||
1. Write code
|
||||
2. **TEST THOROUGHLY**
|
||||
3. If broken → debug cooperatively
|
||||
4. When working → commit
|
||||
|
||||
### **🔍 Cooperative Debugging Method**
|
||||
|
||||
**When user reports: "ça marche pas" or "y'a pas de lettres":**
|
||||
|
||||
1. **Get specific symptoms** - Don't assume, ask exactly what they see
|
||||
2. **Add targeted debug logs** - Console.log the exact variables in question
|
||||
3. **Test together** - Have user run and report console output
|
||||
4. **Analyze together** - Look at debug output to find root cause
|
||||
5. **Fix precisely** - Target the exact issue, don't rewrite everything
|
||||
|
||||
**Real Example - Letter Discovery Issue:**
|
||||
```javascript
|
||||
// ❌ ASSUMPTION: "Letters not working, must rewrite everything"
|
||||
|
||||
// ✅ ACTUAL DEBUG:
|
||||
console.log('🔍 DEBUG this.content.letters:', this.content.letters); // undefined
|
||||
console.log('🔍 DEBUG this.content.rawContent?.letters:', this.content.rawContent?.letters); // {U: Array(4), V: Array(4), T: Array(4)}
|
||||
|
||||
// ✅ PRECISE FIX: Check both locations
|
||||
const letters = this.content.letters || this.content.rawContent?.letters;
|
||||
```
|
||||
|
||||
### **🎯 Key Principles**
|
||||
|
||||
- **Communication > Code** - Clear problem description saves hours
|
||||
- **Debug logs > Assumptions** - Add console.log to see actual data
|
||||
- **Test early, test often** - Don't tunnel vision on untested code
|
||||
- **Pair debugging** - Two brains spot issues faster than one
|
||||
- **Patience > Speed** - Taking time to understand beats rushing broken fixes
|
||||
|
||||
**"C'est mieux quand on prend notre temps en coop plutot que de tunnel vision !"** 🎯
|
||||
441
Legacy/index.html
Normal file
441
Legacy/index.html
Normal file
@ -0,0 +1,441 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Interactive English Class</title>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<link rel="stylesheet" href="css/navigation.css">
|
||||
<link rel="stylesheet" href="css/games.css">
|
||||
<link rel="stylesheet" href="css/settings.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top Bar with Network Status -->
|
||||
<div class="top-bar">
|
||||
<div class="top-bar-left">
|
||||
<div class="top-bar-title">🎓 Interactive English Class</div>
|
||||
<nav class="breadcrumb" id="breadcrumb">
|
||||
<button class="breadcrumb-item active" data-page="home">🏠 Home</button>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="top-bar-right">
|
||||
<div class="network-status" id="network-status">
|
||||
<div class="network-indicator connecting" id="network-indicator"></div>
|
||||
<span class="network-status-text" id="network-status-text">Connecting...</span>
|
||||
</div>
|
||||
<button class="logger-toggle" onclick="openLogsInterface()" title="Open logs interface">📋</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Container -->
|
||||
<main class="container" id="main-container">
|
||||
|
||||
<!-- Home Page -->
|
||||
<div class="page active" id="home-page">
|
||||
|
||||
<div class="hero">
|
||||
<h1>🎓 Interactive English Class</h1>
|
||||
<p>Learn English while having fun!</p>
|
||||
</div>
|
||||
|
||||
<div class="main-options">
|
||||
<button class="option-card primary" onclick="navigateTo('levels')">
|
||||
📚 <span>Create a custom lesson</span>
|
||||
</button>
|
||||
<button class="option-card secondary" onclick="showComingSoon()">
|
||||
📊 <span>Statistics</span>
|
||||
<small>Coming soon</small>
|
||||
</button>
|
||||
<button class="option-card secondary" onclick="navigateTo('settings')">
|
||||
⚙️ <span>Settings</span>
|
||||
<small>Audio & Debug Tools</small>
|
||||
</button>
|
||||
<button class="option-card primary" onclick="showContentCreator()">
|
||||
🏭 <span>Content Creator</span>
|
||||
<small>Create your own exercises</small>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Level/Content Selection -->
|
||||
<div class="page" id="levels-page">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="AppNavigation.goBack()">← Back</button>
|
||||
<h2>📚 Choose your level</h2>
|
||||
<p>Select the content you want to learn</p>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid" id="levels-grid">
|
||||
<!-- Cards will be generated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Type Selection -->
|
||||
<div class="page" id="games-page">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="AppNavigation.goBack()">← Back</button>
|
||||
<h2>🎮 Choose your game</h2>
|
||||
<p id="game-description">Select the type of activity for this content</p>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid" id="games-grid">
|
||||
<!-- Cards will be generated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Page -->
|
||||
<div class="page" id="game-page">
|
||||
<div class="game-header">
|
||||
<button class="back-btn" onclick="goBack()">← Back</button>
|
||||
<h3 id="game-title">Game in progress...</h3>
|
||||
<div class="score-display">
|
||||
Score: <span id="current-score">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-container" id="game-container">
|
||||
<!-- The game will be loaded here dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Page -->
|
||||
<div class="page" id="settings-page">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="goBack()">← Back</button>
|
||||
<h2>⚙️ Settings & Debug Tools</h2>
|
||||
</div>
|
||||
|
||||
<div class="settings-container">
|
||||
<!-- Audio Settings Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🔊 Audio Settings</h3>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>TTS Voice Speed:</label>
|
||||
<input type="range" id="tts-rate" min="0.5" max="2" step="0.1" value="0.8">
|
||||
<span id="tts-rate-value">0.8</span>
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>TTS Volume:</label>
|
||||
<input type="range" id="tts-volume" min="0" max="1" step="0.1" value="1">
|
||||
<span id="tts-volume-value">1.0</span>
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>Preferred Voice:</label>
|
||||
<select id="tts-voice">
|
||||
<option value="">Auto (System Default)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TTS Debug Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🔧 TTS Debug Tools</h3>
|
||||
|
||||
<div class="debug-info" id="tts-status">
|
||||
<div class="info-item">
|
||||
<span class="label">Browser Support:</span>
|
||||
<span class="value" id="browser-support">Checking...</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Available Voices:</span>
|
||||
<span class="value" id="voice-count">Loading...</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">English Voices:</span>
|
||||
<span class="value" id="english-voice-count">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-controls">
|
||||
<button class="debug-btn" onclick="testBasicTTS()">🔊 Test Basic TTS</button>
|
||||
<button class="debug-btn" onclick="testWithCallbacks()">📞 Test with Callbacks</button>
|
||||
<button class="debug-btn" onclick="testGameWords()">🎮 Test Game Words</button>
|
||||
<button class="debug-btn" onclick="refreshVoices()">🔄 Refresh Voices</button>
|
||||
</div>
|
||||
|
||||
<div class="debug-output" id="debug-output">
|
||||
<h4>Debug Output:</h4>
|
||||
<div class="debug-log" id="debug-log"></div>
|
||||
<button class="clear-btn" onclick="clearDebugLog()">Clear Log</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Voice List Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🎤 Available Voices</h3>
|
||||
<div class="voice-list" id="voice-list">
|
||||
Loading voices...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Browser Info Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🌐 Browser Information</h3>
|
||||
<div class="browser-info">
|
||||
<div class="info-item">
|
||||
<span class="label">User Agent:</span>
|
||||
<span class="value small" id="user-agent"></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Platform:</span>
|
||||
<span class="value" id="platform"></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Language:</span>
|
||||
<span class="value" id="browser-language"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Modal Coming Soon -->
|
||||
<div class="modal" id="coming-soon-modal">
|
||||
<div class="modal-content">
|
||||
<h3>🚧 Coming Soon!</h3>
|
||||
<p>This feature will be available in an upcoming version.</p>
|
||||
<button onclick="closeModal()">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Indicator -->
|
||||
<div class="loading" id="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="js/core/websocket-logger.js"></script>
|
||||
<script src="js/core/env-config.js"></script>
|
||||
<script src="js/core/utils.js"></script>
|
||||
<script src="js/core/content-engine.js"></script>
|
||||
<script src="js/core/content-factory.js"></script>
|
||||
<script src="js/core/content-parsers.js"></script>
|
||||
<script src="js/core/content-generators.js"></script>
|
||||
<script src="js/core/content-scanner.js"></script>
|
||||
<script src="js/core/json-content-loader.js"></script>
|
||||
<script src="js/core/content-game-compatibility.js"></script>
|
||||
<script src="js/tools/content-creator.js"></script>
|
||||
<script src="js/core/settings-manager.js"></script>
|
||||
<script src="js/core/navigation.js"></script>
|
||||
<script src="js/core/game-loader.js"></script>
|
||||
<script>
|
||||
// Fonction de debug pour le logger
|
||||
function toggleLoggerDebug() {
|
||||
console.log('🔧 Bouton toggle cliqué!');
|
||||
|
||||
if (typeof window.logger === 'undefined') {
|
||||
console.error('❌ window.logger n\'existe pas!');
|
||||
alert('Erreur: Logger non initialisé!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Logger existe, toggle...');
|
||||
try {
|
||||
window.logger.toggle();
|
||||
console.log('✅ Toggle réussi');
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur toggle:', error);
|
||||
alert('Erreur toggle: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize app when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('🔧 DOM loaded, initializing...');
|
||||
logSh('🎯 DOM loaded, initializing application...', 'INFO');
|
||||
|
||||
// Vérifier que le logger existe
|
||||
if (typeof window.logger === 'undefined') {
|
||||
console.error('❌ Logger not found on load!');
|
||||
} else {
|
||||
console.log('✅ Logger found:', window.logger);
|
||||
}
|
||||
|
||||
// Test du logger
|
||||
if (typeof logSh !== 'undefined') {
|
||||
logSh('🚀 Application démarrée', 'INFO');
|
||||
logSh('Logger système intégré avec succès', 'DEBUG');
|
||||
} else {
|
||||
console.log('🚀 Application démarrée (pas de logger)');
|
||||
}
|
||||
|
||||
// Initialize connection status listener
|
||||
initConnectionStatus();
|
||||
|
||||
// Initialize navigation system
|
||||
AppNavigation.init();
|
||||
|
||||
// Test initial network connection
|
||||
setTimeout(testNetworkConnection, 1000);
|
||||
});
|
||||
|
||||
// Network Status Manager
|
||||
function initConnectionStatus() {
|
||||
// Listen for content connection status events
|
||||
window.addEventListener('contentConnectionStatus', function(event) {
|
||||
updateNetworkStatus(event.detail);
|
||||
});
|
||||
|
||||
// Test connection périodiquement
|
||||
setInterval(testNetworkConnection, 30000); // Test toutes les 30 secondes
|
||||
}
|
||||
|
||||
function updateNetworkStatus(details) {
|
||||
const indicator = document.getElementById('network-indicator');
|
||||
const text = document.getElementById('network-status-text');
|
||||
|
||||
// Remove all status classes
|
||||
indicator.classList.remove('connecting', 'online', 'offline');
|
||||
|
||||
// Update based on status
|
||||
switch(details.status) {
|
||||
case 'loading':
|
||||
indicator.classList.add('connecting');
|
||||
text.textContent = 'Connexion...';
|
||||
break;
|
||||
|
||||
case 'online':
|
||||
indicator.classList.add('online');
|
||||
text.textContent = 'Online';
|
||||
break;
|
||||
|
||||
case 'offline':
|
||||
indicator.classList.add('offline');
|
||||
text.textContent = 'Local';
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
indicator.classList.add('offline');
|
||||
text.textContent = 'Hors ligne';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function testNetworkConnection() {
|
||||
const indicator = document.getElementById('network-indicator');
|
||||
const text = document.getElementById('network-status-text');
|
||||
|
||||
// Désactiver les tests réseau en mode file://
|
||||
if (window.location.protocol === 'file:') {
|
||||
indicator.classList.remove('connecting', 'online');
|
||||
indicator.classList.add('offline');
|
||||
text.textContent = 'Local Mode';
|
||||
logSh('📁 Mode file:// - Test réseau ignoré', 'INFO');
|
||||
return;
|
||||
}
|
||||
|
||||
logSh('🔍 Test de connexion réseau démarré...', 'INFO');
|
||||
|
||||
try {
|
||||
// Test avec l'endpoint DigitalOcean avec timeout approprié
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000); // Plus de temps
|
||||
|
||||
// Utiliser le proxy local au lieu de DigitalOcean directement
|
||||
const testUrl = 'http://localhost:8083/do-proxy/english-class-demo.json';
|
||||
logSh(`🌐 Test URL (proxy): ${testUrl}`, 'INFO');
|
||||
logSh(`🕐 Timeout configuré: 5000ms`, 'DEBUG');
|
||||
logSh(`🔧 Mode: GET sans headers spéciaux`, 'DEBUG');
|
||||
|
||||
logSh('📤 Envoi de la requête fetch...', 'INFO');
|
||||
const fetchStart = Date.now();
|
||||
|
||||
const response = await fetch(testUrl, {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
mode: 'cors',
|
||||
cache: 'no-cache'
|
||||
});
|
||||
|
||||
const fetchDuration = Date.now() - fetchStart;
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
logSh(`⏱️ Durée de la requête: ${fetchDuration}ms`, 'INFO');
|
||||
logSh(`📡 Réponse reçue: ${response.status} ${response.statusText}`, 'INFO');
|
||||
logSh(`📋 Headers response: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`, 'DEBUG');
|
||||
|
||||
if (response.ok) {
|
||||
indicator.classList.remove('connecting', 'offline');
|
||||
indicator.classList.add('online');
|
||||
text.textContent = 'Online';
|
||||
logSh('✅ Connection successful!', 'INFO');
|
||||
} else {
|
||||
// Pour les buckets privés, on considère 403 comme "connexion OK mais accès privé"
|
||||
if (response.status === 403) {
|
||||
indicator.classList.remove('connecting', 'offline');
|
||||
indicator.classList.add('online');
|
||||
text.textContent = 'Privé';
|
||||
logSh('🔒 Connexion OK mais accès privé (403)', 'WARN');
|
||||
} else {
|
||||
logSh(`❌ Erreur HTTP: ${response.status}`, 'ERROR');
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logSh(`💥 ERREUR DÉTAILLÉE: Type=${error.constructor.name}, Name=${error.name}, Message=${error.message}`, 'ERROR');
|
||||
logSh(`🔍 Stack trace: ${error.stack}`, 'DEBUG');
|
||||
logSh(`🌐 URL qui a échoué: http://localhost:8083/do-proxy/english-class-demo.json`, 'ERROR');
|
||||
logSh(`🔧 Type d'erreur: ${typeof error}`, 'DEBUG');
|
||||
|
||||
// Vérifier si le serveur proxy est accessible
|
||||
logSh('🔍 Test de base du serveur proxy...', 'INFO');
|
||||
try {
|
||||
const basicTest = await fetch('http://localhost:8083/', { method: 'GET' });
|
||||
logSh(`📡 Test serveur proxy: ${basicTest.status}`, 'INFO');
|
||||
} catch (proxyError) {
|
||||
logSh(`❌ Serveur proxy inaccessible: ${proxyError.message}`, 'ERROR');
|
||||
}
|
||||
|
||||
indicator.classList.remove('connecting', 'online');
|
||||
indicator.classList.add('offline');
|
||||
if (error.name === 'AbortError') {
|
||||
text.textContent = 'Timeout';
|
||||
logSh('⏰ Timeout de connexion après 5000ms', 'WARN');
|
||||
} else if (error.message.includes('Failed to fetch')) {
|
||||
text.textContent = 'Serveur inaccessible';
|
||||
logSh(`🚫 Le serveur proxy sur port 8083 n'est pas accessible`, 'ERROR');
|
||||
} else {
|
||||
text.textContent = 'Hors ligne';
|
||||
logSh(`🚫 Hors ligne: ${error.message}`, 'ERROR');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coming soon functionality
|
||||
function showComingSoon() {
|
||||
logSh('🚧 Ouverture modal "Bientôt disponible"', 'INFO');
|
||||
document.getElementById('coming-soon-modal').classList.add('show');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
logSh('❌ Fermeture modal', 'DEBUG');
|
||||
document.getElementById('coming-soon-modal').classList.remove('show');
|
||||
}
|
||||
|
||||
function showContentCreator() {
|
||||
logSh('🏭 Ouverture du créateur de contenu', 'INFO');
|
||||
// Masquer la page d'accueil
|
||||
document.getElementById('home-page').classList.remove('active');
|
||||
|
||||
// Créer et afficher le créateur de contenu
|
||||
const creator = new ContentCreator();
|
||||
creator.init();
|
||||
}
|
||||
|
||||
// Keyboard navigation
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
logSh('⌨️ Touche Escape pressée', 'DEBUG');
|
||||
closeModal();
|
||||
AppNavigation.goBack();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
204
README.md
Normal file
204
README.md
Normal file
@ -0,0 +1,204 @@
|
||||
# Class Generator 2.0
|
||||
|
||||
**Educational Games Platform with Ultra-Modular Architecture**
|
||||
|
||||
A complete rewrite of the Class Generator system using strict modular design patterns, vanilla JavaScript, and rigorous separation of concerns.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Method 1: Batch File (Windows)
|
||||
```bash
|
||||
# Double-click or run:
|
||||
start.bat
|
||||
```
|
||||
|
||||
### Method 2: Command Line
|
||||
```bash
|
||||
# Install Node.js (if not already installed)
|
||||
# Then run:
|
||||
node server.js
|
||||
|
||||
# Or using npm:
|
||||
npm start
|
||||
```
|
||||
|
||||
### Method 3: NPM Scripts
|
||||
```bash
|
||||
npm run dev # Start development server
|
||||
npm run serve # Same as start
|
||||
```
|
||||
|
||||
**Server will start on:** `http://localhost:3000`
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
### Core System (Ultra-Rigid)
|
||||
|
||||
- **Module.js** - Abstract base class with sealed instances and WeakMap privates
|
||||
- **EventBus.js** - Strict event system with validation and type safety
|
||||
- **ModuleLoader.js** - Dependency injection with proper initialization order
|
||||
- **Router.js** - Navigation with guards, middleware, and state management
|
||||
- **Application.js** - Bootstrap system with auto-initialization
|
||||
|
||||
### Key Architecture Principles
|
||||
|
||||
✅ **Inviolable Responsibility** - Each module has exactly one purpose
|
||||
✅ **Zero Direct Dependencies** - All communication via EventBus
|
||||
✅ **Sealed Instances** - Modules cannot be modified after creation
|
||||
✅ **Private State** - Internal data completely inaccessible
|
||||
✅ **Contract Enforcement** - Abstract methods must be implemented
|
||||
✅ **Dependency Injection** - No global access, everything injected
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
├── src/
|
||||
│ ├── core/ # Core system modules (sealed)
|
||||
│ │ ├── Module.js
|
||||
│ │ ├── EventBus.js
|
||||
│ │ ├── ModuleLoader.js
|
||||
│ │ ├── Router.js
|
||||
│ │ └── index.js
|
||||
│ ├── components/ # Reusable UI components
|
||||
│ ├── games/ # Game modules
|
||||
│ ├── content/ # Content modules
|
||||
│ ├── styles/ # Modular CSS
|
||||
│ └── Application.js # Main application bootstrap
|
||||
├── Legacy/ # Old system (archived)
|
||||
├── index.html # Entry point
|
||||
├── server.js # Development server
|
||||
├── start.bat # Windows quick start
|
||||
└── package.json # Node.js configuration
|
||||
```
|
||||
|
||||
## 🔥 Features
|
||||
|
||||
### Development Server
|
||||
- **ES6 Modules** - Full import/export support
|
||||
- **CORS Enabled** - For online communication
|
||||
- **Hot Reload Ready** - Manual refresh (planned: auto-refresh)
|
||||
- **Proper MIME Types** - All file types supported
|
||||
- **Security Headers** - Basic security implemented
|
||||
|
||||
### Application System
|
||||
- **Auto-Start** - No commands needed, just open in browser
|
||||
- **Loading Screen** - Elegant initialization feedback
|
||||
- **Debug Panel** - F12 to toggle, shows system status
|
||||
- **Error Handling** - Graceful error recovery
|
||||
- **Network Status** - Real-time connectivity indicator
|
||||
|
||||
### Module System
|
||||
- **Strict Validation** - Errors if contracts violated
|
||||
- **Lifecycle Management** - Proper init/destroy patterns
|
||||
- **Event-Driven** - Loose coupling via events
|
||||
- **Dependency Resolution** - Automatic dependency loading
|
||||
|
||||
## 🎮 Creating Game Modules
|
||||
|
||||
```javascript
|
||||
import Module from '../core/Module.js';
|
||||
|
||||
class MyGame extends Module {
|
||||
constructor(name, dependencies, config) {
|
||||
super(name, ['eventBus', 'ui']);
|
||||
|
||||
this._eventBus = dependencies.eventBus;
|
||||
this._ui = dependencies.ui;
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
async init() {
|
||||
// Initialize game
|
||||
this._eventBus.on('game:start', this._handleStart.bind(this), this.name);
|
||||
this._setInitialized();
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
// Cleanup
|
||||
this._setDestroyed();
|
||||
}
|
||||
|
||||
_handleStart(event) {
|
||||
// Game logic here
|
||||
}
|
||||
}
|
||||
|
||||
export default MyGame;
|
||||
```
|
||||
|
||||
## 🧩 Adding to Application
|
||||
|
||||
```javascript
|
||||
// In Application.js modules config:
|
||||
{
|
||||
name: 'myGame',
|
||||
path: './games/MyGame.js',
|
||||
dependencies: ['eventBus', 'ui'],
|
||||
config: { difficulty: 'easy' }
|
||||
}
|
||||
```
|
||||
|
||||
## 🐛 Debugging
|
||||
|
||||
### Debug Panel (F12)
|
||||
- System status and uptime
|
||||
- Loaded modules list
|
||||
- Event history
|
||||
- Performance metrics
|
||||
|
||||
### Console Access
|
||||
```javascript
|
||||
// Global app instance available in console:
|
||||
window.app.getStatus() // Application status
|
||||
window.app.getCore().eventBus // Access EventBus
|
||||
window.app.getCore().router // Access Router
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Module loading fails:**
|
||||
- Check file paths in Application.js
|
||||
- Verify module extends Module base class
|
||||
- Ensure all dependencies are listed
|
||||
|
||||
**Events not working:**
|
||||
- Verify module is registered with EventBus
|
||||
- Check event type strings match exactly
|
||||
- Ensure module is initialized before emitting
|
||||
|
||||
## 🔒 Security & Rigidity
|
||||
|
||||
The architecture enforces several levels of protection:
|
||||
|
||||
1. **Sealed Classes** - `Object.seal()` prevents property addition/deletion
|
||||
2. **Frozen Prototypes** - `Object.freeze()` prevents method modification
|
||||
3. **WeakMap Privates** - Internal state completely hidden
|
||||
4. **Abstract Enforcement** - Missing methods throw errors
|
||||
5. **Validation Layers** - Input validation at every boundary
|
||||
|
||||
**Violation attempts will throw explicit errors with helpful messages.**
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
1. **Create Game Modules** - Implement actual games using new architecture
|
||||
2. **Add Content System** - Port content loading from Legacy system
|
||||
3. **UI Components** - Build reusable component library
|
||||
4. **Performance** - Add lazy loading and caching
|
||||
5. **Testing** - Add automated test suite
|
||||
|
||||
## 📝 Migration from Legacy
|
||||
|
||||
The Legacy/ folder contains the complete old system. Key differences:
|
||||
|
||||
- **Old:** Global variables and direct coupling
|
||||
- **New:** Strict modules with dependency injection
|
||||
|
||||
- **Old:** CSS modifications in global files
|
||||
- **New:** Component-scoped CSS injection
|
||||
|
||||
- **Old:** Manual module registration
|
||||
- **New:** Automatic loading with dependency resolution
|
||||
|
||||
---
|
||||
|
||||
**Built with strict architectural principles for maintainable, scalable educational software.**
|
||||
568
index.html
568
index.html
@ -3,439 +3,193 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Interactive English Class</title>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<link rel="stylesheet" href="css/navigation.css">
|
||||
<link rel="stylesheet" href="css/games.css">
|
||||
<link rel="stylesheet" href="css/settings.css">
|
||||
<title>Class Generator - Educational Games Platform</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="src/styles/base.css">
|
||||
<link rel="stylesheet" href="src/styles/components.css">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top Bar with Network Status -->
|
||||
<div class="top-bar">
|
||||
<div class="top-bar-left">
|
||||
<div class="top-bar-title">🎓 Interactive English Class</div>
|
||||
<nav class="breadcrumb" id="breadcrumb">
|
||||
<button class="breadcrumb-item active" data-page="home">🏠 Home</button>
|
||||
</nav>
|
||||
<!-- Application Root -->
|
||||
<div id="app">
|
||||
<!-- Loading screen shown until app initializes -->
|
||||
<div id="loading-screen" class="loading-screen">
|
||||
<div class="loading-spinner"></div>
|
||||
<h2>Loading Class Generator...</h2>
|
||||
<p>Initializing educational games platform</p>
|
||||
</div>
|
||||
<div class="top-bar-right">
|
||||
<div class="network-status" id="network-status">
|
||||
<div class="network-indicator connecting" id="network-indicator"></div>
|
||||
<span class="network-status-text" id="network-status-text">Connecting...</span>
|
||||
|
||||
<!-- Main application container -->
|
||||
<div id="app-container" class="app-container" style="display: none;">
|
||||
<!-- Top navigation bar -->
|
||||
<header id="app-header" class="app-header">
|
||||
<div class="header-content">
|
||||
<h1 class="app-title">Class Generator</h1>
|
||||
<div id="connection-status" class="connection-status">
|
||||
<span class="status-indicator"></span>
|
||||
<span class="status-text">Online</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main content area -->
|
||||
<main id="app-main" class="app-main">
|
||||
<!-- Content will be rendered here by modules -->
|
||||
</main>
|
||||
|
||||
<!-- Debug panel (only shown in debug mode) -->
|
||||
<div id="debug-panel" class="debug-panel" style="display: none;">
|
||||
<div class="debug-header">
|
||||
<h3>Debug Panel</h3>
|
||||
<button id="debug-toggle" class="debug-toggle">×</button>
|
||||
</div>
|
||||
<div class="debug-content">
|
||||
<div id="debug-status"></div>
|
||||
<div id="debug-events"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="logger-toggle" onclick="openLogsInterface()" title="Open logs interface">📋</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Container -->
|
||||
<main class="container" id="main-container">
|
||||
<!-- Application Bootstrap -->
|
||||
<script type="module">
|
||||
// Import and start the application
|
||||
import app from './src/Application.js';
|
||||
|
||||
<!-- Home Page -->
|
||||
<div class="page active" id="home-page">
|
||||
// Listen for application events
|
||||
const { eventBus } = app.getCore();
|
||||
|
||||
<div class="hero">
|
||||
<h1>🎓 Interactive English Class</h1>
|
||||
<p>Learn English while having fun!</p>
|
||||
</div>
|
||||
// Handle app ready event
|
||||
eventBus.on('app:ready', (event) => {
|
||||
console.log('🎉 Application is ready!');
|
||||
|
||||
<div class="main-options">
|
||||
<button class="option-card primary" onclick="navigateTo('levels')">
|
||||
📚 <span>Create a custom lesson</span>
|
||||
</button>
|
||||
<button class="option-card secondary" onclick="showComingSoon()">
|
||||
📊 <span>Statistics</span>
|
||||
<small>Coming soon</small>
|
||||
</button>
|
||||
<button class="option-card secondary" onclick="navigateTo('settings')">
|
||||
⚙️ <span>Settings</span>
|
||||
<small>Audio & Debug Tools</small>
|
||||
</button>
|
||||
<button class="option-card primary" onclick="showContentCreator()">
|
||||
🏭 <span>Content Creator</span>
|
||||
<small>Create your own exercises</small>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
// Hide loading screen
|
||||
const loadingScreen = document.getElementById('loading-screen');
|
||||
const appContainer = document.getElementById('app-container');
|
||||
|
||||
<!-- Level/Content Selection -->
|
||||
<div class="page" id="levels-page">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="AppNavigation.goBack()">← Back</button>
|
||||
<h2>📚 Choose your level</h2>
|
||||
<p>Select the content you want to learn</p>
|
||||
</div>
|
||||
if (loadingScreen) loadingScreen.style.display = 'none';
|
||||
if (appContainer) appContainer.style.display = 'block';
|
||||
|
||||
<div class="cards-grid" id="levels-grid">
|
||||
<!-- Cards will be generated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Type Selection -->
|
||||
<div class="page" id="games-page">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="AppNavigation.goBack()">← Back</button>
|
||||
<h2>🎮 Choose your game</h2>
|
||||
<p id="game-description">Select the type of activity for this content</p>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid" id="games-grid">
|
||||
<!-- Cards will be generated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Page -->
|
||||
<div class="page" id="game-page">
|
||||
<div class="game-header">
|
||||
<button class="back-btn" onclick="goBack()">← Back</button>
|
||||
<h3 id="game-title">Game in progress...</h3>
|
||||
<div class="score-display">
|
||||
Score: <span id="current-score">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-container" id="game-container">
|
||||
<!-- The game will be loaded here dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Page -->
|
||||
<div class="page" id="settings-page">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="goBack()">← Back</button>
|
||||
<h2>⚙️ Settings & Debug Tools</h2>
|
||||
</div>
|
||||
|
||||
<div class="settings-container">
|
||||
<!-- Audio Settings Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🔊 Audio Settings</h3>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>TTS Voice Speed:</label>
|
||||
<input type="range" id="tts-rate" min="0.5" max="2" step="0.1" value="0.8">
|
||||
<span id="tts-rate-value">0.8</span>
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>TTS Volume:</label>
|
||||
<input type="range" id="tts-volume" min="0" max="1" step="0.1" value="1">
|
||||
<span id="tts-volume-value">1.0</span>
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>Preferred Voice:</label>
|
||||
<select id="tts-voice">
|
||||
<option value="">Auto (System Default)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TTS Debug Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🔧 TTS Debug Tools</h3>
|
||||
|
||||
<div class="debug-info" id="tts-status">
|
||||
<div class="info-item">
|
||||
<span class="label">Browser Support:</span>
|
||||
<span class="value" id="browser-support">Checking...</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Available Voices:</span>
|
||||
<span class="value" id="voice-count">Loading...</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">English Voices:</span>
|
||||
<span class="value" id="english-voice-count">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-controls">
|
||||
<button class="debug-btn" onclick="testBasicTTS()">🔊 Test Basic TTS</button>
|
||||
<button class="debug-btn" onclick="testWithCallbacks()">📞 Test with Callbacks</button>
|
||||
<button class="debug-btn" onclick="testGameWords()">🎮 Test Game Words</button>
|
||||
<button class="debug-btn" onclick="refreshVoices()">🔄 Refresh Voices</button>
|
||||
</div>
|
||||
|
||||
<div class="debug-output" id="debug-output">
|
||||
<h4>Debug Output:</h4>
|
||||
<div class="debug-log" id="debug-log"></div>
|
||||
<button class="clear-btn" onclick="clearDebugLog()">Clear Log</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Voice List Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🎤 Available Voices</h3>
|
||||
<div class="voice-list" id="voice-list">
|
||||
Loading voices...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Browser Info Section -->
|
||||
<div class="settings-section">
|
||||
<h3>🌐 Browser Information</h3>
|
||||
<div class="browser-info">
|
||||
<div class="info-item">
|
||||
<span class="label">User Agent:</span>
|
||||
<span class="value small" id="user-agent"></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Platform:</span>
|
||||
<span class="value" id="platform"></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Language:</span>
|
||||
<span class="value" id="browser-language"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Modal Coming Soon -->
|
||||
<div class="modal" id="coming-soon-modal">
|
||||
<div class="modal-content">
|
||||
<h3>🚧 Coming Soon!</h3>
|
||||
<p>This feature will be available in an upcoming version.</p>
|
||||
<button onclick="closeModal()">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Indicator -->
|
||||
<div class="loading" id="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="js/core/websocket-logger.js"></script>
|
||||
<script src="js/core/env-config.js"></script>
|
||||
<script src="js/core/utils.js"></script>
|
||||
<script src="js/core/content-engine.js"></script>
|
||||
<script src="js/core/content-factory.js"></script>
|
||||
<script src="js/core/content-parsers.js"></script>
|
||||
<script src="js/core/content-generators.js"></script>
|
||||
<script src="js/core/content-scanner.js"></script>
|
||||
<script src="js/core/json-content-loader.js"></script>
|
||||
<script src="js/core/content-game-compatibility.js"></script>
|
||||
<script src="js/tools/content-creator.js"></script>
|
||||
<script src="js/core/settings-manager.js"></script>
|
||||
<script src="js/core/navigation.js"></script>
|
||||
<script src="js/core/game-loader.js"></script>
|
||||
<script>
|
||||
// Fonction de debug pour le logger
|
||||
function toggleLoggerDebug() {
|
||||
console.log('🔧 Bouton toggle cliqué!');
|
||||
|
||||
if (typeof window.logger === 'undefined') {
|
||||
console.error('❌ window.logger n\'existe pas!');
|
||||
alert('Erreur: Logger non initialisé!');
|
||||
return;
|
||||
// Show debug panel if enabled
|
||||
const status = app.getStatus();
|
||||
if (status.config.enableDebug) {
|
||||
const debugPanel = document.getElementById('debug-panel');
|
||||
if (debugPanel) debugPanel.style.display = 'block';
|
||||
updateDebugInfo();
|
||||
}
|
||||
}, 'Bootstrap');
|
||||
|
||||
console.log('✅ Logger existe, toggle...');
|
||||
try {
|
||||
window.logger.toggle();
|
||||
console.log('✅ Toggle réussi');
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur toggle:', error);
|
||||
alert('Erreur toggle: ' + error.message);
|
||||
// Handle navigation events
|
||||
eventBus.on('navigation:home', () => {
|
||||
updateMainContent('<h2>Welcome to Class Generator</h2><p>Select a game to start learning!</p>');
|
||||
}, 'Bootstrap');
|
||||
|
||||
eventBus.on('navigation:games', () => {
|
||||
updateMainContent('<h2>Available Games</h2><p>Loading games...</p>');
|
||||
}, 'Bootstrap');
|
||||
|
||||
eventBus.on('navigation:play', () => {
|
||||
updateMainContent('<h2>Game Arena</h2><p>Loading game...</p>');
|
||||
}, 'Bootstrap');
|
||||
|
||||
// Handle errors
|
||||
eventBus.on('app:error', (event) => {
|
||||
console.error('Application error:', event.data);
|
||||
showError('An error occurred. Check console for details.');
|
||||
}, 'Bootstrap');
|
||||
|
||||
// Utility functions
|
||||
function updateMainContent(html) {
|
||||
const mainElement = document.getElementById('app-main');
|
||||
if (mainElement) {
|
||||
mainElement.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize app when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('🔧 DOM loaded, initializing...');
|
||||
logSh('🎯 DOM loaded, initializing application...', 'INFO');
|
||||
|
||||
// Vérifier que le logger existe
|
||||
if (typeof window.logger === 'undefined') {
|
||||
console.error('❌ Logger not found on load!');
|
||||
} else {
|
||||
console.log('✅ Logger found:', window.logger);
|
||||
}
|
||||
|
||||
// Test du logger
|
||||
if (typeof logSh !== 'undefined') {
|
||||
logSh('🚀 Application démarrée', 'INFO');
|
||||
logSh('Logger système intégré avec succès', 'DEBUG');
|
||||
} else {
|
||||
console.log('🚀 Application démarrée (pas de logger)');
|
||||
}
|
||||
|
||||
// Initialize connection status listener
|
||||
initConnectionStatus();
|
||||
|
||||
// Initialize navigation system
|
||||
AppNavigation.init();
|
||||
|
||||
// Test initial network connection
|
||||
setTimeout(testNetworkConnection, 1000);
|
||||
});
|
||||
|
||||
// Network Status Manager
|
||||
function initConnectionStatus() {
|
||||
// Listen for content connection status events
|
||||
window.addEventListener('contentConnectionStatus', function(event) {
|
||||
updateNetworkStatus(event.detail);
|
||||
});
|
||||
|
||||
// Test connection périodiquement
|
||||
setInterval(testNetworkConnection, 30000); // Test toutes les 30 secondes
|
||||
}
|
||||
|
||||
function updateNetworkStatus(details) {
|
||||
const indicator = document.getElementById('network-indicator');
|
||||
const text = document.getElementById('network-status-text');
|
||||
|
||||
// Remove all status classes
|
||||
indicator.classList.remove('connecting', 'online', 'offline');
|
||||
|
||||
// Update based on status
|
||||
switch(details.status) {
|
||||
case 'loading':
|
||||
indicator.classList.add('connecting');
|
||||
text.textContent = 'Connexion...';
|
||||
break;
|
||||
|
||||
case 'online':
|
||||
indicator.classList.add('online');
|
||||
text.textContent = 'Online';
|
||||
break;
|
||||
|
||||
case 'offline':
|
||||
indicator.classList.add('offline');
|
||||
text.textContent = 'Local';
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
indicator.classList.add('offline');
|
||||
text.textContent = 'Hors ligne';
|
||||
break;
|
||||
function showError(message) {
|
||||
const mainElement = document.getElementById('app-main');
|
||||
if (mainElement) {
|
||||
mainElement.innerHTML = `
|
||||
<div class="error-message">
|
||||
<h2>Error</h2>
|
||||
<p>${message}</p>
|
||||
<button onclick="window.location.reload()">Reload Page</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testNetworkConnection() {
|
||||
const indicator = document.getElementById('network-indicator');
|
||||
const text = document.getElementById('network-status-text');
|
||||
|
||||
// Désactiver les tests réseau en mode file://
|
||||
if (window.location.protocol === 'file:') {
|
||||
indicator.classList.remove('connecting', 'online');
|
||||
indicator.classList.add('offline');
|
||||
text.textContent = 'Local Mode';
|
||||
logSh('📁 Mode file:// - Test réseau ignoré', 'INFO');
|
||||
return;
|
||||
function updateDebugInfo() {
|
||||
const debugStatus = document.getElementById('debug-status');
|
||||
if (debugStatus) {
|
||||
const status = app.getStatus();
|
||||
debugStatus.innerHTML = `
|
||||
<h4>System Status</h4>
|
||||
<ul>
|
||||
<li>Running: ${status.isRunning}</li>
|
||||
<li>Uptime: ${Math.round(status.uptime / 1000)}s</li>
|
||||
<li>Loaded Modules: ${status.modules.loaded.length}</li>
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
logSh('🔍 Test de connexion réseau démarré...', 'INFO');
|
||||
// Debug panel toggle
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const debugToggle = document.getElementById('debug-toggle');
|
||||
const debugPanel = document.getElementById('debug-panel');
|
||||
|
||||
try {
|
||||
// Test avec l'endpoint DigitalOcean avec timeout approprié
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000); // Plus de temps
|
||||
|
||||
// Utiliser le proxy local au lieu de DigitalOcean directement
|
||||
const testUrl = 'http://localhost:8083/do-proxy/english-class-demo.json';
|
||||
logSh(`🌐 Test URL (proxy): ${testUrl}`, 'INFO');
|
||||
logSh(`🕐 Timeout configuré: 5000ms`, 'DEBUG');
|
||||
logSh(`🔧 Mode: GET sans headers spéciaux`, 'DEBUG');
|
||||
|
||||
logSh('📤 Envoi de la requête fetch...', 'INFO');
|
||||
const fetchStart = Date.now();
|
||||
|
||||
const response = await fetch(testUrl, {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
mode: 'cors',
|
||||
cache: 'no-cache'
|
||||
if (debugToggle && debugPanel) {
|
||||
debugToggle.addEventListener('click', () => {
|
||||
debugPanel.style.display = 'none';
|
||||
});
|
||||
|
||||
const fetchDuration = Date.now() - fetchStart;
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
logSh(`⏱️ Durée de la requête: ${fetchDuration}ms`, 'INFO');
|
||||
logSh(`📡 Réponse reçue: ${response.status} ${response.statusText}`, 'INFO');
|
||||
logSh(`📋 Headers response: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`, 'DEBUG');
|
||||
|
||||
if (response.ok) {
|
||||
indicator.classList.remove('connecting', 'offline');
|
||||
indicator.classList.add('online');
|
||||
text.textContent = 'Online';
|
||||
logSh('✅ Connection successful!', 'INFO');
|
||||
} else {
|
||||
// Pour les buckets privés, on considère 403 comme "connexion OK mais accès privé"
|
||||
if (response.status === 403) {
|
||||
indicator.classList.remove('connecting', 'offline');
|
||||
indicator.classList.add('online');
|
||||
text.textContent = 'Privé';
|
||||
logSh('🔒 Connexion OK mais accès privé (403)', 'WARN');
|
||||
} else {
|
||||
logSh(`❌ Erreur HTTP: ${response.status}`, 'ERROR');
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logSh(`💥 ERREUR DÉTAILLÉE: Type=${error.constructor.name}, Name=${error.name}, Message=${error.message}`, 'ERROR');
|
||||
logSh(`🔍 Stack trace: ${error.stack}`, 'DEBUG');
|
||||
logSh(`🌐 URL qui a échoué: http://localhost:8083/do-proxy/english-class-demo.json`, 'ERROR');
|
||||
logSh(`🔧 Type d'erreur: ${typeof error}`, 'DEBUG');
|
||||
|
||||
// Vérifier si le serveur proxy est accessible
|
||||
logSh('🔍 Test de base du serveur proxy...', 'INFO');
|
||||
try {
|
||||
const basicTest = await fetch('http://localhost:8083/', { method: 'GET' });
|
||||
logSh(`📡 Test serveur proxy: ${basicTest.status}`, 'INFO');
|
||||
} catch (proxyError) {
|
||||
logSh(`❌ Serveur proxy inaccessible: ${proxyError.message}`, 'ERROR');
|
||||
}
|
||||
|
||||
indicator.classList.remove('connecting', 'online');
|
||||
indicator.classList.add('offline');
|
||||
if (error.name === 'AbortError') {
|
||||
text.textContent = 'Timeout';
|
||||
logSh('⏰ Timeout de connexion après 5000ms', 'WARN');
|
||||
} else if (error.message.includes('Failed to fetch')) {
|
||||
text.textContent = 'Serveur inaccessible';
|
||||
logSh(`🚫 Le serveur proxy sur port 8083 n'est pas accessible`, 'ERROR');
|
||||
} else {
|
||||
text.textContent = 'Hors ligne';
|
||||
logSh(`🚫 Hors ligne: ${error.message}`, 'ERROR');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coming soon functionality
|
||||
function showComingSoon() {
|
||||
logSh('🚧 Ouverture modal "Bientôt disponible"', 'INFO');
|
||||
document.getElementById('coming-soon-modal').classList.add('show');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
logSh('❌ Fermeture modal', 'DEBUG');
|
||||
document.getElementById('coming-soon-modal').classList.remove('show');
|
||||
}
|
||||
|
||||
function showContentCreator() {
|
||||
logSh('🏭 Ouverture du créateur de contenu', 'INFO');
|
||||
// Masquer la page d'accueil
|
||||
document.getElementById('home-page').classList.remove('active');
|
||||
|
||||
// Créer et afficher le créateur de contenu
|
||||
const creator = new ContentCreator();
|
||||
creator.init();
|
||||
}
|
||||
|
||||
// Keyboard navigation
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
logSh('⌨️ Touche Escape pressée', 'DEBUG');
|
||||
closeModal();
|
||||
AppNavigation.goBack();
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', (event) => {
|
||||
// ESC to go back
|
||||
if (event.key === 'Escape') {
|
||||
const { router } = app.getCore();
|
||||
if (router && !router.isCurrentRoute('/')) {
|
||||
router.back();
|
||||
}
|
||||
}
|
||||
|
||||
// F12 to toggle debug (if enabled)
|
||||
if (event.key === 'F12' && app.getStatus().config.enableDebug) {
|
||||
event.preventDefault();
|
||||
const debugPanel = document.getElementById('debug-panel');
|
||||
if (debugPanel) {
|
||||
debugPanel.style.display = debugPanel.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Connection status monitoring
|
||||
function updateConnectionStatus() {
|
||||
const statusIndicator = document.querySelector('.status-indicator');
|
||||
const statusText = document.querySelector('.status-text');
|
||||
|
||||
if (navigator.onLine) {
|
||||
if (statusIndicator) statusIndicator.className = 'status-indicator online';
|
||||
if (statusText) statusText.textContent = 'Online';
|
||||
} else {
|
||||
if (statusIndicator) statusIndicator.className = 'status-indicator offline';
|
||||
if (statusText) statusText.textContent = 'Offline';
|
||||
}
|
||||
}
|
||||
|
||||
// Monitor connection changes
|
||||
window.addEventListener('online', updateConnectionStatus);
|
||||
window.addEventListener('offline', updateConnectionStatus);
|
||||
updateConnectionStatus(); // Initial check
|
||||
|
||||
// Export app globally for console access
|
||||
window.app = app;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
24
package.json
Normal file
24
package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "class-generator",
|
||||
"version": "2.0.0",
|
||||
"description": "Educational games platform with modular architecture",
|
||||
"type": "module",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "node server.js",
|
||||
"serve": "node server.js"
|
||||
},
|
||||
"keywords": [
|
||||
"education",
|
||||
"games",
|
||||
"learning",
|
||||
"vanilla-js",
|
||||
"modular"
|
||||
],
|
||||
"author": "Alexis Trouvé",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
197
server.js
Normal file
197
server.js
Normal file
@ -0,0 +1,197 @@
|
||||
/**
|
||||
* Development Server - Simple HTTP server for local development
|
||||
* Handles static files, CORS, and development features
|
||||
*/
|
||||
|
||||
import { createServer } from 'http';
|
||||
import { readFile, stat } from 'fs/promises';
|
||||
import { join, extname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const HOST = process.env.HOST || 'localhost';
|
||||
|
||||
// MIME types for different file extensions
|
||||
const MIME_TYPES = {
|
||||
'.html': 'text/html',
|
||||
'.css': 'text/css',
|
||||
'.js': 'application/javascript',
|
||||
'.json': 'application/json',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.ico': 'image/x-icon',
|
||||
'.woff': 'font/woff',
|
||||
'.woff2': 'font/woff2',
|
||||
'.ttf': 'font/ttf',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.wav': 'audio/wav',
|
||||
'.mp4': 'video/mp4'
|
||||
};
|
||||
|
||||
const server = createServer(async (req, res) => {
|
||||
try {
|
||||
// Parse URL and remove query parameters
|
||||
const urlPath = new URL(req.url, `http://${req.headers.host}`).pathname;
|
||||
|
||||
// Default to index.html for root requests
|
||||
const filePath = urlPath === '/' ? 'index.html' : urlPath.slice(1);
|
||||
const fullPath = join(__dirname, filePath);
|
||||
|
||||
console.log(`${new Date().toISOString()} - ${req.method} ${urlPath}`);
|
||||
|
||||
// Set CORS headers for all requests
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
|
||||
// Handle preflight requests
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if file exists
|
||||
try {
|
||||
const stats = await stat(fullPath);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
// Try to serve index.html from directory
|
||||
const indexPath = join(fullPath, 'index.html');
|
||||
try {
|
||||
await stat(indexPath);
|
||||
return serveFile(indexPath, res);
|
||||
} catch {
|
||||
return send404(res, `Directory listing not allowed for ${urlPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
return serveFile(fullPath, res);
|
||||
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
return send404(res, `File not found: ${urlPath}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Server error:', error);
|
||||
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
res.end('Internal Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
async function serveFile(filePath, res) {
|
||||
try {
|
||||
const ext = extname(filePath).toLowerCase();
|
||||
const mimeType = MIME_TYPES[ext] || 'application/octet-stream';
|
||||
|
||||
// Set content type
|
||||
res.setHeader('Content-Type', mimeType);
|
||||
|
||||
// Set cache headers for static assets
|
||||
if (['.css', '.js', '.png', '.jpg', '.gif', '.svg', '.ico', '.woff', '.woff2'].includes(ext)) {
|
||||
res.setHeader('Cache-Control', 'public, max-age=3600'); // 1 hour
|
||||
} else {
|
||||
res.setHeader('Cache-Control', 'no-cache'); // No cache for HTML and other files
|
||||
}
|
||||
|
||||
// Add security headers
|
||||
if (ext === '.js') {
|
||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||||
}
|
||||
|
||||
// Read and serve file
|
||||
const content = await readFile(filePath);
|
||||
|
||||
res.writeHead(200);
|
||||
res.end(content);
|
||||
|
||||
console.log(` ✅ Served ${filePath} (${content.length} bytes, ${mimeType})`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error serving file ${filePath}:`, error);
|
||||
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
res.end('Error reading file');
|
||||
}
|
||||
}
|
||||
|
||||
function send404(res, message = 'Not Found') {
|
||||
const html404 = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>404 - Not Found</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
h1 { font-size: 3rem; margin-bottom: 1rem; }
|
||||
p { font-size: 1.2rem; opacity: 0.8; }
|
||||
a { color: white; text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>404</h1>
|
||||
<p>${message}</p>
|
||||
<p><a href="/">← Back to Class Generator</a></p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
res.writeHead(404, { 'Content-Type': 'text/html' });
|
||||
res.end(html404);
|
||||
|
||||
console.log(` ❌ 404: ${message}`);
|
||||
}
|
||||
|
||||
// Start server
|
||||
server.listen(PORT, HOST, () => {
|
||||
console.log('\n🚀 Class Generator Development Server');
|
||||
console.log('=====================================');
|
||||
console.log(`📍 Local: http://${HOST}:${PORT}/`);
|
||||
console.log(`🌐 Network: http://localhost:${PORT}/`);
|
||||
console.log('📁 Serving files from:', __dirname);
|
||||
console.log('\n✨ Features:');
|
||||
console.log(' • ES6 modules support');
|
||||
console.log(' • CORS enabled');
|
||||
console.log(' • Static file serving');
|
||||
console.log(' • Development-friendly caching');
|
||||
console.log('\n🔥 Ready for development!');
|
||||
console.log('Press Ctrl+C to stop\n');
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n👋 Shutting down server...');
|
||||
server.close(() => {
|
||||
console.log('✅ Server stopped');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('\n👋 Received SIGTERM, shutting down...');
|
||||
server.close(() => {
|
||||
console.log('✅ Server stopped');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
260
src/Application.js
Normal file
260
src/Application.js
Normal file
@ -0,0 +1,260 @@
|
||||
/**
|
||||
* Application - Main application bootstrap and lifecycle manager
|
||||
* Auto-initializes the entire system without external commands
|
||||
*/
|
||||
|
||||
import { EventBus, ModuleLoader, Router } from './core/index.js';
|
||||
|
||||
class Application {
|
||||
constructor(config = {}) {
|
||||
// Application state
|
||||
this._isRunning = false;
|
||||
this._isInitialized = false;
|
||||
this._startTime = null;
|
||||
|
||||
// Core system instances
|
||||
this._eventBus = new EventBus();
|
||||
this._moduleLoader = new ModuleLoader(this._eventBus);
|
||||
this._router = null;
|
||||
|
||||
// Configuration
|
||||
this._config = {
|
||||
autoStart: config.autoStart !== false, // Default true
|
||||
defaultRoute: config.defaultRoute || '/',
|
||||
enableDebug: config.enableDebug || false,
|
||||
modules: config.modules || [],
|
||||
...config
|
||||
};
|
||||
|
||||
// Auto-start if enabled
|
||||
if (this._config.autoStart) {
|
||||
this._autoStart();
|
||||
}
|
||||
|
||||
// Seal to prevent modification
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and start the application
|
||||
*/
|
||||
async start() {
|
||||
if (this._isRunning) {
|
||||
console.warn('Application is already running');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this._startTime = Date.now();
|
||||
|
||||
console.log('🚀 Starting Class Generator Application...');
|
||||
|
||||
// Initialize core systems
|
||||
await this._initializeCore();
|
||||
|
||||
// Load and initialize modules
|
||||
await this._loadModules();
|
||||
|
||||
// Start routing
|
||||
await this._startRouting();
|
||||
|
||||
// Set up global error handling
|
||||
this._setupErrorHandling();
|
||||
|
||||
this._isRunning = true;
|
||||
this._isInitialized = true;
|
||||
|
||||
const startupTime = Date.now() - this._startTime;
|
||||
console.log(`✅ Application started successfully in ${startupTime}ms`);
|
||||
|
||||
// Emit application ready event
|
||||
this._eventBus.emit('app:ready', {
|
||||
startupTime,
|
||||
modules: this._moduleLoader.getStatus()
|
||||
}, 'Application');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to start application:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the application and clean up
|
||||
*/
|
||||
async stop() {
|
||||
if (!this._isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🛑 Stopping application...');
|
||||
|
||||
// Emit application stopping event
|
||||
this._eventBus.emit('app:stopping', {}, 'Application');
|
||||
|
||||
// Destroy all modules
|
||||
const status = this._moduleLoader.getStatus();
|
||||
for (const moduleName of status.loaded) {
|
||||
await this._moduleLoader.destroy(moduleName);
|
||||
}
|
||||
|
||||
// Destroy router
|
||||
if (this._router) {
|
||||
await this._moduleLoader.destroy('router');
|
||||
}
|
||||
|
||||
this._isRunning = false;
|
||||
this._isInitialized = false;
|
||||
|
||||
console.log('✅ Application stopped successfully');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error stopping application:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application status
|
||||
*/
|
||||
getStatus() {
|
||||
return {
|
||||
isRunning: this._isRunning,
|
||||
isInitialized: this._isInitialized,
|
||||
startTime: this._startTime,
|
||||
uptime: this._startTime ? Date.now() - this._startTime : 0,
|
||||
modules: this._moduleLoader.getStatus(),
|
||||
config: { ...this._config }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core system instances (for advanced usage)
|
||||
*/
|
||||
getCore() {
|
||||
return {
|
||||
eventBus: this._eventBus,
|
||||
moduleLoader: this._moduleLoader,
|
||||
router: this._router
|
||||
};
|
||||
}
|
||||
|
||||
// Private methods
|
||||
async _autoStart() {
|
||||
// Wait for DOM to be ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => this.start());
|
||||
} else {
|
||||
// DOM is already loaded
|
||||
setTimeout(() => this.start(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
async _initializeCore() {
|
||||
// Register router as a module
|
||||
this._moduleLoader.register('router', Router, ['eventBus']);
|
||||
|
||||
// Create and initialize router
|
||||
this._router = await this._moduleLoader.loadAndInitialize('router', {
|
||||
defaultRoute: this._config.defaultRoute,
|
||||
maxHistorySize: 100
|
||||
});
|
||||
|
||||
if (this._config.enableDebug) {
|
||||
console.log('🔧 Core systems initialized');
|
||||
}
|
||||
}
|
||||
|
||||
async _loadModules() {
|
||||
for (const moduleConfig of this._config.modules) {
|
||||
try {
|
||||
const { name, path, dependencies = [], config = {} } = moduleConfig;
|
||||
|
||||
// Dynamically import module
|
||||
const moduleModule = await import(path);
|
||||
const ModuleClass = moduleModule.default;
|
||||
|
||||
// Register and initialize
|
||||
this._moduleLoader.register(name, ModuleClass, dependencies);
|
||||
await this._moduleLoader.loadAndInitialize(name, config);
|
||||
|
||||
if (this._config.enableDebug) {
|
||||
console.log(`📦 Module ${name} loaded successfully`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to load module ${moduleConfig.name}:`, error);
|
||||
|
||||
// Emit module load error
|
||||
this._eventBus.emit('app:module-error', {
|
||||
module: moduleConfig.name,
|
||||
error: error.message
|
||||
}, 'Application');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _startRouting() {
|
||||
// Register default routes
|
||||
this._router.register('/', this._handleHomeRoute.bind(this));
|
||||
this._router.register('/games', this._handleGamesRoute.bind(this));
|
||||
this._router.register('/play', this._handlePlayRoute.bind(this));
|
||||
|
||||
if (this._config.enableDebug) {
|
||||
console.log('🛣️ Routing system started');
|
||||
}
|
||||
}
|
||||
|
||||
_setupErrorHandling() {
|
||||
// Global error handler
|
||||
window.addEventListener('error', (event) => {
|
||||
console.error('Global error:', event.error);
|
||||
this._eventBus.emit('app:error', {
|
||||
message: event.error.message,
|
||||
filename: event.filename,
|
||||
lineno: event.lineno,
|
||||
colno: event.colno
|
||||
}, 'Application');
|
||||
});
|
||||
|
||||
// Unhandled promise rejection handler
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
console.error('Unhandled promise rejection:', event.reason);
|
||||
this._eventBus.emit('app:promise-rejection', {
|
||||
reason: event.reason
|
||||
}, 'Application');
|
||||
});
|
||||
|
||||
if (this._config.enableDebug) {
|
||||
console.log('🛡️ Error handling setup complete');
|
||||
}
|
||||
}
|
||||
|
||||
// Default route handlers
|
||||
async _handleHomeRoute(path, state) {
|
||||
this._eventBus.emit('navigation:home', { path, state }, 'Application');
|
||||
}
|
||||
|
||||
async _handleGamesRoute(path, state) {
|
||||
this._eventBus.emit('navigation:games', { path, state }, 'Application');
|
||||
}
|
||||
|
||||
async _handlePlayRoute(path, state) {
|
||||
this._eventBus.emit('navigation:play', { path, state }, 'Application');
|
||||
}
|
||||
}
|
||||
|
||||
// Create global application instance
|
||||
const app = new Application({
|
||||
enableDebug: true,
|
||||
modules: [
|
||||
// Modules will be registered here
|
||||
// { name: 'ui', path: './components/UI.js', dependencies: ['eventBus'] },
|
||||
// { name: 'gameEngine', path: './games/GameEngine.js', dependencies: ['eventBus', 'ui'] }
|
||||
]
|
||||
});
|
||||
|
||||
// Export for manual control if needed
|
||||
export default app;
|
||||
|
||||
// Auto-start is handled by the constructor
|
||||
850
src/content/NCE1-Lesson63-64.js
Normal file
850
src/content/NCE1-Lesson63-64.js
Normal file
@ -0,0 +1,850 @@
|
||||
// === ENGLISH MEDICAL AND SAFETY LESSONS ===
|
||||
// Lessons 63-64: Doctor visit and prohibition commands with Chinese translation
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.NCE1Lesson6364 = {
|
||||
id: "nce1-lesson63-64",
|
||||
name: "NCE1-Lesson63-64",
|
||||
description: "English medical dialogue and prohibition commands with modal verbs",
|
||||
difficulty: "intermediate",
|
||||
language: "en-US",
|
||||
userLanguage: "zh-CN",
|
||||
totalWords: 120,
|
||||
|
||||
// === GRAMMAR LESSONS SYSTEM ===
|
||||
grammar: {
|
||||
"modal-must-mustnot": {
|
||||
title: "Modal Verbs: Must and Mustn't - 情态动词must和mustn't",
|
||||
explanation: "English uses 'must' for strong obligation and 'mustn't' for prohibition.",
|
||||
rules: [
|
||||
"must + verb - for strong necessity: You must stay in bed",
|
||||
"mustn't + verb - for prohibition: You mustn't get up yet",
|
||||
"Must + subject + verb? - for questions: Must he stay in bed?",
|
||||
"No contraction for positive must, but mustn't = must not"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "You must stay in bed.",
|
||||
chinese: "你必须卧床休息。",
|
||||
explanation: "Use 'must' for strong obligation or medical advice",
|
||||
pronunciation: "ju mʌst steɪ ɪn bed"
|
||||
},
|
||||
{
|
||||
english: "You mustn't get up yet.",
|
||||
chinese: "你还不能起床。",
|
||||
explanation: "Use 'mustn't' for prohibition or things not allowed",
|
||||
pronunciation: "ju mʌsnt get ʌp jet"
|
||||
},
|
||||
{
|
||||
english: "Must he stay in bed?",
|
||||
chinese: "他必须卧床吗?",
|
||||
explanation: "Use 'Must' at the start for yes/no questions",
|
||||
pronunciation: "mʌst hi steɪ ɪn bed"
|
||||
},
|
||||
{
|
||||
english: "He mustn't eat rich food.",
|
||||
chinese: "他不能吃油腻食物。",
|
||||
explanation: "Use 'mustn't' for medical restrictions",
|
||||
pronunciation: "hi mʌsnt it rɪtʃ fud"
|
||||
},
|
||||
{
|
||||
english: "You must keep the room warm.",
|
||||
chinese: "你必须保持房间温暖。",
|
||||
explanation: "Use 'must' for important instructions",
|
||||
pronunciation: "ju mʌst kip ðə rum wɔrm"
|
||||
},
|
||||
{
|
||||
english: "The boy mustn't go to school yet.",
|
||||
chinese: "这个男孩还不能去上学。",
|
||||
explanation: "Use 'mustn't' for temporary restrictions",
|
||||
pronunciation: "ðə bɔɪ mʌsnt gəʊ tu skul jet"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "You _____ stay in bed for two days.",
|
||||
options: ["must", "mustn't", "can", "can't"],
|
||||
correct: "must",
|
||||
explanation: "Use 'must' for medical necessity"
|
||||
},
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "He _____ eat rich food when he's sick.",
|
||||
options: ["must", "mustn't", "should", "can"],
|
||||
correct: "mustn't",
|
||||
explanation: "Use 'mustn't' for medical restrictions"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"imperatives-commands": {
|
||||
title: "Imperative Commands - 祈使句",
|
||||
explanation: "English uses imperatives to give commands, instructions, or make requests.",
|
||||
rules: [
|
||||
"Positive commands: Verb + object: Come upstairs",
|
||||
"Negative commands: Don't + verb: Don't take medicine",
|
||||
"No subject pronoun needed in commands",
|
||||
"Use for instructions, warnings, and advice"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "Come upstairs.",
|
||||
chinese: "上楼来。",
|
||||
explanation: "Positive command - just use the base verb",
|
||||
pronunciation: "kʌm ʌpstɛrz"
|
||||
},
|
||||
{
|
||||
english: "Don't take any aspirins.",
|
||||
chinese: "不要吃任何阿司匹林。",
|
||||
explanation: "Negative command - Don't + base verb",
|
||||
pronunciation: "dəʊnt teɪk eni æspɪrɪnz"
|
||||
},
|
||||
{
|
||||
english: "Don't play with matches.",
|
||||
chinese: "不要玩火柴。",
|
||||
explanation: "Safety warning using negative command",
|
||||
pronunciation: "dəʊnt pleɪ wɪð mætʃɪz"
|
||||
},
|
||||
{
|
||||
english: "Don't make a noise.",
|
||||
chinese: "不要制造噪音。",
|
||||
explanation: "Polite request using negative command",
|
||||
pronunciation: "dəʊnt meɪk ə nɔɪz"
|
||||
},
|
||||
{
|
||||
english: "Don't drive so quickly.",
|
||||
chinese: "不要开得这么快。",
|
||||
explanation: "Safety advice using negative command",
|
||||
pronunciation: "dəʊnt draɪv səʊ kwɪkli"
|
||||
},
|
||||
{
|
||||
english: "Don't break that vase.",
|
||||
chinese: "不要打破那个花瓶。",
|
||||
explanation: "Warning using negative command",
|
||||
pronunciation: "dəʊnt breɪk ðæt vɑːz"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "transformation",
|
||||
instruction: "Make this a negative command:",
|
||||
original: "Take this medicine.",
|
||||
correct: "Don't take this medicine.",
|
||||
explanation: "Add 'Don't' before the verb"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"present-simple-questions": {
|
||||
title: "Present Simple Questions - 一般现在时疑问句",
|
||||
explanation: "English forms questions differently for 'be' verbs and other verbs.",
|
||||
rules: [
|
||||
"With 'be': Be + subject: How's Jimmy? Where's Mr. Williams?",
|
||||
"With other verbs: Do/Does + subject + verb: Does he have a temperature?",
|
||||
"Question words come first: How, What, Where, When"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "How's Jimmy today?",
|
||||
chinese: "吉米今天怎么样?",
|
||||
explanation: "Question with 'be' verb - How + is contracted",
|
||||
pronunciation: "haʊz dʒɪmi tədeɪ"
|
||||
},
|
||||
{
|
||||
english: "Where's Mr. Williams?",
|
||||
chinese: "威廉姆斯先生在哪里?",
|
||||
explanation: "Question with 'be' verb - Where + is contracted",
|
||||
pronunciation: "wɛrz mɪstər wɪljəmz"
|
||||
},
|
||||
{
|
||||
english: "Does he have a temperature?",
|
||||
chinese: "他发烧吗?",
|
||||
explanation: "Question with regular verb - Does + subject + verb",
|
||||
pronunciation: "dʌz hi hæv ə tempərətʃər"
|
||||
},
|
||||
{
|
||||
english: "Can I see him please?",
|
||||
chinese: "我可以看看他吗?",
|
||||
explanation: "Question with modal verb - Modal + subject + verb",
|
||||
pronunciation: "kæn aɪ si hɪm pliz"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "question_formation",
|
||||
statement: "He has a cold.",
|
||||
correct: "Does he have a cold?",
|
||||
explanation: "Use 'Does' + subject + base verb for questions"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
vocabulary: {
|
||||
"doctor": {
|
||||
"user_language": "医生",
|
||||
"type": "noun",
|
||||
"pronunciation": "dɔktər"
|
||||
},
|
||||
"better": {
|
||||
"user_language": "更好的",
|
||||
"type": "adjective",
|
||||
"pronunciation": "betər"
|
||||
},
|
||||
"certainly": {
|
||||
"user_language": "当然",
|
||||
"type": "adverb",
|
||||
"pronunciation": "sɜrtənli"
|
||||
},
|
||||
"upstairs": {
|
||||
"user_language": "楼上",
|
||||
"type": "adverb",
|
||||
"pronunciation": "ʌpstɛrz"
|
||||
},
|
||||
"bed": {
|
||||
"user_language": "床",
|
||||
"type": "noun",
|
||||
"pronunciation": "bed"
|
||||
},
|
||||
"yet": {
|
||||
"user_language": "还,仍",
|
||||
"type": "adverb",
|
||||
"pronunciation": "jet"
|
||||
},
|
||||
"stay": {
|
||||
"user_language": "停留",
|
||||
"type": "verb",
|
||||
"pronunciation": "steɪ"
|
||||
},
|
||||
"school": {
|
||||
"user_language": "学校",
|
||||
"type": "noun",
|
||||
"pronunciation": "skul"
|
||||
},
|
||||
"rich": {
|
||||
"user_language": "油腻的",
|
||||
"type": "adjective",
|
||||
"pronunciation": "rɪtʃ"
|
||||
},
|
||||
"food": {
|
||||
"user_language": "食物",
|
||||
"type": "noun",
|
||||
"pronunciation": "fud"
|
||||
},
|
||||
"temperature": {
|
||||
"user_language": "体温,发烧",
|
||||
"type": "noun",
|
||||
"pronunciation": "tempərətʃər"
|
||||
},
|
||||
"remain": {
|
||||
"user_language": "保持,继续",
|
||||
"type": "verb",
|
||||
"pronunciation": "rɪmeɪn"
|
||||
},
|
||||
"warm": {
|
||||
"user_language": "温暖的",
|
||||
"type": "adjective",
|
||||
"pronunciation": "wɔrm"
|
||||
},
|
||||
"cold": {
|
||||
"user_language": "感冒",
|
||||
"type": "noun",
|
||||
"pronunciation": "kəʊld"
|
||||
},
|
||||
"aspirins": {
|
||||
"user_language": "阿司匹林",
|
||||
"type": "noun",
|
||||
"pronunciation": "æspɪrɪnz"
|
||||
},
|
||||
"medicine": {
|
||||
"user_language": "药",
|
||||
"type": "noun",
|
||||
"pronunciation": "medɪsən"
|
||||
},
|
||||
"play": {
|
||||
"user_language": "玩",
|
||||
"type": "verb",
|
||||
"pronunciation": "pleɪ"
|
||||
},
|
||||
"matches": {
|
||||
"user_language": "火柴",
|
||||
"type": "noun",
|
||||
"pronunciation": "mætʃɪz"
|
||||
},
|
||||
"talk": {
|
||||
"user_language": "谈话",
|
||||
"type": "verb",
|
||||
"pronunciation": "tɔk"
|
||||
},
|
||||
"library": {
|
||||
"user_language": "图书馆",
|
||||
"type": "noun",
|
||||
"pronunciation": "laɪbrəri"
|
||||
},
|
||||
"noise": {
|
||||
"user_language": "噪音",
|
||||
"type": "noun",
|
||||
"pronunciation": "nɔɪz"
|
||||
},
|
||||
"drive": {
|
||||
"user_language": "开车",
|
||||
"type": "verb",
|
||||
"pronunciation": "draɪv"
|
||||
},
|
||||
"quickly": {
|
||||
"user_language": "快地",
|
||||
"type": "adverb",
|
||||
"pronunciation": "kwɪkli"
|
||||
},
|
||||
"lean": {
|
||||
"user_language": "探身",
|
||||
"type": "verb",
|
||||
"pronunciation": "lin"
|
||||
},
|
||||
"window": {
|
||||
"user_language": "窗户",
|
||||
"type": "noun",
|
||||
"pronunciation": "wɪndəʊ"
|
||||
},
|
||||
"break": {
|
||||
"user_language": "打破",
|
||||
"type": "verb",
|
||||
"pronunciation": "breɪk"
|
||||
},
|
||||
"vase": {
|
||||
"user_language": "花瓶",
|
||||
"type": "noun",
|
||||
"pronunciation": "vɑz"
|
||||
}
|
||||
},
|
||||
|
||||
// === SENTENCES FOR GAMES (extracted from stories) ===
|
||||
sentences: [
|
||||
{
|
||||
english: "How's Jimmy today?",
|
||||
chinese: "吉米今天怎么样?",
|
||||
prononciation: "haʊz dʒɪmi tədeɪ"
|
||||
},
|
||||
{
|
||||
english: "Better. Thank you, doctor.",
|
||||
chinese: "好一些了。谢谢你,医生。",
|
||||
prononciation: "betər θæŋk ju dɔktər"
|
||||
},
|
||||
{
|
||||
english: "Can I see him please?",
|
||||
chinese: "我可以看看他吗?",
|
||||
prononciation: "kæn aɪ si hɪm pliz"
|
||||
},
|
||||
{
|
||||
english: "You must stay in bed.",
|
||||
chinese: "你必须卧床休息。",
|
||||
prononciation: "ju mʌst steɪ ɪn bed"
|
||||
},
|
||||
{
|
||||
english: "You mustn't get up yet.",
|
||||
chinese: "你还不能起床。",
|
||||
prononciation: "ju mʌsnt get ʌp jet"
|
||||
},
|
||||
{
|
||||
english: "Don't take any aspirins.",
|
||||
chinese: "不要吃任何阿司匹林。",
|
||||
prononciation: "dəʊnt teɪk eni æspɪrɪnz"
|
||||
},
|
||||
{
|
||||
english: "Don't play with matches.",
|
||||
chinese: "不要玩火柴。",
|
||||
prononciation: "dəʊnt pleɪ wɪð mætʃɪz"
|
||||
},
|
||||
{
|
||||
english: "The doctor says Jimmy must rest.",
|
||||
chinese: "医生说吉米必须休息。",
|
||||
prononciation: "ðə dɔktər sez dʒɪmi mʌst rest"
|
||||
},
|
||||
{
|
||||
english: "He mustn't go outside yet.",
|
||||
chinese: "他还不能出门。",
|
||||
prononciation: "hi mʌsnt gəʊ aʊtsaɪd jet"
|
||||
},
|
||||
{
|
||||
english: "Mrs. Williams must keep him warm.",
|
||||
chinese: "威廉姆斯太太必须让他保暖。",
|
||||
prononciation: "mɪsɪz wɪljəmz mʌst kip hɪm wɔrm"
|
||||
}
|
||||
],
|
||||
|
||||
story: {
|
||||
title: "At the Doctor's Visit - 看医生",
|
||||
totalSentences: 23,
|
||||
chapters: [
|
||||
{
|
||||
title: "Chapter 1: Jimmy is Sick - 第一章:吉米生病了",
|
||||
sentences: [
|
||||
{
|
||||
id: 1,
|
||||
original: "How's Jimmy today?",
|
||||
translation: "吉米今天怎么样?",
|
||||
words: [
|
||||
{word: "How's", translation: "怎么样", type: "question", pronunciation: "haʊz"},
|
||||
{word: "Jimmy", translation: "吉米", type: "noun", pronunciation: "dʒɪmi"},
|
||||
{word: "today", translation: "今天", type: "adverb", pronunciation: "tədeɪ"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
original: "Better. Thank you, doctor.",
|
||||
translation: "好一些了。谢谢你,医生。",
|
||||
words: [
|
||||
{word: "Better", translation: "好一些", type: "adjective", pronunciation: "betər"},
|
||||
{word: "Thank", translation: "谢谢", type: "verb", pronunciation: "θæŋk"},
|
||||
{word: "you", translation: "你", type: "pronoun", pronunciation: "ju"},
|
||||
{word: "doctor", translation: "医生", type: "noun", pronunciation: "dɔktər"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
original: "Can I see him please?",
|
||||
translation: "我可以看看他吗?",
|
||||
words: [
|
||||
{word: "Can", translation: "可以", type: "modal", pronunciation: "kæn"},
|
||||
{word: "I", translation: "我", type: "pronoun", pronunciation: "aɪ"},
|
||||
{word: "see", translation: "看", type: "verb", pronunciation: "si"},
|
||||
{word: "him", translation: "他", type: "pronoun", pronunciation: "hɪm"},
|
||||
{word: "please", translation: "请", type: "adverb", pronunciation: "pliz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
original: "Certainly, doctor. Come upstairs.",
|
||||
translation: "当然可以,医生。上楼来。",
|
||||
words: [
|
||||
{word: "Certainly", translation: "当然", type: "adverb", pronunciation: "sɜrtənli"},
|
||||
{word: "doctor", translation: "医生", type: "noun", pronunciation: "dɔktər"},
|
||||
{word: "Come", translation: "来", type: "verb", pronunciation: "kʌm"},
|
||||
{word: "upstairs", translation: "楼上", type: "adverb", pronunciation: "ʌpstɛrz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
original: "You look very well, Jimmy.",
|
||||
translation: "你看起来很好,吉米。",
|
||||
words: [
|
||||
{word: "You", translation: "你", type: "pronoun", pronunciation: "ju"},
|
||||
{word: "look", translation: "看起来", type: "verb", pronunciation: "lʊk"},
|
||||
{word: "very", translation: "很", type: "adverb", pronunciation: "vɛri"},
|
||||
{word: "well", translation: "好", type: "adverb", pronunciation: "wɛl"},
|
||||
{word: "Jimmy", translation: "吉米", type: "noun", pronunciation: "dʒɪmi"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
original: "You are better now.",
|
||||
translation: "你现在好多了。",
|
||||
words: [
|
||||
{word: "You", translation: "你", type: "pronoun", pronunciation: "ju"},
|
||||
{word: "are", translation: "是", type: "verb", pronunciation: "ɑr"},
|
||||
{word: "better", translation: "更好", type: "adjective", pronunciation: "betər"},
|
||||
{word: "now", translation: "现在", type: "adverb", pronunciation: "naʊ"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
original: "You mustn't get up yet.",
|
||||
translation: "你还不能起床。",
|
||||
words: [
|
||||
{word: "You", translation: "你", type: "pronoun", pronunciation: "ju"},
|
||||
{word: "mustn't", translation: "不能", type: "modal", pronunciation: "mʌsnt"},
|
||||
{word: "get", translation: "起", type: "verb", pronunciation: "get"},
|
||||
{word: "up", translation: "床", type: "adverb", pronunciation: "ʌp"},
|
||||
{word: "yet", translation: "还", type: "adverb", pronunciation: "jet"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
original: "You must stay in bed.",
|
||||
translation: "你必须卧床休息。",
|
||||
words: [
|
||||
{word: "You", translation: "你", type: "pronoun", pronunciation: "ju"},
|
||||
{word: "must", translation: "必须", type: "modal", pronunciation: "mʌst"},
|
||||
{word: "stay", translation: "停留", type: "verb", pronunciation: "steɪ"},
|
||||
{word: "in", translation: "在", type: "preposition", pronunciation: "ɪn"},
|
||||
{word: "bed", translation: "床", type: "noun", pronunciation: "bed"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
original: "He mustn't go to school yet.",
|
||||
translation: "他还不能去上学。",
|
||||
words: [
|
||||
{word: "He", translation: "他", type: "pronoun", pronunciation: "hi"},
|
||||
{word: "mustn't", translation: "不能", type: "modal", pronunciation: "mʌsnt"},
|
||||
{word: "go", translation: "去", type: "verb", pronunciation: "gəʊ"},
|
||||
{word: "to", translation: "到", type: "preposition", pronunciation: "tu"},
|
||||
{word: "school", translation: "学校", type: "noun", pronunciation: "skul"},
|
||||
{word: "yet", translation: "还", type: "adverb", pronunciation: "jet"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
original: "He mustn't eat rich food.",
|
||||
translation: "他不能吃油腻食物。",
|
||||
words: [
|
||||
{word: "He", translation: "他", type: "pronoun", pronunciation: "hi"},
|
||||
{word: "mustn't", translation: "不能", type: "modal", pronunciation: "mʌsnt"},
|
||||
{word: "eat", translation: "吃", type: "verb", pronunciation: "it"},
|
||||
{word: "rich", translation: "油腻的", type: "adjective", pronunciation: "rɪtʃ"},
|
||||
{word: "food", translation: "食物", type: "noun", pronunciation: "fud"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Chapter 2: Safety Rules - 第二章:安全规则",
|
||||
sentences: [
|
||||
{
|
||||
id: 11,
|
||||
original: "Don't take any aspirins.",
|
||||
translation: "不要吃任何阿司匹林。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "take", translation: "吃", type: "verb", pronunciation: "teɪk"},
|
||||
{word: "any", translation: "任何", type: "determiner", pronunciation: "eni"},
|
||||
{word: "aspirins", translation: "阿司匹林", type: "noun", pronunciation: "æspɪrɪnz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
original: "Don't take this medicine.",
|
||||
translation: "不要吃这个药。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "take", translation: "吃", type: "verb", pronunciation: "teɪk"},
|
||||
{word: "this", translation: "这个", type: "determiner", pronunciation: "ðɪs"},
|
||||
{word: "medicine", translation: "药", type: "noun", pronunciation: "medɪsən"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
original: "Don't play with matches.",
|
||||
translation: "不要玩火柴。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "play", translation: "玩", type: "verb", pronunciation: "pleɪ"},
|
||||
{word: "with", translation: "和", type: "preposition", pronunciation: "wɪð"},
|
||||
{word: "matches", translation: "火柴", type: "noun", pronunciation: "mætʃɪz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
original: "Don't talk in the library.",
|
||||
translation: "不要在图书馆说话。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "talk", translation: "说话", type: "verb", pronunciation: "tɔk"},
|
||||
{word: "in", translation: "在", type: "preposition", pronunciation: "ɪn"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "library", translation: "图书馆", type: "noun", pronunciation: "laɪbrəri"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
original: "Don't make a noise.",
|
||||
translation: "不要制造噪音。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "make", translation: "制造", type: "verb", pronunciation: "meɪk"},
|
||||
{word: "a", translation: "一个", type: "article", pronunciation: "ə"},
|
||||
{word: "noise", translation: "噪音", type: "noun", pronunciation: "nɔɪz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
original: "Don't drive so quickly.",
|
||||
translation: "不要开得这么快。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "drive", translation: "开车", type: "verb", pronunciation: "draɪv"},
|
||||
{word: "so", translation: "如此", type: "adverb", pronunciation: "səʊ"},
|
||||
{word: "quickly", translation: "快地", type: "adverb", pronunciation: "kwɪkli"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
original: "Don't lean out of the window.",
|
||||
translation: "不要从窗户探出身子。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "lean", translation: "探身", type: "verb", pronunciation: "lin"},
|
||||
{word: "out", translation: "出", type: "adverb", pronunciation: "aʊt"},
|
||||
{word: "of", translation: "从", type: "preposition", pronunciation: "ʌv"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "window", translation: "窗户", type: "noun", pronunciation: "wɪndəʊ"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
original: "Don't break that vase.",
|
||||
translation: "不要打破那个花瓶。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "break", translation: "打破", type: "verb", pronunciation: "breɪk"},
|
||||
{word: "that", translation: "那个", type: "determiner", pronunciation: "ðæt"},
|
||||
{word: "vase", translation: "花瓶", type: "noun", pronunciation: "vɑz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
original: "Does he have a temperature?",
|
||||
translation: "他发烧吗?",
|
||||
words: [
|
||||
{word: "Does", translation: "吗", type: "auxiliary", pronunciation: "dʌz"},
|
||||
{word: "he", translation: "他", type: "pronoun", pronunciation: "hi"},
|
||||
{word: "have", translation: "有", type: "verb", pronunciation: "hæv"},
|
||||
{word: "a", translation: "一个", type: "article", pronunciation: "ə"},
|
||||
{word: "temperature", translation: "体温", type: "noun", pronunciation: "tempərətʃər"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
original: "He has a bad cold too.",
|
||||
translation: "他也得了重感冒。",
|
||||
words: [
|
||||
{word: "He", translation: "他", type: "pronoun", pronunciation: "hi"},
|
||||
{word: "has", translation: "有", type: "verb", pronunciation: "hæz"},
|
||||
{word: "a", translation: "一个", type: "article", pronunciation: "ə"},
|
||||
{word: "bad", translation: "严重的", type: "adjective", pronunciation: "bæd"},
|
||||
{word: "cold", translation: "感冒", type: "noun", pronunciation: "kəʊld"},
|
||||
{word: "too", translation: "也", type: "adverb", pronunciation: "tu"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
original: "The doctor told Mrs. Williams to keep Jimmy warm and give him plenty of rest.",
|
||||
translation: "医生告诉威廉姆斯太太要让吉米保暖并让他多休息。",
|
||||
words: [
|
||||
{word: "The", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "doctor", translation: "医生", type: "noun", pronunciation: "dɔktər"},
|
||||
{word: "told", translation: "告诉", type: "verb", pronunciation: "toʊld"},
|
||||
{word: "Mrs.", translation: "太太", type: "title", pronunciation: "mɪsɪz"},
|
||||
{word: "Williams", translation: "威廉姆斯", type: "noun", pronunciation: "wɪljəmz"},
|
||||
{word: "to", translation: "要", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "keep", translation: "保持", type: "verb", pronunciation: "kiːp"},
|
||||
{word: "Jimmy", translation: "吉米", type: "noun", pronunciation: "dʒɪmi"},
|
||||
{word: "warm", translation: "温暖", type: "adjective", pronunciation: "wɔːrm"},
|
||||
{word: "and", translation: "并且", type: "conjunction", pronunciation: "ænd"},
|
||||
{word: "give", translation: "给", type: "verb", pronunciation: "gɪv"},
|
||||
{word: "him", translation: "他", type: "pronoun", pronunciation: "hɪm"},
|
||||
{word: "plenty", translation: "大量", type: "noun", pronunciation: "plenti"},
|
||||
{word: "of", translation: "的", type: "preposition", pronunciation: "ʌv"},
|
||||
{word: "rest", translation: "休息", type: "noun", pronunciation: "rest"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
original: "Jimmy must stay in bed until the doctor says he can go back to school.",
|
||||
translation: "吉米必须卧床直到医生说他可以回学校。",
|
||||
words: [
|
||||
{word: "Jimmy", translation: "吉米", type: "noun", pronunciation: "dʒɪmi"},
|
||||
{word: "must", translation: "必须", type: "modal", pronunciation: "mʌst"},
|
||||
{word: "stay", translation: "停留", type: "verb", pronunciation: "steɪ"},
|
||||
{word: "in", translation: "在", type: "preposition", pronunciation: "ɪn"},
|
||||
{word: "bed", translation: "床", type: "noun", pronunciation: "bed"},
|
||||
{word: "until", translation: "直到", type: "conjunction", pronunciation: "ənˈtɪl"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "doctor", translation: "医生", type: "noun", pronunciation: "dɔktər"},
|
||||
{word: "says", translation: "说", type: "verb", pronunciation: "sez"},
|
||||
{word: "he", translation: "他", type: "pronoun", pronunciation: "hi"},
|
||||
{word: "can", translation: "可以", type: "modal", pronunciation: "kæn"},
|
||||
{word: "go", translation: "去", type: "verb", pronunciation: "goʊ"},
|
||||
{word: "back", translation: "回", type: "adverb", pronunciation: "bæk"},
|
||||
{word: "to", translation: "到", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "school", translation: "学校", type: "noun", pronunciation: "skuːl"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
original: "Mrs. Williams promised to follow all the doctor's instructions very carefully until Jimmy feels completely better.",
|
||||
translation: "威廉姆斯太太承诺会非常仔细地遵循医生的所有指示直到吉米完全康复。",
|
||||
words: [
|
||||
{word: "Mrs.", translation: "太太", type: "title", pronunciation: "mɪsɪz"},
|
||||
{word: "Williams", translation: "威廉姆斯", type: "noun", pronunciation: "wɪljəmz"},
|
||||
{word: "promised", translation: "承诺", type: "verb", pronunciation: "prɑːmɪst"},
|
||||
{word: "to", translation: "要", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "follow", translation: "遵循", type: "verb", pronunciation: "fɑːloʊ"},
|
||||
{word: "all", translation: "所有", type: "determiner", pronunciation: "ɔːl"},
|
||||
{word: "the", translation: "这些", type: "article", pronunciation: "ðə"},
|
||||
{word: "doctor's", translation: "医生的", type: "noun", pronunciation: "dɔktərz"},
|
||||
{word: "instructions", translation: "指示", type: "noun", pronunciation: "ɪnˈstrʌkʃənz"},
|
||||
{word: "very", translation: "非常", type: "adverb", pronunciation: "veri"},
|
||||
{word: "carefully", translation: "仔细地", type: "adverb", pronunciation: "kɛrfəli"},
|
||||
{word: "until", translation: "直到", type: "conjunction", pronunciation: "ənˈtɪl"},
|
||||
{word: "Jimmy", translation: "吉米", type: "noun", pronunciation: "dʒɪmi"},
|
||||
{word: "feels", translation: "感觉", type: "verb", pronunciation: "fiːlz"},
|
||||
{word: "completely", translation: "完全", type: "adverb", pronunciation: "kəmˈpliːtli"},
|
||||
{word: "better", translation: "更好", type: "adjective", pronunciation: "betər"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// === GRAMMAR-BASED FILL IN THE BLANKS ===
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "You _____ stay in bed for two days.",
|
||||
options: ["must", "mustn't", "can", "don't"],
|
||||
correctAnswer: "must",
|
||||
explanation: "Use 'must' for strong medical advice",
|
||||
grammarFocus: "modal-must-mustnot"
|
||||
},
|
||||
{
|
||||
sentence: "He _____ eat rich food when sick.",
|
||||
options: ["must", "mustn't", "should", "can"],
|
||||
correctAnswer: "mustn't",
|
||||
explanation: "Use 'mustn't' for medical restrictions",
|
||||
grammarFocus: "modal-must-mustnot"
|
||||
},
|
||||
{
|
||||
sentence: "_____ take this medicine!",
|
||||
options: ["Don't", "Not", "No", "Doesn't"],
|
||||
correctAnswer: "Don't",
|
||||
explanation: "Use 'Don't' for negative commands",
|
||||
grammarFocus: "imperatives-commands"
|
||||
},
|
||||
{
|
||||
sentence: "_____ he have a temperature?",
|
||||
options: ["Do", "Does", "Is", "Has"],
|
||||
correctAnswer: "Does",
|
||||
explanation: "Use 'Does' for questions with third person singular",
|
||||
grammarFocus: "present-simple-questions"
|
||||
},
|
||||
{
|
||||
sentence: "_____ play with matches!",
|
||||
options: ["Not", "Don't", "No", "Mustn't"],
|
||||
correctAnswer: "Don't",
|
||||
explanation: "Use 'Don't' for safety warnings",
|
||||
grammarFocus: "imperatives-commands"
|
||||
},
|
||||
{
|
||||
sentence: "_____ Mr. Williams this evening?",
|
||||
options: ["Where", "Where's", "Where are", "Where is"],
|
||||
correctAnswer: "Where's",
|
||||
explanation: "Use 'Where's' (Where is) for location questions",
|
||||
grammarFocus: "present-simple-questions"
|
||||
}
|
||||
],
|
||||
|
||||
// === GRAMMAR CORRECTION EXERCISES ===
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "You don't must get up yet.",
|
||||
correct: "You mustn't get up yet.",
|
||||
explanation: "Use 'mustn't' not 'don't must' for prohibition",
|
||||
grammarFocus: "modal-must-mustnot"
|
||||
},
|
||||
{
|
||||
incorrect: "Not take this medicine!",
|
||||
correct: "Don't take this medicine!",
|
||||
explanation: "Use 'Don't' + verb for negative commands",
|
||||
grammarFocus: "imperatives-commands"
|
||||
},
|
||||
{
|
||||
incorrect: "He must not to eat rich food.",
|
||||
correct: "He mustn't eat rich food.",
|
||||
explanation: "Don't use 'to' after modal verbs",
|
||||
grammarFocus: "modal-must-mustnot"
|
||||
},
|
||||
{
|
||||
incorrect: "Do he have a temperature?",
|
||||
correct: "Does he have a temperature?",
|
||||
explanation: "Use 'Does' with third person singular subjects",
|
||||
grammarFocus: "present-simple-questions"
|
||||
},
|
||||
{
|
||||
incorrect: "You must to stay in bed.",
|
||||
correct: "You must stay in bed.",
|
||||
explanation: "Don't use 'to' after 'must'",
|
||||
grammarFocus: "modal-must-mustnot"
|
||||
}
|
||||
],
|
||||
|
||||
// === ADDITIONAL READING STORIES ===
|
||||
additionalStories: [
|
||||
{
|
||||
title: "Doctor's Advice - 医生的建议",
|
||||
totalSentences: 12,
|
||||
chapters: [
|
||||
{
|
||||
title: "Chapter 1: Following Medical Instructions - 遵循医嘱",
|
||||
sentences: [
|
||||
{
|
||||
id: 1,
|
||||
original: "The doctor says Jimmy must rest.",
|
||||
translation: "医生说吉米必须休息。",
|
||||
words: [
|
||||
{word: "The", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "doctor", translation: "医生", type: "noun", pronunciation: "dɔktər"},
|
||||
{word: "says", translation: "说", type: "verb", pronunciation: "sez"},
|
||||
{word: "Jimmy", translation: "吉米", type: "noun", pronunciation: "dʒɪmi"},
|
||||
{word: "must", translation: "必须", type: "modal", pronunciation: "mʌst"},
|
||||
{word: "rest", translation: "休息", type: "verb", pronunciation: "rest"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
original: "He mustn't go outside yet.",
|
||||
translation: "他还不能出门。",
|
||||
words: [
|
||||
{word: "He", translation: "他", type: "pronoun", pronunciation: "hi"},
|
||||
{word: "mustn't", translation: "不能", type: "modal", pronunciation: "mʌsnt"},
|
||||
{word: "go", translation: "去", type: "verb", pronunciation: "gəʊ"},
|
||||
{word: "outside", translation: "外面", type: "adverb", pronunciation: "aʊtsaɪd"},
|
||||
{word: "yet", translation: "还", type: "adverb", pronunciation: "jet"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
original: "Mrs. Williams must keep him warm.",
|
||||
translation: "威廉姆斯太太必须让他保暖。",
|
||||
words: [
|
||||
{word: "Mrs.", translation: "太太", type: "title", pronunciation: "mɪsɪz"},
|
||||
{word: "Williams", translation: "威廉姆斯", type: "noun", pronunciation: "wɪljəmz"},
|
||||
{word: "must", translation: "必须", type: "modal", pronunciation: "mʌst"},
|
||||
{word: "keep", translation: "保持", type: "verb", pronunciation: "kip"},
|
||||
{word: "him", translation: "他", type: "pronoun", pronunciation: "hɪm"},
|
||||
{word: "warm", translation: "温暖", type: "adjective", pronunciation: "wɔrm"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
original: "Don't give him cold drinks.",
|
||||
translation: "不要给他冷饮。",
|
||||
words: [
|
||||
{word: "Don't", translation: "不要", type: "auxiliary", pronunciation: "dəʊnt"},
|
||||
{word: "give", translation: "给", type: "verb", pronunciation: "gɪv"},
|
||||
{word: "him", translation: "他", type: "pronoun", pronunciation: "hɪm"},
|
||||
{word: "cold", translation: "冷的", type: "adjective", pronunciation: "kəʊld"},
|
||||
{word: "drinks", translation: "饮料", type: "noun", pronunciation: "drɪŋks"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// CONTENT STRUCTURE SUMMARY - FOR AI REFERENCE
|
||||
// ============================================================================
|
||||
// This module contains:
|
||||
// - Medical dialogue vocabulary and situations
|
||||
// - Modal verbs (must/mustn't) for obligations and prohibitions
|
||||
// - Imperative commands for instructions and warnings
|
||||
// - Present simple questions for medical inquiries
|
||||
// - Safety-focused vocabulary and scenarios
|
||||
// - Chinese translations for all content
|
||||
// - Comprehensive grammar explanations and examples
|
||||
// ============================================================================
|
||||
1134
src/content/NCE2-Lesson3.js
Normal file
1134
src/content/NCE2-Lesson3.js
Normal file
File diff suppressed because it is too large
Load Diff
785
src/content/NCE2-Lesson30.js
Normal file
785
src/content/NCE2-Lesson30.js
Normal file
@ -0,0 +1,785 @@
|
||||
// === LESSON 30: FOOTBALL OR POLO? ===
|
||||
// English learning story with Chinese translation - Intermediate Level
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.NCE2Lesson30 = {
|
||||
id: "nce2-lesson30",
|
||||
name: "NCE2-Lesson30",
|
||||
description: "Football or polo? - A story about the Wayle river with grammar focus on articles and quantifiers",
|
||||
difficulty: "intermediate",
|
||||
language: "en-US",
|
||||
userLanguage: "zh-CN",
|
||||
totalWords: 200,
|
||||
|
||||
// === GRAMMAR LESSONS SYSTEM ===
|
||||
grammar: {
|
||||
"articles-usage": {
|
||||
title: "Articles Usage - 冠词使用",
|
||||
explanation: "English uses 'a', 'an', and 'the' in specific ways, especially with names and places.",
|
||||
rules: [
|
||||
"the - use with rivers, seas, oceans, mountain ranges: the Thames, the Pacific",
|
||||
"no article - use with most personal names and countries: John, England",
|
||||
"the - use with certain countries: the United States, the United Kingdom",
|
||||
"the - use with superlatives and unique things: the best, the sun"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "The Wayle is a small river.",
|
||||
chinese: "威尔河是一条小河。",
|
||||
explanation: "Use 'the' with river names",
|
||||
pronunciation: "ðə weɪl ɪz ə smɔːl ˈrɪvər"
|
||||
},
|
||||
{
|
||||
english: "Paris is on the Seine.",
|
||||
chinese: "巴黎在塞纳河上。",
|
||||
explanation: "Use 'the' with famous rivers",
|
||||
pronunciation: "ˈpærɪs ɪz ɒn ðə seɪn"
|
||||
},
|
||||
{
|
||||
english: "London is on the Thames.",
|
||||
chinese: "伦敦在泰晤士河上。",
|
||||
explanation: "River names always take 'the'",
|
||||
pronunciation: "ˈlʌndən ɪz ɒn ðə temz"
|
||||
},
|
||||
{
|
||||
english: "He lives in England.",
|
||||
chinese: "他住在英国。",
|
||||
explanation: "Most country names don't use 'the'",
|
||||
pronunciation: "hiː lɪvz ɪn ˈɪŋɡlənd"
|
||||
},
|
||||
{
|
||||
english: "I went to the United States.",
|
||||
chinese: "我去了美国。",
|
||||
explanation: "Some countries use 'the'",
|
||||
pronunciation: "aɪ went tuː ðə juˈnaɪtɪd steɪts"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "_____ Thames flows through London.",
|
||||
options: ["The", "A", "An", "No article"],
|
||||
correct: "The",
|
||||
explanation: "River names always use 'the'"
|
||||
},
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "John lives in _____ England.",
|
||||
options: ["the", "a", "an", "no article"],
|
||||
correct: "no article",
|
||||
explanation: "Most country names don't use articles"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"some-any-usage": {
|
||||
title: "Some and Any Usage - Some和Any的使用",
|
||||
explanation: "English uses 'some' and 'any' differently depending on sentence type and meaning.",
|
||||
rules: [
|
||||
"some - use in positive statements: There are some people",
|
||||
"any - use in questions: Are there any people?",
|
||||
"any - use in negative statements: There aren't any people",
|
||||
"some - use in offers and requests: Would you like some tea?"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "Some children were playing games.",
|
||||
chinese: "一些孩子在玩游戏。",
|
||||
explanation: "Use 'some' in positive statements",
|
||||
pronunciation: "sʌm ˈtʃɪldrən wər ˈpleɪɪŋ ɡeɪmz"
|
||||
},
|
||||
{
|
||||
english: "There were some people rowing.",
|
||||
chinese: "有一些人在划船。",
|
||||
explanation: "Use 'some' to describe what exists",
|
||||
pronunciation: "ðər wər sʌm ˈpiːpəl ˈroʊɪŋ"
|
||||
},
|
||||
{
|
||||
english: "There weren't any children in sight.",
|
||||
chinese: "看不见任何孩子。",
|
||||
explanation: "Use 'any' in negative statements",
|
||||
pronunciation: "ðər wərnt ˈeni ˈtʃɪldrən ɪn saɪt"
|
||||
},
|
||||
{
|
||||
english: "Are there any boats on the river?",
|
||||
chinese: "河上有船吗?",
|
||||
explanation: "Use 'any' in questions",
|
||||
pronunciation: "ər ðər ˈeni boʊts ɒn ðə ˈrɪvər"
|
||||
},
|
||||
{
|
||||
english: "Would you like some water?",
|
||||
chinese: "你想要一些水吗?",
|
||||
explanation: "Use 'some' in offers",
|
||||
pronunciation: "wʊd juː laɪk sʌm ˈwɔːtər"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "There are _____ people on the bank.",
|
||||
options: ["some", "any", "a", "the"],
|
||||
correct: "some",
|
||||
explanation: "Use 'some' in positive statements"
|
||||
},
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "There weren't _____ children in sight.",
|
||||
options: ["some", "any", "a", "the"],
|
||||
correct: "any",
|
||||
explanation: "Use 'any' in negative statements"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"past-continuous": {
|
||||
title: "Past Continuous Tense - 过去进行时",
|
||||
explanation: "Use past continuous for actions that were in progress at a specific time in the past.",
|
||||
rules: [
|
||||
"Form: was/were + verb-ing",
|
||||
"Use for ongoing past actions: I was sitting by the river",
|
||||
"Use for background actions in stories: Children were playing while I sat",
|
||||
"Use with time expressions: at 3pm yesterday, last Sunday"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "Some children were playing games.",
|
||||
chinese: "一些孩子在玩游戏。",
|
||||
explanation: "Action in progress in the past",
|
||||
pronunciation: "sʌm ˈtʃɪldrən wər ˈpleɪɪŋ ɡeɪmz"
|
||||
},
|
||||
{
|
||||
english: "People were rowing on the river.",
|
||||
chinese: "人们在河上划船。",
|
||||
explanation: "Ongoing action in the past",
|
||||
pronunciation: "ˈpiːpəl wər ˈroʊɪŋ ɒn ðə ˈrɪvər"
|
||||
},
|
||||
{
|
||||
english: "I was sitting by the river.",
|
||||
chinese: "我坐在河边。",
|
||||
explanation: "Past continuous shows duration",
|
||||
pronunciation: "aɪ wəz ˈsɪtɪŋ baɪ ðə ˈrɪvər"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "The children _____ playing when it happened.",
|
||||
options: ["were", "was", "are", "is"],
|
||||
correct: "were",
|
||||
explanation: "Use 'were' with plural subjects in past continuous"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
vocabulary: {
|
||||
"polo": {
|
||||
"user_language": "水球",
|
||||
"type": "noun",
|
||||
"pronunciation": "ˈpoʊloʊ"
|
||||
},
|
||||
"river": {
|
||||
"user_language": "河流",
|
||||
"type": "noun",
|
||||
"pronunciation": "ˈrɪvər"
|
||||
},
|
||||
"cut": {
|
||||
"user_language": "穿过",
|
||||
"type": "verb",
|
||||
"pronunciation": "kʌt"
|
||||
},
|
||||
"park": {
|
||||
"user_language": "公园",
|
||||
"type": "noun",
|
||||
"pronunciation": "pɑːrk"
|
||||
},
|
||||
"afternoon": {
|
||||
"user_language": "下午",
|
||||
"type": "noun",
|
||||
"pronunciation": "ˌæftərˈnuːn"
|
||||
},
|
||||
"bank": {
|
||||
"user_language": "河岸",
|
||||
"type": "noun",
|
||||
"pronunciation": "bæŋk"
|
||||
},
|
||||
"usual": {
|
||||
"user_language": "平常的",
|
||||
"type": "adjective",
|
||||
"pronunciation": "ˈjuːʒuəl"
|
||||
},
|
||||
"children": {
|
||||
"user_language": "孩子们",
|
||||
"type": "noun",
|
||||
"pronunciation": "ˈtʃɪldrən"
|
||||
},
|
||||
"games": {
|
||||
"user_language": "游戏",
|
||||
"type": "noun",
|
||||
"pronunciation": "ɡeɪmz"
|
||||
},
|
||||
"people": {
|
||||
"user_language": "人们",
|
||||
"type": "noun",
|
||||
"pronunciation": "ˈpiːpəl"
|
||||
},
|
||||
"row": {
|
||||
"user_language": "划船",
|
||||
"type": "verb",
|
||||
"pronunciation": "roʊ"
|
||||
},
|
||||
"suddenly": {
|
||||
"user_language": "突然",
|
||||
"type": "adverb",
|
||||
"pronunciation": "ˈsʌdənli"
|
||||
},
|
||||
"kick": {
|
||||
"user_language": "踢",
|
||||
"type": "verb",
|
||||
"pronunciation": "kɪk"
|
||||
},
|
||||
"ball": {
|
||||
"user_language": "球",
|
||||
"type": "noun",
|
||||
"pronunciation": "bɔːl"
|
||||
},
|
||||
"hard": {
|
||||
"user_language": "用力地",
|
||||
"type": "adverb",
|
||||
"pronunciation": "hɑːrd"
|
||||
},
|
||||
"towards": {
|
||||
"user_language": "朝向",
|
||||
"type": "preposition",
|
||||
"pronunciation": "təˈwɔːrdz"
|
||||
},
|
||||
"passing": {
|
||||
"user_language": "经过的",
|
||||
"type": "adjective",
|
||||
"pronunciation": "ˈpæsɪŋ"
|
||||
},
|
||||
"boat": {
|
||||
"user_language": "船",
|
||||
"type": "noun",
|
||||
"pronunciation": "boʊt"
|
||||
},
|
||||
"called": {
|
||||
"user_language": "叫喊",
|
||||
"type": "verb",
|
||||
"pronunciation": "kɔːld"
|
||||
},
|
||||
"hear": {
|
||||
"user_language": "听见",
|
||||
"type": "verb",
|
||||
"pronunciation": "hɪr"
|
||||
},
|
||||
"struck": {
|
||||
"user_language": "击打",
|
||||
"type": "verb",
|
||||
"pronunciation": "strʌk"
|
||||
},
|
||||
"nearly": {
|
||||
"user_language": "几乎",
|
||||
"type": "adverb",
|
||||
"pronunciation": "ˈnɪrli"
|
||||
},
|
||||
"fell": {
|
||||
"user_language": "落下",
|
||||
"type": "verb",
|
||||
"pronunciation": "fel"
|
||||
},
|
||||
"water": {
|
||||
"user_language": "水",
|
||||
"type": "noun",
|
||||
"pronunciation": "ˈwɔːtər"
|
||||
},
|
||||
"turned": {
|
||||
"user_language": "转身",
|
||||
"type": "verb",
|
||||
"pronunciation": "tɜːrnd"
|
||||
},
|
||||
"sight": {
|
||||
"user_language": "视线",
|
||||
"type": "noun",
|
||||
"pronunciation": "saɪt"
|
||||
},
|
||||
"run away": {
|
||||
"user_language": "跑开",
|
||||
"type": "verb",
|
||||
"pronunciation": "rʌn əˈweɪ"
|
||||
},
|
||||
"laughed": {
|
||||
"user_language": "笑",
|
||||
"type": "verb",
|
||||
"pronunciation": "læft"
|
||||
},
|
||||
"realized": {
|
||||
"user_language": "意识到",
|
||||
"type": "verb",
|
||||
"pronunciation": "ˈriəlaɪzd"
|
||||
},
|
||||
"happened": {
|
||||
"user_language": "发生",
|
||||
"type": "verb",
|
||||
"pronunciation": "ˈhæpənd"
|
||||
},
|
||||
"threw": {
|
||||
"user_language": "扔",
|
||||
"type": "verb",
|
||||
"pronunciation": "θruː"
|
||||
},
|
||||
"back": {
|
||||
"user_language": "回来",
|
||||
"type": "adverb",
|
||||
"pronunciation": "bæk"
|
||||
}
|
||||
},
|
||||
|
||||
// === SENTENCES FOR GAMES (extracted from stories) ===
|
||||
sentences: [
|
||||
{
|
||||
english: "The Wayle is a small river.",
|
||||
chinese: "威尔河是一条小河。",
|
||||
prononciation: "ðə weɪl ɪz ə smɔːl ˈrɪvər"
|
||||
},
|
||||
{
|
||||
english: "It cuts across the park near my home.",
|
||||
chinese: "它横穿我家附近的公园。",
|
||||
prononciation: "ɪt kʌts əˈkrɔːs ðə pɑːrk nɪr maɪ hoʊm"
|
||||
},
|
||||
{
|
||||
english: "I like sitting by the Wayle on fine afternoons.",
|
||||
chinese: "我喜欢在晴朗的下午坐在威尔河边。",
|
||||
prononciation: "aɪ laɪk ˈsɪtɪŋ baɪ ðə weɪl ɑːn faɪn ˌæftərˈnuːnz"
|
||||
},
|
||||
{
|
||||
english: "Some children were playing games on the bank.",
|
||||
chinese: "一些孩子在河岸上玩游戏。",
|
||||
prononciation: "sʌm ˈtʃɪldrən wər ˈpleɪɪŋ geɪmz ɑːn ðə bæŋk"
|
||||
},
|
||||
{
|
||||
english: "There were some people rowing on the river.",
|
||||
chinese: "河上有一些人在划船。",
|
||||
prononciation: "ðɛr wər sʌm ˈpiːpəl ˈroʊɪŋ ɑːn ðə ˈrɪvər"
|
||||
},
|
||||
{
|
||||
english: "The ball struck him so hard.",
|
||||
chinese: "球重重地打在他身上。",
|
||||
prononciation: "ðə bɔːl strʌk hɪm soʊ hɑːrd"
|
||||
},
|
||||
{
|
||||
english: "This is a pleasant surprise!",
|
||||
chinese: "这真是个意外的惊喜!",
|
||||
prononciation: "ðɪs ɪz ə ˈplɛzənt sərˈpraɪz"
|
||||
},
|
||||
{
|
||||
english: "I turned to look at the children.",
|
||||
chinese: "我转身看向孩子们。",
|
||||
prononciation: "aɪ tɜːrnd tuː lʊk æt ðə ˈtʃɪldrən"
|
||||
},
|
||||
{
|
||||
english: "They were playing football, not polo!",
|
||||
chinese: "他们在踢足球,不是水球!",
|
||||
prononciation: "ðeɪ wər ˈpleɪɪŋ ˈfʊtbɔːl nɑːt ˈpoʊloʊ"
|
||||
}
|
||||
],
|
||||
|
||||
story: {
|
||||
title: "Football or polo? - 足球还是水球?",
|
||||
totalSentences: 12,
|
||||
chapters: [
|
||||
{
|
||||
title: "Chapter 1: A Day by the River - 河边的一天",
|
||||
sentences: [
|
||||
{
|
||||
id: 1,
|
||||
original: "The Wayle is a small river that cuts across the park near my home.",
|
||||
translation: "威尔河是横穿我家附近公园的一条小河。",
|
||||
words: [
|
||||
{word: "The", translation: "这条", type: "article", pronunciation: "ðə"},
|
||||
{word: "Wayle", translation: "威尔河", type: "noun", pronunciation: "weɪl"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "ɪz"},
|
||||
{word: "a", translation: "一条", type: "article", pronunciation: "ə"},
|
||||
{word: "small", translation: "小的", type: "adjective", pronunciation: "smɔːl"},
|
||||
{word: "river", translation: "河流", type: "noun", pronunciation: "ˈrɪvər"},
|
||||
{word: "that", translation: "那", type: "pronoun", pronunciation: "ðæt"},
|
||||
{word: "cuts", translation: "穿过", type: "verb", pronunciation: "kʌts"},
|
||||
{word: "across", translation: "横穿", type: "preposition", pronunciation: "əˈkrɔːs"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "park", translation: "公园", type: "noun", pronunciation: "pɑːrk"},
|
||||
{word: "near", translation: "靠近", type: "preposition", pronunciation: "nɪr"},
|
||||
{word: "my", translation: "我的", type: "pronoun", pronunciation: "maɪ"},
|
||||
{word: "home", translation: "家", type: "noun", pronunciation: "hoʊm"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
original: "I like sitting by the Wayle on fine afternoons.",
|
||||
translation: "我喜欢在天气晴朗的下午坐在威尔河边。",
|
||||
words: [
|
||||
{word: "I", translation: "我", type: "pronoun", pronunciation: "aɪ"},
|
||||
{word: "like", translation: "喜欢", type: "verb", pronunciation: "laɪk"},
|
||||
{word: "sitting", translation: "坐", type: "verb", pronunciation: "ˈsɪtɪŋ"},
|
||||
{word: "by", translation: "在...旁边", type: "preposition", pronunciation: "baɪ"},
|
||||
{word: "the", translation: "这条", type: "article", pronunciation: "ðə"},
|
||||
{word: "Wayle", translation: "威尔河", type: "noun", pronunciation: "weɪl"},
|
||||
{word: "on", translation: "在", type: "preposition", pronunciation: "ɒn"},
|
||||
{word: "fine", translation: "晴朗的", type: "adjective", pronunciation: "faɪn"},
|
||||
{word: "afternoons", translation: "下午", type: "noun", pronunciation: "ˌæftərˈnuːnz"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
original: "It was warm last Sunday, so I went and sat on the river bank as usual.",
|
||||
translation: "上星期日天气很暖和,于是我像往常一样又去河边坐坐。",
|
||||
words: [
|
||||
{word: "It", translation: "天气", type: "pronoun", pronunciation: "ɪt"},
|
||||
{word: "was", translation: "是", type: "verb", pronunciation: "wəz"},
|
||||
{word: "warm", translation: "暖和的", type: "adjective", pronunciation: "wɔːrm"},
|
||||
{word: "last", translation: "上", type: "adjective", pronunciation: "læst"},
|
||||
{word: "Sunday", translation: "星期日", type: "noun", pronunciation: "ˈsʌndeɪ"},
|
||||
{word: "so", translation: "所以", type: "conjunction", pronunciation: "soʊ"},
|
||||
{word: "I", translation: "我", type: "pronoun", pronunciation: "aɪ"},
|
||||
{word: "went", translation: "去了", type: "verb", pronunciation: "went"},
|
||||
{word: "and", translation: "和", type: "conjunction", pronunciation: "ænd"},
|
||||
{word: "sat", translation: "坐", type: "verb", pronunciation: "sæt"},
|
||||
{word: "on", translation: "在", type: "preposition", pronunciation: "ɒn"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "river", translation: "河", type: "noun", pronunciation: "ˈrɪvər"},
|
||||
{word: "bank", translation: "岸边", type: "noun", pronunciation: "bæŋk"},
|
||||
{word: "as", translation: "像", type: "adverb", pronunciation: "æz"},
|
||||
{word: "usual", translation: "往常一样", type: "adjective", pronunciation: "ˈjuːʒuəl"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
original: "Some children were playing games on the bank and there were some people rowing on the river.",
|
||||
translation: "河岸上有些孩子在玩耍,河面上有些人在划船。",
|
||||
words: [
|
||||
{word: "Some", translation: "一些", type: "determiner", pronunciation: "sʌm"},
|
||||
{word: "children", translation: "孩子们", type: "noun", pronunciation: "ˈtʃɪldrən"},
|
||||
{word: "were", translation: "正在", type: "verb", pronunciation: "wər"},
|
||||
{word: "playing", translation: "玩", type: "verb", pronunciation: "ˈpleɪɪŋ"},
|
||||
{word: "games", translation: "游戏", type: "noun", pronunciation: "ɡeɪmz"},
|
||||
{word: "on", translation: "在", type: "preposition", pronunciation: "ɒn"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "bank", translation: "岸边", type: "noun", pronunciation: "bæŋk"},
|
||||
{word: "and", translation: "和", type: "conjunction", pronunciation: "ænd"},
|
||||
{word: "there", translation: "那里", type: "adverb", pronunciation: "ðer"},
|
||||
{word: "were", translation: "有", type: "verb", pronunciation: "wər"},
|
||||
{word: "some", translation: "一些", type: "determiner", pronunciation: "sʌm"},
|
||||
{word: "people", translation: "人们", type: "noun", pronunciation: "ˈpiːpəl"},
|
||||
{word: "rowing", translation: "划船", type: "verb", pronunciation: "ˈroʊɪŋ"},
|
||||
{word: "on", translation: "在", type: "preposition", pronunciation: "ɒn"},
|
||||
{word: "the", translation: "这条", type: "article", pronunciation: "ðə"},
|
||||
{word: "river", translation: "河", type: "noun", pronunciation: "ˈrɪvər"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Chapter 2: The Ball Incident - 球的事件",
|
||||
sentences: [
|
||||
{
|
||||
id: 5,
|
||||
original: "Suddenly, one of the children kicked a ball very hard and it went towards a passing boat.",
|
||||
translation: "突然,一个孩子狠狠地踢了一脚球,球便向一条经过的小船飞去。",
|
||||
words: [
|
||||
{word: "Suddenly", translation: "突然", type: "adverb", pronunciation: "ˈsʌdənli"},
|
||||
{word: "one", translation: "一个", type: "number", pronunciation: "wʌn"},
|
||||
{word: "of", translation: "的", type: "preposition", pronunciation: "ʌv"},
|
||||
{word: "the", translation: "这些", type: "article", pronunciation: "ðə"},
|
||||
{word: "children", translation: "孩子们", type: "noun", pronunciation: "ˈtʃɪldrən"},
|
||||
{word: "kicked", translation: "踢了", type: "verb", pronunciation: "kɪkt"},
|
||||
{word: "a", translation: "一个", type: "article", pronunciation: "ə"},
|
||||
{word: "ball", translation: "球", type: "noun", pronunciation: "bɔːl"},
|
||||
{word: "very", translation: "非常", type: "adverb", pronunciation: "ˈveri"},
|
||||
{word: "hard", translation: "用力地", type: "adverb", pronunciation: "hɑːrd"},
|
||||
{word: "and", translation: "并且", type: "conjunction", pronunciation: "ænd"},
|
||||
{word: "it", translation: "它", type: "pronoun", pronunciation: "ɪt"},
|
||||
{word: "went", translation: "飞去", type: "verb", pronunciation: "went"},
|
||||
{word: "towards", translation: "朝向", type: "preposition", pronunciation: "təˈwɔːrdz"},
|
||||
{word: "a", translation: "一条", type: "article", pronunciation: "ə"},
|
||||
{word: "passing", translation: "经过的", type: "adjective", pronunciation: "ˈpæsɪŋ"},
|
||||
{word: "boat", translation: "船", type: "noun", pronunciation: "boʊt"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
original: "Some people on the bank called out to the man in the boat, but he did not hear them.",
|
||||
translation: "岸上的一些人对船上的人高喊,但他没有听见。",
|
||||
words: [
|
||||
{word: "Some", translation: "一些", type: "determiner", pronunciation: "sʌm"},
|
||||
{word: "people", translation: "人们", type: "noun", pronunciation: "ˈpiːpəl"},
|
||||
{word: "on", translation: "在", type: "preposition", pronunciation: "ɒn"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "bank", translation: "岸上", type: "noun", pronunciation: "bæŋk"},
|
||||
{word: "called", translation: "叫喊", type: "verb", pronunciation: "kɔːld"},
|
||||
{word: "out", translation: "出来", type: "adverb", pronunciation: "aʊt"},
|
||||
{word: "to", translation: "对", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "man", translation: "人", type: "noun", pronunciation: "mæn"},
|
||||
{word: "in", translation: "在", type: "preposition", pronunciation: "ɪn"},
|
||||
{word: "the", translation: "这条", type: "article", pronunciation: "ðə"},
|
||||
{word: "boat", translation: "船上", type: "noun", pronunciation: "boʊt"},
|
||||
{word: "but", translation: "但是", type: "conjunction", pronunciation: "bʌt"},
|
||||
{word: "he", translation: "他", type: "pronoun", pronunciation: "hiː"},
|
||||
{word: "did", translation: "助动词", type: "auxiliary", pronunciation: "dɪd"},
|
||||
{word: "not", translation: "不", type: "adverb", pronunciation: "nɑːt"},
|
||||
{word: "hear", translation: "听见", type: "verb", pronunciation: "hɪr"},
|
||||
{word: "them", translation: "他们", type: "pronoun", pronunciation: "ðem"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
original: "The ball struck him so hard that he nearly fell into the water.",
|
||||
translation: "球重重地打在他身上,使他差点儿落入水中。",
|
||||
words: [
|
||||
{word: "The", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "ball", translation: "球", type: "noun", pronunciation: "bɔːl"},
|
||||
{word: "struck", translation: "击打", type: "verb", pronunciation: "strʌk"},
|
||||
{word: "him", translation: "他", type: "pronoun", pronunciation: "hɪm"},
|
||||
{word: "so", translation: "如此", type: "adverb", pronunciation: "soʊ"},
|
||||
{word: "hard", translation: "用力", type: "adverb", pronunciation: "hɑːrd"},
|
||||
{word: "that", translation: "以至于", type: "conjunction", pronunciation: "ðæt"},
|
||||
{word: "he", translation: "他", type: "pronoun", pronunciation: "hiː"},
|
||||
{word: "nearly", translation: "几乎", type: "adverb", pronunciation: "ˈnɪrli"},
|
||||
{word: "fell", translation: "落下", type: "verb", pronunciation: "fel"},
|
||||
{word: "into", translation: "进入", type: "preposition", pronunciation: "ˈɪntuː"},
|
||||
{word: "the", translation: "这", type: "article", pronunciation: "ðə"},
|
||||
{word: "water", translation: "水中", type: "noun", pronunciation: "ˈwɔːtər"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
original: "I turned to look at the children, but there weren't any in sight: they had all run away!",
|
||||
translation: "我转过头去看那些孩子,但一个也不见,全都跑了!",
|
||||
words: [
|
||||
{word: "I", translation: "我", type: "pronoun", pronunciation: "aɪ"},
|
||||
{word: "turned", translation: "转身", type: "verb", pronunciation: "tɜːrnd"},
|
||||
{word: "to", translation: "去", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "look", translation: "看", type: "verb", pronunciation: "lʊk"},
|
||||
{word: "at", translation: "看", type: "preposition", pronunciation: "æt"},
|
||||
{word: "the", translation: "这些", type: "article", pronunciation: "ðə"},
|
||||
{word: "children", translation: "孩子们", type: "noun", pronunciation: "ˈtʃɪldrən"},
|
||||
{word: "but", translation: "但是", type: "conjunction", pronunciation: "bʌt"},
|
||||
{word: "there", translation: "那里", type: "adverb", pronunciation: "ðer"},
|
||||
{word: "weren't", translation: "没有", type: "verb", pronunciation: "wərnt"},
|
||||
{word: "any", translation: "任何", type: "determiner", pronunciation: "ˈeni"},
|
||||
{word: "in", translation: "在", type: "preposition", pronunciation: "ɪn"},
|
||||
{word: "sight", translation: "视线中", type: "noun", pronunciation: "saɪt"},
|
||||
{word: "they", translation: "他们", type: "pronoun", pronunciation: "ðeɪ"},
|
||||
{word: "had", translation: "已经", type: "auxiliary", pronunciation: "hæd"},
|
||||
{word: "all", translation: "全部", type: "adverb", pronunciation: "ɔːl"},
|
||||
{word: "run", translation: "跑", type: "verb", pronunciation: "rʌn"},
|
||||
{word: "away", translation: "走了", type: "adverb", pronunciation: "əˈweɪ"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Chapter 3: The Happy Ending - 愉快的结局",
|
||||
sentences: [
|
||||
{
|
||||
id: 9,
|
||||
original: "The man laughed when he realized what had happened.",
|
||||
translation: "当那个人明白了发生的事情时,笑了。",
|
||||
words: [
|
||||
{word: "The", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "man", translation: "人", type: "noun", pronunciation: "mæn"},
|
||||
{word: "laughed", translation: "笑了", type: "verb", pronunciation: "læft"},
|
||||
{word: "when", translation: "当", type: "conjunction", pronunciation: "wen"},
|
||||
{word: "he", translation: "他", type: "pronoun", pronunciation: "hiː"},
|
||||
{word: "realized", translation: "意识到", type: "verb", pronunciation: "ˈriəlaɪzd"},
|
||||
{word: "what", translation: "什么", type: "pronoun", pronunciation: "wʌt"},
|
||||
{word: "had", translation: "已经", type: "auxiliary", pronunciation: "hæd"},
|
||||
{word: "happened", translation: "发生了", type: "verb", pronunciation: "ˈhæpənd"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
original: "He called out to the children and threw the ball back to the bank.",
|
||||
translation: "他大声叫那些孩子,把球扔回到岸上。",
|
||||
words: [
|
||||
{word: "He", translation: "他", type: "pronoun", pronunciation: "hiː"},
|
||||
{word: "called", translation: "叫", type: "verb", pronunciation: "kɔːld"},
|
||||
{word: "out", translation: "出来", type: "adverb", pronunciation: "aʊt"},
|
||||
{word: "to", translation: "对", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "the", translation: "这些", type: "article", pronunciation: "ðə"},
|
||||
{word: "children", translation: "孩子们", type: "noun", pronunciation: "ˈtʃɪldrən"},
|
||||
{word: "and", translation: "并且", type: "conjunction", pronunciation: "ænd"},
|
||||
{word: "threw", translation: "扔", type: "verb", pronunciation: "θruː"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "ball", translation: "球", type: "noun", pronunciation: "bɔːl"},
|
||||
{word: "back", translation: "回", type: "adverb", pronunciation: "bæk"},
|
||||
{word: "to", translation: "到", type: "preposition", pronunciation: "tuː"},
|
||||
{word: "the", translation: "这个", type: "article", pronunciation: "ðə"},
|
||||
{word: "bank", translation: "岸上", type: "noun", pronunciation: "bæŋk"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// === GRAMMAR-BASED FILL IN THE BLANKS ===
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "_____ Wayle is a small river.",
|
||||
options: ["The", "A", "An", "No article"],
|
||||
correctAnswer: "The",
|
||||
explanation: "River names always use 'the'",
|
||||
grammarFocus: "articles-usage"
|
||||
},
|
||||
{
|
||||
sentence: "There were _____ people rowing on the river.",
|
||||
options: ["some", "any", "a", "the"],
|
||||
correctAnswer: "some",
|
||||
explanation: "Use 'some' in positive statements",
|
||||
grammarFocus: "some-any-usage"
|
||||
},
|
||||
{
|
||||
sentence: "There weren't _____ children in sight.",
|
||||
options: ["some", "any", "a", "the"],
|
||||
correctAnswer: "any",
|
||||
explanation: "Use 'any' in negative statements",
|
||||
grammarFocus: "some-any-usage"
|
||||
},
|
||||
{
|
||||
sentence: "The children _____ playing games on the bank.",
|
||||
options: ["were", "was", "are", "is"],
|
||||
correctAnswer: "were",
|
||||
explanation: "Use 'were' with plural subjects in past continuous",
|
||||
grammarFocus: "past-continuous"
|
||||
},
|
||||
{
|
||||
sentence: "He lives in _____ England.",
|
||||
options: ["the", "a", "an", "no article"],
|
||||
correctAnswer: "no article",
|
||||
explanation: "Most country names don't use articles",
|
||||
grammarFocus: "articles-usage"
|
||||
},
|
||||
{
|
||||
sentence: "Are there _____ boats on the river?",
|
||||
options: ["some", "any", "a", "the"],
|
||||
correctAnswer: "any",
|
||||
explanation: "Use 'any' in questions",
|
||||
grammarFocus: "some-any-usage"
|
||||
}
|
||||
],
|
||||
|
||||
// === GRAMMAR CORRECTION EXERCISES ===
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "Wayle is small river.",
|
||||
correct: "The Wayle is a small river.",
|
||||
explanation: "River names need 'the' and countable nouns need 'a'",
|
||||
grammarFocus: "articles-usage"
|
||||
},
|
||||
{
|
||||
incorrect: "There were any children playing.",
|
||||
correct: "There were some children playing.",
|
||||
explanation: "Use 'some' in positive statements, not 'any'",
|
||||
grammarFocus: "some-any-usage"
|
||||
},
|
||||
{
|
||||
incorrect: "There wasn't some people in sight.",
|
||||
correct: "There weren't any people in sight.",
|
||||
explanation: "Use 'any' in negative statements, not 'some'",
|
||||
grammarFocus: "some-any-usage"
|
||||
},
|
||||
{
|
||||
incorrect: "He goes to United States.",
|
||||
correct: "He goes to the United States.",
|
||||
explanation: "Some countries like 'the United States' require 'the'",
|
||||
grammarFocus: "articles-usage"
|
||||
}
|
||||
],
|
||||
|
||||
// === COMPREHENSION QUESTIONS ===
|
||||
comprehension: [
|
||||
{
|
||||
question: "Where does the writer like to sit?",
|
||||
options: [
|
||||
"In the park",
|
||||
"By the Wayle river",
|
||||
"On the boat",
|
||||
"In his garden"
|
||||
],
|
||||
correct: "By the Wayle river",
|
||||
explanation: "The writer says 'I like sitting by the Wayle on fine afternoons'"
|
||||
},
|
||||
{
|
||||
question: "What were the children doing?",
|
||||
options: [
|
||||
"Swimming in the river",
|
||||
"Playing games on the bank",
|
||||
"Rowing on the river",
|
||||
"Sitting by the water"
|
||||
],
|
||||
correct: "Playing games on the bank",
|
||||
explanation: "The text states 'Some children were playing games on the bank'"
|
||||
},
|
||||
{
|
||||
question: "Why didn't the man in the boat hear the people calling?",
|
||||
options: [
|
||||
"He was deaf",
|
||||
"The text doesn't say why",
|
||||
"He was sleeping",
|
||||
"The water was too loud"
|
||||
],
|
||||
correct: "The text doesn't say why",
|
||||
explanation: "The text only says 'he did not hear them' but doesn't give a reason"
|
||||
},
|
||||
{
|
||||
question: "What happened after the ball hit the man?",
|
||||
options: [
|
||||
"He fell into the water",
|
||||
"He got angry with the children",
|
||||
"The children ran away",
|
||||
"He threw the ball at the children"
|
||||
],
|
||||
correct: "The children ran away",
|
||||
explanation: "The text says 'there weren't any in sight: they had all run away!'"
|
||||
},
|
||||
{
|
||||
question: "How did the story end?",
|
||||
options: [
|
||||
"The man was angry",
|
||||
"The man called police",
|
||||
"The man laughed and threw the ball back",
|
||||
"The children came back"
|
||||
],
|
||||
correct: "The man laughed and threw the ball back",
|
||||
explanation: "The text ends with 'The man laughed... threw the ball back to the bank'"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// CONTENT STRUCTURE SUMMARY - FOR AI REFERENCE
|
||||
// ============================================================================
|
||||
//
|
||||
// This module represents INTERMEDIATE level English learning content for Chinese speakers:
|
||||
// - Focus on past tense narrative and descriptive language
|
||||
// - Grammar emphasis on articles (the/a/an) and quantifiers (some/any)
|
||||
// - Rich vocabulary around outdoor activities and everyday situations
|
||||
// - Complex sentence structures with subordinate clauses
|
||||
// - Cultural context of British/Western recreational activities
|
||||
//
|
||||
// LEARNING OBJECTIVES:
|
||||
// - Master use of definite/indefinite articles with different noun types
|
||||
// - Understand some/any usage in different sentence types
|
||||
// - Practice past continuous vs simple past tense
|
||||
// - Build vocabulary around outdoor activities and emotions
|
||||
// - Develop reading comprehension of narrative texts
|
||||
//
|
||||
// DIFFICULTY INDICATORS:
|
||||
// - Longer sentences with multiple clauses
|
||||
// - Past perfect and continuous tenses
|
||||
// - Abstract concepts (realization, consequences)
|
||||
// - Cultural references (British countryside, recreational activities)
|
||||
// - Advanced vocabulary (struck, realized, rowing, etc.)
|
||||
//
|
||||
// ============================================================================
|
||||
479
src/content/SBS-level-1.js
Normal file
479
src/content/SBS-level-1.js
Normal file
@ -0,0 +1,479 @@
|
||||
// === ENGLISH LEARNING MODULE ===
|
||||
// Complete English learning module with Chinese translation and pronunciation
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.SBSLevel1 = {
|
||||
id: "sbs-level-1",
|
||||
name: "SBS-1",
|
||||
description: "English introduction lessons with Chinese translation and pronunciation",
|
||||
difficulty: "beginner",
|
||||
language: "en-US",
|
||||
userLanguage: "zh-CN",
|
||||
totalWords: 150,
|
||||
|
||||
// === GRAMMAR LESSONS SYSTEM ===
|
||||
grammar: {
|
||||
"to-be-verb": {
|
||||
title: "The Verb 'To Be' - 动词Be",
|
||||
explanation: "The verb 'be' is one of the most important verbs in English, used to describe states, identity, and location.",
|
||||
rules: [
|
||||
"I am - 我是 (first person singular)",
|
||||
"You are - 你是/你们是 (second person)",
|
||||
"He/She/It is - 他/她/它是 (third person singular)",
|
||||
"We are - 我们是 (first person plural)",
|
||||
"They are - 他们是 (third person plural)"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "My name is Maria.",
|
||||
chinese: "我的名字是玛丽亚。",
|
||||
explanation: "Use 'is' because 'name' is third person singular",
|
||||
pronunciation: "/maɪ neɪm ɪz məˈriːə/"
|
||||
},
|
||||
{
|
||||
english: "I am from Mexico City.",
|
||||
chinese: "我来自墨西哥城。",
|
||||
explanation: "Use 'am' because the subject is 'I'",
|
||||
pronunciation: "/aɪ æm frʌm ˈmeksɪkoʊ ˈsɪti/"
|
||||
},
|
||||
{
|
||||
english: "Where are you from?",
|
||||
chinese: "你来自哪里?",
|
||||
explanation: "Use 'are' because the subject is 'you'",
|
||||
pronunciation: "/wer ɑr ju frʌm/"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "My address _____ 235 Main Street.",
|
||||
options: ["am", "is", "are"],
|
||||
correct: "is",
|
||||
explanation: "Use 'is' because 'address' is third person singular"
|
||||
},
|
||||
{
|
||||
type: "translation",
|
||||
english: "What's your phone number?",
|
||||
chinese: "你的电话号码是多少?",
|
||||
focus: "Contraction What's = What is"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"contractions": {
|
||||
title: "Contractions - 缩写形式",
|
||||
explanation: "English often uses contractions to make conversation more natural and fluent.",
|
||||
rules: [
|
||||
"What's = What is - 什么是",
|
||||
"I'm = I am - 我是",
|
||||
"You're = You are - 你是",
|
||||
"He's/She's/It's = He/She/It is - 他/她/它是"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "What's your name?",
|
||||
chinese: "你叫什么名字?",
|
||||
explanation: "What's is the contraction of What is",
|
||||
pronunciation: "/wʌts jʊr neɪm/"
|
||||
},
|
||||
{
|
||||
english: "I'm Nancy Lee.",
|
||||
chinese: "我是南希·李。",
|
||||
explanation: "I'm is the contraction of I am",
|
||||
pronunciation: "/aɪm ˈnænsi li/"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "contraction_match",
|
||||
full_form: "What is your address?",
|
||||
contracted: "What's your address?",
|
||||
chinese: "你的地址是什么?"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"personal-information": {
|
||||
title: "Personal Information - 个人信息",
|
||||
explanation: "Learn how to ask for and provide basic personal information in English.",
|
||||
rules: [
|
||||
"Name - 姓名: What's your name? My name is...",
|
||||
"Address - 地址: What's your address? My address is...",
|
||||
"Phone - 电话: What's your phone number? My phone number is...",
|
||||
"Origin - 来源: Where are you from? I'm from..."
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "My name is David Carter.",
|
||||
chinese: "我的名字是大卫·卡特。",
|
||||
explanation: "Standard expression for introducing name",
|
||||
pronunciation: "/maɪ neɪm ɪz ˈdeɪvɪd ˈkɑrtər/"
|
||||
},
|
||||
{
|
||||
english: "I'm from San Francisco.",
|
||||
chinese: "我来自旧金山。",
|
||||
explanation: "Expression for stating origin",
|
||||
pronunciation: "/aɪm frʌm sæn frænˈsɪskoʊ/"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "dialogue_completion",
|
||||
prompt: "A: What's your name? B: _____",
|
||||
answer: "My name is [your name].",
|
||||
chinese: "A: 你叫什么名字? B: 我的名字是[你的名字]。"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"meeting-people": {
|
||||
title: "Meeting People - 与人见面",
|
||||
explanation: "Common phrases and expressions used when meeting new people.",
|
||||
rules: [
|
||||
"Hello - 你好 (formal greeting)",
|
||||
"Hi - 嗨 (informal greeting)",
|
||||
"Nice to meet you - 很高兴认识你",
|
||||
"Nice to meet you, too - 我也很高兴认识你"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
english: "Hello. My name is Peter Lewis.",
|
||||
chinese: "你好。我的名字是彼得·刘易斯。",
|
||||
explanation: "Formal introduction",
|
||||
pronunciation: "/həˈloʊ maɪ neɪm ɪz ˈpitər ˈluɪs/"
|
||||
},
|
||||
{
|
||||
english: "Hi. I'm Nancy Lee. Nice to meet you.",
|
||||
chinese: "嗨。我是南希·李。很高兴认识你。",
|
||||
explanation: "Informal introduction with greeting",
|
||||
pronunciation: "/haɪ aɪm ˈnænsi li naɪs tu mit ju/"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "role_play",
|
||||
scenario: "Meeting someone new",
|
||||
dialogue: "A: Hello. B: Hi. A: What's your name? B: My name is ____."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
vocabulary: {
|
||||
"name": {
|
||||
"user_language": "名字",
|
||||
"type": "noun",
|
||||
"pronunciation": "/neɪm/"
|
||||
},
|
||||
"address": {
|
||||
"user_language": "地址",
|
||||
"type": "noun",
|
||||
"pronunciation": "/əˈdres/"
|
||||
},
|
||||
"phone number": {
|
||||
"user_language": "电话号码",
|
||||
"type": "noun",
|
||||
"pronunciation": "/foʊn ˈnʌmbər/"
|
||||
},
|
||||
"telephone number": {
|
||||
"user_language": "电话号码",
|
||||
"type": "noun",
|
||||
"pronunciation": "/ˈteləfoʊn ˈnʌmbər/"
|
||||
},
|
||||
"apartment number": {
|
||||
"user_language": "公寓号码",
|
||||
"type": "noun",
|
||||
"pronunciation": "/əˈpɑrtmənt ˈnʌmbər/"
|
||||
},
|
||||
"e-mail address": {
|
||||
"user_language": "电子邮件地址",
|
||||
"type": "noun",
|
||||
"pronunciation": "/ˈiːmeɪl əˈdres/"
|
||||
},
|
||||
"first name": {
|
||||
"user_language": "名",
|
||||
"type": "noun",
|
||||
"pronunciation": "/fɜrst neɪm/"
|
||||
},
|
||||
"last name": {
|
||||
"user_language": "姓",
|
||||
"type": "noun",
|
||||
"pronunciation": "/læst neɪm/"
|
||||
},
|
||||
"hello": {
|
||||
"user_language": "你好",
|
||||
"type": "interjection",
|
||||
"pronunciation": "/həˈloʊ/"
|
||||
},
|
||||
"hi": {
|
||||
"user_language": "嗨",
|
||||
"type": "interjection",
|
||||
"pronunciation": "/haɪ/"
|
||||
},
|
||||
"nice to meet you": {
|
||||
"user_language": "很高兴认识你",
|
||||
"type": "phrase",
|
||||
"pronunciation": "/naɪs tu mit ju/"
|
||||
},
|
||||
"where": {
|
||||
"user_language": "哪里",
|
||||
"type": "adverb",
|
||||
"pronunciation": "/wer/"
|
||||
},
|
||||
"from": {
|
||||
"user_language": "来自",
|
||||
"type": "preposition",
|
||||
"pronunciation": "/frʌm/"
|
||||
},
|
||||
"alphabet": {
|
||||
"user_language": "字母表",
|
||||
"type": "noun",
|
||||
"pronunciation": "/ˈælfəbet/"
|
||||
},
|
||||
"numbers": {
|
||||
"user_language": "数字",
|
||||
"type": "noun",
|
||||
"pronunciation": "/ˈnʌmbərz/"
|
||||
}
|
||||
},
|
||||
|
||||
story: {
|
||||
title: "To Be: Introduction - 动词Be的介绍",
|
||||
totalSentences: 50,
|
||||
chapters: [
|
||||
{
|
||||
title: "Chapter 1: Vocabulary Preview - 第一章:词汇预览",
|
||||
sentences: [
|
||||
{
|
||||
id: 1,
|
||||
original: "Learn the alphabet Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz",
|
||||
translation: "学习字母表 Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz",
|
||||
words: [
|
||||
{word: "Learn", translation: "学习", type: "verb", pronunciation: "/lɜrn/"},
|
||||
{word: "alphabet", translation: "字母表", type: "noun", pronunciation: "/ˈælfəbet/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
original: "Practice numbers 0 1 2 3 4 5 6 7 8 9 10",
|
||||
translation: "练习数字 0 1 2 3 4 5 6 7 8 9 10",
|
||||
words: [
|
||||
{word: "Practice", translation: "练习", type: "verb", pronunciation: "/ˈpræktɪs/"},
|
||||
{word: "numbers", translation: "数字", type: "noun", pronunciation: "/ˈnʌmbərz/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
original: "This is Maria's name tag.",
|
||||
translation: "这是玛丽亚的姓名牌。",
|
||||
words: [
|
||||
{word: "This", translation: "这", type: "pronoun", pronunciation: "/ðɪs/"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"},
|
||||
{word: "Maria's", translation: "玛丽亚的", type: "possessive", pronunciation: "/məˈriːəz/"},
|
||||
{word: "name", translation: "姓名", type: "noun", pronunciation: "/neɪm/"},
|
||||
{word: "tag", translation: "牌", type: "noun", pronunciation: "/tæg/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
original: "235 Main Street is an address.",
|
||||
translation: "主街235号是一个地址。",
|
||||
words: [
|
||||
{word: "235", translation: "235", type: "number", pronunciation: "/tu ˈθɜrti faɪv/"},
|
||||
{word: "Main", translation: "主要的", type: "adjective", pronunciation: "/meɪn/"},
|
||||
{word: "Street", translation: "街", type: "noun", pronunciation: "/strit/"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"},
|
||||
{word: "an", translation: "一个", type: "article", pronunciation: "/æn/"},
|
||||
{word: "address", translation: "地址", type: "noun", pronunciation: "/əˈdres/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
original: "741-8906 is a telephone number.",
|
||||
translation: "741-8906是一个电话号码。",
|
||||
words: [
|
||||
{word: "741-8906", translation: "741-8906", type: "number", pronunciation: "/ˈsevən fɔr wʌn eɪt naɪn oʊ sɪks/"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"},
|
||||
{word: "a", translation: "一个", type: "article", pronunciation: "/ə/"},
|
||||
{word: "telephone", translation: "电话", type: "noun", pronunciation: "/ˈteləfoʊn/"},
|
||||
{word: "number", translation: "号码", type: "noun", pronunciation: "/ˈnʌmbər/"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Chapter 2: What's Your Name? - 第二章:你叫什么名字?",
|
||||
sentences: [
|
||||
{
|
||||
id: 6,
|
||||
original: "What's your name?",
|
||||
translation: "你叫什么名字?",
|
||||
words: [
|
||||
{word: "What's", translation: "什么是", type: "contraction", pronunciation: "/wʌts/"},
|
||||
{word: "your", translation: "你的", type: "possessive", pronunciation: "/jʊr/"},
|
||||
{word: "name", translation: "名字", type: "noun", pronunciation: "/neɪm/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
original: "My name is Maria.",
|
||||
translation: "我的名字是玛丽亚。",
|
||||
words: [
|
||||
{word: "My", translation: "我的", type: "possessive", pronunciation: "/maɪ/"},
|
||||
{word: "name", translation: "名字", type: "noun", pronunciation: "/neɪm/"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"},
|
||||
{word: "Maria", translation: "玛丽亚", type: "name", pronunciation: "/məˈriːə/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
original: "What's your address?",
|
||||
translation: "你的地址是什么?",
|
||||
words: [
|
||||
{word: "What's", translation: "什么是", type: "contraction", pronunciation: "/wʌts/"},
|
||||
{word: "your", translation: "你的", type: "possessive", pronunciation: "/jʊr/"},
|
||||
{word: "address", translation: "地址", type: "noun", pronunciation: "/əˈdres/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
original: "My address is 235 Main Street.",
|
||||
translation: "我的地址是主街235号。",
|
||||
words: [
|
||||
{word: "My", translation: "我的", type: "possessive", pronunciation: "/maɪ/"},
|
||||
{word: "address", translation: "地址", type: "noun", pronunciation: "/əˈdres/"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"},
|
||||
{word: "235", translation: "235", type: "number", pronunciation: "/tu ˈθɜrti faɪv/"},
|
||||
{word: "Main", translation: "主要的", type: "adjective", pronunciation: "/meɪn/"},
|
||||
{word: "Street", translation: "街", type: "noun", pronunciation: "/strit/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
original: "I'm from Mexico City.",
|
||||
translation: "我来自墨西哥城。",
|
||||
words: [
|
||||
{word: "I'm", translation: "我是", type: "contraction", pronunciation: "/aɪm/"},
|
||||
{word: "from", translation: "来自", type: "preposition", pronunciation: "/frʌm/"},
|
||||
{word: "Mexico", translation: "墨西哥", type: "place", pronunciation: "/ˈmeksɪkoʊ/"},
|
||||
{word: "City", translation: "城", type: "noun", pronunciation: "/ˈsɪti/"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Chapter 3: Meeting People - 第三章:与人见面",
|
||||
sentences: [
|
||||
{
|
||||
id: 11,
|
||||
original: "Hello. My name is Peter Lewis.",
|
||||
translation: "你好。我的名字是彼得·刘易斯。",
|
||||
words: [
|
||||
{word: "Hello", translation: "你好", type: "interjection", pronunciation: "/həˈloʊ/"},
|
||||
{word: "My", translation: "我的", type: "possessive", pronunciation: "/maɪ/"},
|
||||
{word: "name", translation: "名字", type: "noun", pronunciation: "/neɪm/"},
|
||||
{word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"},
|
||||
{word: "Peter", translation: "彼得", type: "name", pronunciation: "/ˈpitər/"},
|
||||
{word: "Lewis", translation: "刘易斯", type: "name", pronunciation: "/ˈluɪs/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
original: "Hi. I'm Nancy Lee. Nice to meet you.",
|
||||
translation: "嗨。我是南希·李。很高兴认识你。",
|
||||
words: [
|
||||
{word: "Hi", translation: "嗨", type: "interjection", pronunciation: "/haɪ/"},
|
||||
{word: "I'm", translation: "我是", type: "contraction", pronunciation: "/aɪm/"},
|
||||
{word: "Nancy", translation: "南希", type: "name", pronunciation: "/ˈnænsi/"},
|
||||
{word: "Lee", translation: "李", type: "name", pronunciation: "/li/"},
|
||||
{word: "Nice", translation: "很好的", type: "adjective", pronunciation: "/naɪs/"},
|
||||
{word: "to", translation: "到", type: "preposition", pronunciation: "/tu/"},
|
||||
{word: "meet", translation: "遇见", type: "verb", pronunciation: "/mit/"},
|
||||
{word: "you", translation: "你", type: "pronoun", pronunciation: "/ju/"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
original: "Nice to meet you, too.",
|
||||
translation: "我也很高兴认识你。",
|
||||
words: [
|
||||
{word: "Nice", translation: "很好的", type: "adjective", pronunciation: "/naɪs/"},
|
||||
{word: "to", translation: "到", type: "preposition", pronunciation: "/tu/"},
|
||||
{word: "meet", translation: "遇见", type: "verb", pronunciation: "/mit/"},
|
||||
{word: "you", translation: "你", type: "pronoun", pronunciation: "/ju/"},
|
||||
{word: "too", translation: "也", type: "adverb", pronunciation: "/tu/"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// === GRAMMAR-BASED FILL IN THE BLANKS ===
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "My name _____ David.",
|
||||
options: ["am", "is", "are"],
|
||||
correctAnswer: "is",
|
||||
explanation: "Use 'is' because 'name' is third person singular",
|
||||
grammarFocus: "to-be-verb"
|
||||
},
|
||||
{
|
||||
sentence: "I _____ from China.",
|
||||
options: ["am", "is", "are"],
|
||||
correctAnswer: "am",
|
||||
explanation: "Use 'am' because the subject is 'I'",
|
||||
grammarFocus: "to-be-verb"
|
||||
},
|
||||
{
|
||||
sentence: "_____ your phone number?",
|
||||
options: ["What", "What's", "Where"],
|
||||
correctAnswer: "What's",
|
||||
explanation: "What's = What is, used to ask for phone number",
|
||||
grammarFocus: "contractions"
|
||||
},
|
||||
{
|
||||
sentence: "Where _____ you from?",
|
||||
options: ["am", "is", "are"],
|
||||
correctAnswer: "are",
|
||||
explanation: "Use 'are' because the subject is 'you'",
|
||||
grammarFocus: "to-be-verb"
|
||||
},
|
||||
{
|
||||
sentence: "_____ to meet you.",
|
||||
options: ["Nice", "Good", "Fine"],
|
||||
correctAnswer: "Nice",
|
||||
explanation: "Standard expression for meeting people",
|
||||
grammarFocus: "meeting-people"
|
||||
}
|
||||
],
|
||||
|
||||
// === GRAMMAR CORRECTION EXERCISES ===
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "My name are John.",
|
||||
correct: "My name is John.",
|
||||
explanation: "'Name' is third person singular, so use 'is'",
|
||||
grammarFocus: "to-be-verb"
|
||||
},
|
||||
{
|
||||
incorrect: "Where you are from?",
|
||||
correct: "Where are you from?",
|
||||
explanation: "In questions, the be verb comes before the subject",
|
||||
grammarFocus: "to-be-verb"
|
||||
},
|
||||
{
|
||||
incorrect: "What is you name?",
|
||||
correct: "What is your name?",
|
||||
explanation: "Use possessive 'your' not subject pronoun 'you'",
|
||||
grammarFocus: "personal-information"
|
||||
},
|
||||
{
|
||||
incorrect: "I are from Mexico.",
|
||||
correct: "I am from Mexico.",
|
||||
explanation: "Use 'am' with subject 'I'",
|
||||
grammarFocus: "to-be-verb"
|
||||
}
|
||||
]
|
||||
};
|
||||
1252
src/content/WTA1B1-documented.js
Normal file
1252
src/content/WTA1B1-documented.js
Normal file
File diff suppressed because it is too large
Load Diff
1188
src/content/WTA1B1.js
Normal file
1188
src/content/WTA1B1.js
Normal file
File diff suppressed because it is too large
Load Diff
575
src/content/chinese-long-story.js
Normal file
575
src/content/chinese-long-story.js
Normal file
@ -0,0 +1,575 @@
|
||||
// === CHINESE LONG STORY ===
|
||||
// Complete Chinese story with English translation and pinyin pronunciation
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.ChineseLongStory = {
|
||||
name: "The Dragon's Pearl - 龙珠传说",
|
||||
description: "Long story with translation and pronunciation",
|
||||
difficulty: "intermediate",
|
||||
language: "zh-CN",
|
||||
totalWords: 1200,
|
||||
|
||||
// === GRAMMAR LESSONS SYSTEM ===
|
||||
grammar: {
|
||||
"chinese-particles": {
|
||||
title: "Chinese Grammar Particles - 语法助词",
|
||||
explanation: "Chinese particles are essential grammatical markers that show relationships between words and add meaning to sentences.",
|
||||
rules: [
|
||||
"的 (de) - Possessive marker and adjective connector",
|
||||
"在 (zài) - Location and time marker 'at/in/on'",
|
||||
"里 (lǐ) - Inside/within location marker",
|
||||
"中 (zhōng) - In/among/middle position marker"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
chinese: "老人的故事",
|
||||
english: "the old man's story",
|
||||
explanation: "的 shows possession - 'old man's'",
|
||||
pronunciation: "lǎo rén de gù shì"
|
||||
},
|
||||
{
|
||||
chinese: "在山里",
|
||||
english: "in the mountains",
|
||||
explanation: "在...里 shows location 'in/inside'",
|
||||
pronunciation: "zài shān lǐ"
|
||||
},
|
||||
{
|
||||
chinese: "村庄中的龙",
|
||||
english: "the dragon in the village",
|
||||
explanation: "中 shows position 'in/among'",
|
||||
pronunciation: "cūn zhuāng zhōng de lóng"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "fill_blank",
|
||||
sentence: "这是小明___书包",
|
||||
options: ["的", "在", "里", "中"],
|
||||
correct: "的",
|
||||
explanation: "Use 的 for possession - 'Xiaoming's backpack'"
|
||||
},
|
||||
{
|
||||
type: "translation",
|
||||
chinese: "龙在水里",
|
||||
english: "The dragon is in the water",
|
||||
focus: "Location marker 在...里"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"chinese-word-order": {
|
||||
title: "Chinese Word Order - 中文语序",
|
||||
explanation: "Chinese follows Subject-Verb-Object order like English, but with important differences for time, place, and manner.",
|
||||
rules: [
|
||||
"Basic pattern: Subject + Time + Place + Verb + Object",
|
||||
"Time comes before place: '昨天在家' (yesterday at home)",
|
||||
"Manner often comes before verb: '慢慢地走' (slowly walk)",
|
||||
"Place words use specific markers: 在 (at), 里 (in), 上 (on)"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
chinese: "老人昨天在村庄里讲故事",
|
||||
english: "The old man told stories in the village yesterday",
|
||||
breakdown: "老人(S) + 昨天(Time) + 在村庄里(Place) + 讲(V) + 故事(O)",
|
||||
pronunciation: "lǎo rén zuó tiān zài cūn zhuāng lǐ jiǎng gù shì"
|
||||
},
|
||||
{
|
||||
chinese: "龙慢慢地飞向山顶",
|
||||
english: "The dragon slowly flew toward the mountain peak",
|
||||
breakdown: "龙(S) + 慢慢地(Manner) + 飞向(V) + 山顶(O)",
|
||||
pronunciation: "lóng màn màn de fēi xiàng shān dǐng"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "word_order",
|
||||
scrambled: ["在", "昨天", "老人", "家里", "休息"],
|
||||
correct: ["老人", "昨天", "在", "家里", "休息"],
|
||||
english: "The old man rested at home yesterday"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"measure-words": {
|
||||
title: "Chinese Measure Words - 量词",
|
||||
explanation: "Chinese uses specific measure words (classifiers) between numbers and nouns, similar to 'a piece of paper' in English.",
|
||||
rules: [
|
||||
"Pattern: Number + Measure Word + Noun",
|
||||
"个 (gè) - Most common, used for people and general objects",
|
||||
"条 (tiáo) - For long, thin things like dragons, rivers, roads",
|
||||
"座 (zuò) - For mountains, buildings, bridges",
|
||||
"本 (běn) - For books, magazines"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
chinese: "一条龙",
|
||||
english: "one dragon",
|
||||
explanation: "条 is used for long creatures like dragons",
|
||||
pronunciation: "yì tiáo lóng"
|
||||
},
|
||||
{
|
||||
chinese: "三座山",
|
||||
english: "three mountains",
|
||||
explanation: "座 is used for large structures like mountains",
|
||||
pronunciation: "sān zuò shān"
|
||||
},
|
||||
{
|
||||
chinese: "两个老人",
|
||||
english: "two old people",
|
||||
explanation: "个 is the general classifier for people",
|
||||
pronunciation: "liǎng gè lǎo rén"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "classifier_choice",
|
||||
chinese: "五___珠子",
|
||||
options: ["个", "条", "座", "本"],
|
||||
correct: "个",
|
||||
explanation: "珠子 (pearls) use 个 as the general classifier"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"chinese-tones": {
|
||||
title: "Chinese Tones - 声调",
|
||||
explanation: "Mandarin Chinese has 4 main tones that change word meaning. Tone is crucial for communication.",
|
||||
rules: [
|
||||
"First tone (ā) - High, flat tone",
|
||||
"Second tone (á) - Rising tone, like asking a question",
|
||||
"Third tone (ǎ) - Falling then rising, dip tone",
|
||||
"Fourth tone (à) - Sharp falling tone",
|
||||
"Neutral tone (a) - Light, quick, no specific pitch"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
chinese: "妈 (mā) - mother",
|
||||
tone: "First tone - high and flat",
|
||||
pronunciation: "mā"
|
||||
},
|
||||
{
|
||||
chinese: "麻 (má) - hemp/numb",
|
||||
tone: "Second tone - rising",
|
||||
pronunciation: "má"
|
||||
},
|
||||
{
|
||||
chinese: "马 (mǎ) - horse",
|
||||
tone: "Third tone - dip",
|
||||
pronunciation: "mǎ"
|
||||
},
|
||||
{
|
||||
chinese: "骂 (mà) - to scold",
|
||||
tone: "Fourth tone - falling",
|
||||
pronunciation: "mà"
|
||||
}
|
||||
],
|
||||
exercises: [
|
||||
{
|
||||
type: "tone_identification",
|
||||
word: "山",
|
||||
pronunciation: "shān",
|
||||
tone_options: ["First", "Second", "Third", "Fourth"],
|
||||
correct: "First",
|
||||
explanation: "山 (shān) uses first tone - high and flat"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
vocabulary: {
|
||||
"龙": {
|
||||
"user_language": "dragon",
|
||||
"type": "noun",
|
||||
"pronunciation": "lóng"
|
||||
},
|
||||
"珠": {
|
||||
"user_language": "pearl",
|
||||
"type": "noun",
|
||||
"pronunciation": "zhū"
|
||||
},
|
||||
"传说": {
|
||||
"user_language": "legend",
|
||||
"type": "noun",
|
||||
"pronunciation": "chuán shuō"
|
||||
},
|
||||
"故事": {
|
||||
"user_language": "story",
|
||||
"type": "noun",
|
||||
"pronunciation": "gù shì"
|
||||
},
|
||||
"山": {
|
||||
"user_language": "mountain",
|
||||
"type": "noun",
|
||||
"pronunciation": "shān"
|
||||
},
|
||||
"水": {
|
||||
"user_language": "water",
|
||||
"type": "noun",
|
||||
"pronunciation": "shuǐ"
|
||||
},
|
||||
"村庄": {
|
||||
"user_language": "village",
|
||||
"type": "noun",
|
||||
"pronunciation": "cūn zhuāng"
|
||||
},
|
||||
"老人": {
|
||||
"user_language": "old man",
|
||||
"type": "noun",
|
||||
"pronunciation": "lǎo rén"
|
||||
},
|
||||
"年轻": {
|
||||
"user_language": "young",
|
||||
"type": "adjective",
|
||||
"pronunciation": "nián qīng"
|
||||
},
|
||||
"美丽": {
|
||||
"user_language": "beautiful",
|
||||
"type": "adjective",
|
||||
"pronunciation": "měi lì"
|
||||
}
|
||||
},
|
||||
|
||||
story: {
|
||||
title: "The Dragon's Pearl - 龙珠传说",
|
||||
totalSentences: 150,
|
||||
chapters: [
|
||||
{
|
||||
title: "第一章:古老的传说 (Chapter 1: The Ancient Legend)",
|
||||
sentences: [
|
||||
{
|
||||
id: 1,
|
||||
original: "很久很久以前,在中国的一个偏远山村里,住着一个年轻的渔夫叫李明。",
|
||||
translation: "Long, long ago, in a remote mountain village in China, there lived a young fisherman named Li Ming.",
|
||||
words: [
|
||||
{word: "很久", translation: "long", type: "adverb", pronunciation: "hěn jiǔ"},
|
||||
{word: "很久", translation: "long", type: "adverb", pronunciation: "hěn jiǔ"},
|
||||
{word: "以前", translation: "ago", type: "noun", pronunciation: "yǐ qián"},
|
||||
{word: "在", translation: "in", type: "preposition", pronunciation: "zài"},
|
||||
{word: "中国", translation: "China", type: "noun", pronunciation: "zhōng guó"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "一个", translation: "a", type: "number", pronunciation: "yī gè"},
|
||||
{word: "偏远", translation: "remote", type: "adjective", pronunciation: "piān yuǎn"},
|
||||
{word: "山村", translation: "mountain village", type: "noun", pronunciation: "shān cūn"},
|
||||
{word: "里", translation: "in", type: "preposition", pronunciation: "lǐ"},
|
||||
{word: "住着", translation: "lived", type: "verb", pronunciation: "zhù zhe"},
|
||||
{word: "一个", translation: "a", type: "number", pronunciation: "yī gè"},
|
||||
{word: "年轻", translation: "young", type: "adjective", pronunciation: "nián qīng"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "渔夫", translation: "fisherman", type: "noun", pronunciation: "yú fū"},
|
||||
{word: "叫", translation: "named", type: "verb", pronunciation: "jiào"},
|
||||
{word: "李明", translation: "Li Ming", type: "noun", pronunciation: "lǐ míng"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
original: "李明每天都在附近的河里打鱼,生活虽然简单,但他很满足。",
|
||||
translation: "Li Ming fished in the nearby river every day, and although his life was simple, he was very content.",
|
||||
words: [
|
||||
{word: "李明", translation: "Li Ming", type: "noun", pronunciation: "lǐ míng"},
|
||||
{word: "每天", translation: "every day", type: "adverb", pronunciation: "měi tiān"},
|
||||
{word: "都", translation: "all", type: "adverb", pronunciation: "dōu"},
|
||||
{word: "在", translation: "in", type: "preposition", pronunciation: "zài"},
|
||||
{word: "附近", translation: "nearby", type: "adjective", pronunciation: "fù jìn"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "河里", translation: "river", type: "noun", pronunciation: "hé lǐ"},
|
||||
{word: "打鱼", translation: "fish", type: "verb", pronunciation: "dǎ yú"},
|
||||
{word: "生活", translation: "life", type: "noun", pronunciation: "shēng huó"},
|
||||
{word: "虽然", translation: "although", type: "conjunction", pronunciation: "suī rán"},
|
||||
{word: "简单", translation: "simple", type: "adjective", pronunciation: "jiǎn dān"},
|
||||
{word: "但", translation: "but", type: "conjunction", pronunciation: "dàn"},
|
||||
{word: "他", translation: "he", type: "pronoun", pronunciation: "tā"},
|
||||
{word: "很", translation: "very", type: "adverb", pronunciation: "hěn"},
|
||||
{word: "满足", translation: "content", type: "adjective", pronunciation: "mǎn zú"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
original: "村里的老人们经常讲述一个关于神奇龙珠的古老传说。",
|
||||
translation: "The elderly people in the village often told an ancient legend about a magical dragon pearl.",
|
||||
words: [
|
||||
{word: "村里", translation: "village", type: "noun", pronunciation: "cūn lǐ"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "老人们", translation: "elderly people", type: "noun", pronunciation: "lǎo rén men"},
|
||||
{word: "经常", translation: "often", type: "adverb", pronunciation: "jīng cháng"},
|
||||
{word: "讲述", translation: "tell", type: "verb", pronunciation: "jiǎng shù"},
|
||||
{word: "一个", translation: "a", type: "number", pronunciation: "yī gè"},
|
||||
{word: "关于", translation: "about", type: "preposition", pronunciation: "guān yú"},
|
||||
{word: "神奇", translation: "magical", type: "adjective", pronunciation: "shén qí"},
|
||||
{word: "龙珠", translation: "dragon pearl", type: "noun", pronunciation: "lóng zhū"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "古老", translation: "ancient", type: "adjective", pronunciation: "gǔ lǎo"},
|
||||
{word: "传说", translation: "legend", type: "noun", pronunciation: "chuán shuō"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
original: "传说中说,龙珠藏在深山的秘密洞穴里,能够实现持有者的任何愿望。",
|
||||
translation: "The legend said that the dragon pearl was hidden in a secret cave in the deep mountains and could fulfill any wish of its holder.",
|
||||
words: [
|
||||
{word: "传说", translation: "legend", type: "noun", pronunciation: "chuán shuō"},
|
||||
{word: "中", translation: "in", type: "preposition", pronunciation: "zhōng"},
|
||||
{word: "说", translation: "said", type: "verb", pronunciation: "shuō"},
|
||||
{word: "龙珠", translation: "dragon pearl", type: "noun", pronunciation: "lóng zhū"},
|
||||
{word: "藏在", translation: "hidden in", type: "verb", pronunciation: "cáng zài"},
|
||||
{word: "深山", translation: "deep mountains", type: "noun", pronunciation: "shēn shān"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "秘密", translation: "secret", type: "adjective", pronunciation: "mì mì"},
|
||||
{word: "洞穴", translation: "cave", type: "noun", pronunciation: "dòng xué"},
|
||||
{word: "里", translation: "in", type: "preposition", pronunciation: "lǐ"},
|
||||
{word: "能够", translation: "could", type: "verb", pronunciation: "néng gòu"},
|
||||
{word: "实现", translation: "fulfill", type: "verb", pronunciation: "shí xiàn"},
|
||||
{word: "持有者", translation: "holder", type: "noun", pronunciation: "chí yǒu zhě"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "任何", translation: "any", type: "adjective", pronunciation: "rèn hé"},
|
||||
{word: "愿望", translation: "wish", type: "noun", pronunciation: "yuàn wàng"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
original: "但是,想要找到龙珠并不容易,因为山中充满了危险。",
|
||||
translation: "However, finding the dragon pearl was not easy, because the mountains were full of danger.",
|
||||
words: [
|
||||
{word: "但是", translation: "however", type: "conjunction", pronunciation: "dàn shì"},
|
||||
{word: "想要", translation: "want to", type: "verb", pronunciation: "xiǎng yào"},
|
||||
{word: "找到", translation: "find", type: "verb", pronunciation: "zhǎo dào"},
|
||||
{word: "龙珠", translation: "dragon pearl", type: "noun", pronunciation: "lóng zhū"},
|
||||
{word: "并不", translation: "not", type: "adverb", pronunciation: "bìng bù"},
|
||||
{word: "容易", translation: "easy", type: "adjective", pronunciation: "róng yì"},
|
||||
{word: "因为", translation: "because", type: "conjunction", pronunciation: "yīn wèi"},
|
||||
{word: "山中", translation: "mountains", type: "noun", pronunciation: "shān zhōng"},
|
||||
{word: "充满了", translation: "full of", type: "verb", pronunciation: "chōng mǎn le"},
|
||||
{word: "危险", translation: "danger", type: "noun", pronunciation: "wēi xiǎn"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "第二章:神秘的梦境 (Chapter 2: The Mysterious Dream)",
|
||||
sentences: [
|
||||
{
|
||||
id: 6,
|
||||
original: "一天晚上,李明做了一个奇怪的梦。",
|
||||
translation: "One night, Li Ming had a strange dream.",
|
||||
words: [
|
||||
{word: "一天", translation: "one day", type: "noun", pronunciation: "yī tiān"},
|
||||
{word: "晚上", translation: "night", type: "noun", pronunciation: "wǎn shàng"},
|
||||
{word: "李明", translation: "Li Ming", type: "noun", pronunciation: "lǐ míng"},
|
||||
{word: "做了", translation: "had", type: "verb", pronunciation: "zuò le"},
|
||||
{word: "一个", translation: "a", type: "number", pronunciation: "yī gè"},
|
||||
{word: "奇怪", translation: "strange", type: "adjective", pronunciation: "qí guài"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "梦", translation: "dream", type: "noun", pronunciation: "mèng"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
original: "梦中,一条巨大的金龙出现在他面前,龙的眼睛像星星一样闪闪发光。",
|
||||
translation: "In the dream, a huge golden dragon appeared before him, its eyes sparkling like stars.",
|
||||
words: [
|
||||
{word: "梦中", translation: "in dream", type: "noun", pronunciation: "mèng zhōng"},
|
||||
{word: "一条", translation: "a", type: "number", pronunciation: "yī tiáo"},
|
||||
{word: "巨大", translation: "huge", type: "adjective", pronunciation: "jù dà"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "金龙", translation: "golden dragon", type: "noun", pronunciation: "jīn lóng"},
|
||||
{word: "出现", translation: "appeared", type: "verb", pronunciation: "chū xiàn"},
|
||||
{word: "在", translation: "at", type: "preposition", pronunciation: "zài"},
|
||||
{word: "他", translation: "him", type: "pronoun", pronunciation: "tā"},
|
||||
{word: "面前", translation: "before", type: "noun", pronunciation: "miàn qián"},
|
||||
{word: "龙", translation: "dragon", type: "noun", pronunciation: "lóng"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "眼睛", translation: "eyes", type: "noun", pronunciation: "yǎn jīng"},
|
||||
{word: "像", translation: "like", type: "verb", pronunciation: "xiàng"},
|
||||
{word: "星星", translation: "stars", type: "noun", pronunciation: "xīng xīng"},
|
||||
{word: "一样", translation: "same as", type: "adverb", pronunciation: "yī yàng"},
|
||||
{word: "闪闪发光", translation: "sparkling", type: "verb", pronunciation: "shǎn shǎn fā guāng"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
original: "金龙对李明说:'勇敢的年轻人,你有纯洁的心,我要给你一个机会。'",
|
||||
translation: "The golden dragon said to Li Ming: 'Brave young man, you have a pure heart, I want to give you a chance.'",
|
||||
words: [
|
||||
{word: "金龙", translation: "golden dragon", type: "noun", pronunciation: "jīn lóng"},
|
||||
{word: "对", translation: "to", type: "preposition", pronunciation: "duì"},
|
||||
{word: "李明", translation: "Li Ming", type: "noun", pronunciation: "lǐ míng"},
|
||||
{word: "说", translation: "said", type: "verb", pronunciation: "shuō"},
|
||||
{word: "勇敢", translation: "brave", type: "adjective", pronunciation: "yǒng gǎn"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "年轻人", translation: "young man", type: "noun", pronunciation: "nián qīng rén"},
|
||||
{word: "你", translation: "you", type: "pronoun", pronunciation: "nǐ"},
|
||||
{word: "有", translation: "have", type: "verb", pronunciation: "yǒu"},
|
||||
{word: "纯洁", translation: "pure", type: "adjective", pronunciation: "chún jié"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "心", translation: "heart", type: "noun", pronunciation: "xīn"},
|
||||
{word: "我", translation: "I", type: "pronoun", pronunciation: "wǒ"},
|
||||
{word: "要", translation: "want", type: "verb", pronunciation: "yào"},
|
||||
{word: "给", translation: "give", type: "verb", pronunciation: "gěi"},
|
||||
{word: "你", translation: "you", type: "pronoun", pronunciation: "nǐ"},
|
||||
{word: "一个", translation: "a", type: "number", pronunciation: "yī gè"},
|
||||
{word: "机会", translation: "chance", type: "noun", pronunciation: "jī huì"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
original: "'明天早上,当太阳升起的时候,跟着河流向北走,你会找到你寻找的东西。'",
|
||||
translation: "'Tomorrow morning, when the sun rises, follow the river northward, and you will find what you seek.'",
|
||||
words: [
|
||||
{word: "明天", translation: "tomorrow", type: "noun", pronunciation: "míng tiān"},
|
||||
{word: "早上", translation: "morning", type: "noun", pronunciation: "zǎo shàng"},
|
||||
{word: "当", translation: "when", type: "conjunction", pronunciation: "dāng"},
|
||||
{word: "太阳", translation: "sun", type: "noun", pronunciation: "tài yáng"},
|
||||
{word: "升起", translation: "rises", type: "verb", pronunciation: "shēng qǐ"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "时候", translation: "time", type: "noun", pronunciation: "shí hòu"},
|
||||
{word: "跟着", translation: "follow", type: "verb", pronunciation: "gēn zhe"},
|
||||
{word: "河流", translation: "river", type: "noun", pronunciation: "hé liú"},
|
||||
{word: "向", translation: "toward", type: "preposition", pronunciation: "xiàng"},
|
||||
{word: "北", translation: "north", type: "noun", pronunciation: "běi"},
|
||||
{word: "走", translation: "walk", type: "verb", pronunciation: "zǒu"},
|
||||
{word: "你", translation: "you", type: "pronoun", pronunciation: "nǐ"},
|
||||
{word: "会", translation: "will", type: "verb", pronunciation: "huì"},
|
||||
{word: "找到", translation: "find", type: "verb", pronunciation: "zhǎo dào"},
|
||||
{word: "你", translation: "you", type: "pronoun", pronunciation: "nǐ"},
|
||||
{word: "寻找", translation: "seek", type: "verb", pronunciation: "xún zhǎo"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "东西", translation: "thing", type: "noun", pronunciation: "dōng xī"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
original: "李明醒来后,发现这个梦非常清晰,好像真的发生过一样。",
|
||||
translation: "After Li Ming woke up, he found that the dream was very clear, as if it had really happened.",
|
||||
words: [
|
||||
{word: "李明", translation: "Li Ming", type: "noun", pronunciation: "lǐ míng"},
|
||||
{word: "醒来", translation: "woke up", type: "verb", pronunciation: "xǐng lái"},
|
||||
{word: "后", translation: "after", type: "preposition", pronunciation: "hòu"},
|
||||
{word: "发现", translation: "found", type: "verb", pronunciation: "fā xiàn"},
|
||||
{word: "这个", translation: "this", type: "pronoun", pronunciation: "zhè gè"},
|
||||
{word: "梦", translation: "dream", type: "noun", pronunciation: "mèng"},
|
||||
{word: "非常", translation: "very", type: "adverb", pronunciation: "fēi cháng"},
|
||||
{word: "清晰", translation: "clear", type: "adjective", pronunciation: "qīng xī"},
|
||||
{word: "好像", translation: "as if", type: "adverb", pronunciation: "hǎo xiàng"},
|
||||
{word: "真的", translation: "really", type: "adverb", pronunciation: "zhēn de"},
|
||||
{word: "发生过", translation: "happened", type: "verb", pronunciation: "fā shēng guò"},
|
||||
{word: "一样", translation: "same", type: "adverb", pronunciation: "yī yàng"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "第三章:神奇的发现 (Chapter 3: The Amazing Discovery)",
|
||||
sentences: [
|
||||
{
|
||||
id: 11,
|
||||
original: "第二天早上,李明决定按照梦中龙的指示去寻找龙珠。",
|
||||
translation: "The next morning, Li Ming decided to follow the dragon's instructions from his dream to search for the dragon pearl.",
|
||||
words: [
|
||||
{word: "第二天", translation: "next day", type: "noun", pronunciation: "dì èr tiān"},
|
||||
{word: "早上", translation: "morning", type: "noun", pronunciation: "zǎo shàng"},
|
||||
{word: "李明", translation: "Li Ming", type: "noun", pronunciation: "lǐ míng"},
|
||||
{word: "决定", translation: "decided", type: "verb", pronunciation: "jué dìng"},
|
||||
{word: "按照", translation: "according to", type: "preposition", pronunciation: "àn zhào"},
|
||||
{word: "梦中", translation: "dream", type: "noun", pronunciation: "mèng zhōng"},
|
||||
{word: "龙", translation: "dragon", type: "noun", pronunciation: "lóng"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "指示", translation: "instructions", type: "noun", pronunciation: "zhǐ shì"},
|
||||
{word: "去", translation: "go", type: "verb", pronunciation: "qù"},
|
||||
{word: "寻找", translation: "search", type: "verb", pronunciation: "xún zhǎo"},
|
||||
{word: "龙珠", translation: "dragon pearl", type: "noun", pronunciation: "lóng zhū"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
original: "他沿着河流向北走了整整一天,最终到达了一座高山的脚下。",
|
||||
translation: "He walked northward along the river for a whole day and finally reached the foot of a high mountain.",
|
||||
words: [
|
||||
{word: "他", translation: "he", type: "pronoun", pronunciation: "tā"},
|
||||
{word: "沿着", translation: "along", type: "preposition", pronunciation: "yán zhe"},
|
||||
{word: "河流", translation: "river", type: "noun", pronunciation: "hé liú"},
|
||||
{word: "向", translation: "toward", type: "preposition", pronunciation: "xiàng"},
|
||||
{word: "北", translation: "north", type: "noun", pronunciation: "běi"},
|
||||
{word: "走了", translation: "walked", type: "verb", pronunciation: "zǒu le"},
|
||||
{word: "整整", translation: "whole", type: "adverb", pronunciation: "zhěng zhěng"},
|
||||
{word: "一天", translation: "one day", type: "noun", pronunciation: "yī tiān"},
|
||||
{word: "最终", translation: "finally", type: "adverb", pronunciation: "zuì zhōng"},
|
||||
{word: "到达了", translation: "reached", type: "verb", pronunciation: "dào dá le"},
|
||||
{word: "一座", translation: "a", type: "number", pronunciation: "yī zuò"},
|
||||
{word: "高山", translation: "high mountain", type: "noun", pronunciation: "gāo shān"},
|
||||
{word: "的", translation: "of", type: "particle", pronunciation: "de"},
|
||||
{word: "脚下", translation: "foot", type: "noun", pronunciation: "jiǎo xià"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// === GRAMMAR-BASED FILL IN THE BLANKS ===
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "这是老人___故事",
|
||||
options: ["的", "在", "里", "中"],
|
||||
correctAnswer: "的",
|
||||
explanation: "Use 的 to show possession - 'the old man's story'",
|
||||
grammarFocus: "chinese-particles"
|
||||
},
|
||||
{
|
||||
sentence: "龙___水里游泳",
|
||||
options: ["的", "在", "里", "中"],
|
||||
correctAnswer: "在",
|
||||
explanation: "Use 在 to show location - 'the dragon is swimming in the water'",
|
||||
grammarFocus: "chinese-particles"
|
||||
},
|
||||
{
|
||||
sentence: "一___龙飞向天空",
|
||||
options: ["个", "条", "座", "本"],
|
||||
correctAnswer: "条",
|
||||
explanation: "Use 条 for long creatures like dragons",
|
||||
grammarFocus: "measure-words"
|
||||
},
|
||||
{
|
||||
sentence: "三___山很高",
|
||||
options: ["个", "条", "座", "本"],
|
||||
correctAnswer: "座",
|
||||
explanation: "Use 座 for large structures like mountains",
|
||||
grammarFocus: "measure-words"
|
||||
},
|
||||
{
|
||||
sentence: "老人昨天___村庄里讲故事",
|
||||
options: ["在", "的", "里", "中"],
|
||||
correctAnswer: "在",
|
||||
explanation: "Word order: Subject + Time + Place (在 + location) + Verb + Object",
|
||||
grammarFocus: "chinese-word-order"
|
||||
},
|
||||
{
|
||||
sentence: "山___读音是第几声?",
|
||||
options: ["第一声", "第二声", "第三声", "第四声"],
|
||||
correctAnswer: "第一声",
|
||||
explanation: "山 (shān) uses first tone - high and flat",
|
||||
grammarFocus: "chinese-tones"
|
||||
}
|
||||
],
|
||||
|
||||
// === GRAMMAR CORRECTION EXERCISES ===
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "龙里水游泳",
|
||||
correct: "龙在水里游泳",
|
||||
explanation: "Need 在 (at/in) before location marker 里",
|
||||
grammarFocus: "chinese-particles"
|
||||
},
|
||||
{
|
||||
incorrect: "老人在村庄昨天讲故事",
|
||||
correct: "老人昨天在村庄里讲故事",
|
||||
explanation: "Time (昨天) must come before place (在村庄里)",
|
||||
grammarFocus: "chinese-word-order"
|
||||
},
|
||||
{
|
||||
incorrect: "五龙飞在天空",
|
||||
correct: "五条龙飞在天空中",
|
||||
explanation: "Need measure word 条 for dragons and location marker 中",
|
||||
grammarFocus: "measure-words"
|
||||
}
|
||||
]
|
||||
};
|
||||
59
src/content/example-minimal.js
Normal file
59
src/content/example-minimal.js
Normal file
@ -0,0 +1,59 @@
|
||||
// Example content module with minimal data (no images, some missing pronunciation)
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
window.ContentModules.ExampleMinimal = {
|
||||
name: "Minimal Vocabulary Test",
|
||||
description: "Test content with missing images and audio",
|
||||
difficulty: "easy",
|
||||
language: "en-US",
|
||||
|
||||
// Vocabulary with mixed availability of features
|
||||
vocabulary: {
|
||||
"hello": {
|
||||
translation: "bonjour",
|
||||
pronunciation: "həˈloʊ",
|
||||
type: "greeting"
|
||||
// No image
|
||||
},
|
||||
"goodbye": {
|
||||
translation: "au revoir",
|
||||
type: "greeting"
|
||||
// No image, no pronunciation
|
||||
},
|
||||
"water": {
|
||||
translation: "eau",
|
||||
pronunciation: "ˈwɔːtər",
|
||||
type: "noun"
|
||||
// No image
|
||||
},
|
||||
"food": {
|
||||
translation: "nourriture",
|
||||
type: "noun"
|
||||
// No image, no pronunciation
|
||||
},
|
||||
"happy": {
|
||||
translation: "heureux",
|
||||
pronunciation: "ˈhæpi",
|
||||
type: "adjective"
|
||||
// No image
|
||||
},
|
||||
"sad": {
|
||||
translation: "triste",
|
||||
type: "adjective"
|
||||
// No image, no pronunciation
|
||||
}
|
||||
},
|
||||
|
||||
// Backward compatibility for other games
|
||||
sentences: [
|
||||
{
|
||||
english: "Hello, how are you?",
|
||||
chinese: "Bonjour, comment allez-vous?",
|
||||
prononciation: "həˈloʊ haʊ ɑr ju"
|
||||
},
|
||||
{
|
||||
english: "I need some water",
|
||||
chinese: "J'ai besoin d'eau",
|
||||
prononciation: "aɪ nid sʌm ˈwɔːtər"
|
||||
}
|
||||
]
|
||||
};
|
||||
81
src/content/example-with-images.js
Normal file
81
src/content/example-with-images.js
Normal file
@ -0,0 +1,81 @@
|
||||
// Example content module with image support for Word Discovery game
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
window.ContentModules.ExampleWithImages = {
|
||||
name: "Basic Vocabulary with Images",
|
||||
description: "Simple English words with visual support for beginners",
|
||||
difficulty: "easy",
|
||||
language: "en-US",
|
||||
|
||||
// Vocabulary with image support
|
||||
vocabulary: {
|
||||
"apple": {
|
||||
translation: "pomme",
|
||||
pronunciation: "æpəl",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/apple.png",
|
||||
audioFile: "assets/audio/vocabulary/apple.mp3"
|
||||
},
|
||||
"cat": {
|
||||
translation: "chat",
|
||||
pronunciation: "kæt",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/cat.png",
|
||||
audioFile: "assets/audio/vocabulary/cat_broken.mp3" // Broken path to test fallback
|
||||
},
|
||||
"house": {
|
||||
translation: "maison",
|
||||
pronunciation: "haʊs",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/house.png"
|
||||
},
|
||||
"car": {
|
||||
translation: "voiture",
|
||||
pronunciation: "kɑr",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/car.png"
|
||||
},
|
||||
"tree": {
|
||||
translation: "arbre",
|
||||
pronunciation: "tri",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/tree.png"
|
||||
},
|
||||
"book": {
|
||||
translation: "livre",
|
||||
pronunciation: "bʊk",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/book.png"
|
||||
},
|
||||
"sun": {
|
||||
translation: "soleil",
|
||||
pronunciation: "sʌn",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/sun.png"
|
||||
},
|
||||
"dog": {
|
||||
translation: "chien",
|
||||
pronunciation: "dɔg",
|
||||
type: "noun",
|
||||
image: "assets/images/vocabulary/dog.png"
|
||||
}
|
||||
},
|
||||
|
||||
// Backward compatibility for other games
|
||||
sentences: [
|
||||
{
|
||||
english: "The apple is red",
|
||||
chinese: "La pomme est rouge",
|
||||
prononciation: "ðə æpəl ɪz red"
|
||||
},
|
||||
{
|
||||
english: "The cat is sleeping",
|
||||
chinese: "Le chat dort",
|
||||
prononciation: "ðə kæt ɪz slipɪŋ"
|
||||
},
|
||||
{
|
||||
english: "I live in a house",
|
||||
chinese: "J'habite dans une maison",
|
||||
prononciation: "aɪ lɪv ɪn ə haʊs"
|
||||
}
|
||||
]
|
||||
};
|
||||
524
src/content/french-beginner-story.js
Normal file
524
src/content/french-beginner-story.js
Normal file
@ -0,0 +1,524 @@
|
||||
// === CHINESE BEGINNER STORY ===
|
||||
// Histoire chinoise pour débutants+ avec traduction française et prononciation pinyin
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.FrenchBeginnerStory = {
|
||||
id: "french-beginner-story",
|
||||
name: "Le Jardin Magique - The Magic Garden",
|
||||
description: "Simple French story for English speakers",
|
||||
difficulty: "beginner-plus",
|
||||
language: "fr-FR", // Target language = français
|
||||
userLanguage: "en-US", // User language = anglais
|
||||
totalWords: 15,
|
||||
type: "story_course",
|
||||
|
||||
// === GRAMMAIRE DE BASE ===
|
||||
grammar: {
|
||||
"basic-sentence-structure": {
|
||||
title: "French Basic Sentence Structure - Structure de Phrase Française",
|
||||
explanation: "French follows Subject-Verb-Object order like English, but with some important differences.",
|
||||
mainRules: [
|
||||
"Subject + Verb + Object (Je mange une pomme - I eat an apple)",
|
||||
"French verbs conjugate according to the subject (je mange, tu manges, il mange)",
|
||||
"Adjectives usually come after the noun (une fleur rouge - a red flower)",
|
||||
"French nouns have gender (masculine/feminine)"
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
french: "J'aime les fleurs",
|
||||
english: "I love flowers",
|
||||
pronunciation: "ʒɛm le flœʁ",
|
||||
explanation: "Basic structure: Je(I) + aime(love) + les fleurs(flowers)"
|
||||
},
|
||||
{
|
||||
french: "Le jardin est très beau",
|
||||
english: "The garden is very beautiful",
|
||||
pronunciation: "lə ʒaʁdɛ̃ ɛ tʁɛ bo",
|
||||
explanation: "être(to be) + adjective structure"
|
||||
}
|
||||
],
|
||||
detailedExplanation: {
|
||||
"subject-verb-object": {
|
||||
title: "Ordre Sujet-Verbe-Objet",
|
||||
explanation: "Le chinois suit la même logique que le français pour l'ordre des mots de base.",
|
||||
pattern: "Sujet + Verbe + Objet",
|
||||
examples: [
|
||||
{
|
||||
chinese: "小猫吃鱼",
|
||||
english: "Le petit chat mange du poisson",
|
||||
pronunciation: "xiǎo māo chī yú",
|
||||
breakdown: "小猫(petit chat) + 吃(manger) + 鱼(poisson)"
|
||||
},
|
||||
{
|
||||
chinese: "我看书",
|
||||
english: "Je lis un livre",
|
||||
pronunciation: "wǒ kàn shū",
|
||||
breakdown: "我(je) + 看(regarder/lire) + 书(livre)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"adjectives": {
|
||||
title: "Utilisation des Adjectifs",
|
||||
explanation: "Les adjectifs peuvent être utilisés directement après 很 (très) sans verbe 'être'.",
|
||||
pattern: "Sujet + 很 + Adjectif",
|
||||
examples: [
|
||||
{
|
||||
chinese: "花很红",
|
||||
english: "La fleur est très rouge",
|
||||
pronunciation: "huā hěn hóng",
|
||||
breakdown: "花(fleur) + 很(très) + 红(rouge)"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
commonMistakes: [
|
||||
{
|
||||
mistake: "Conjuguer les verbes",
|
||||
wrong: "我吃了,你吃着,他吃的",
|
||||
correct: "我吃,你吃,他吃",
|
||||
explanation: "Les verbes chinois ne se conjuguent pas selon la personne"
|
||||
},
|
||||
{
|
||||
mistake: "Oublier 很 avec les adjectifs",
|
||||
wrong: "花园美",
|
||||
correct: "花园很美",
|
||||
explanation: "Utiliser 很 devant les adjectifs pour une phrase complète"
|
||||
}
|
||||
],
|
||||
practicePoints: [
|
||||
"Commencez par des phrases simples : Sujet + Verbe + Objet",
|
||||
"Utilisez 很 + adjectif pour décrire",
|
||||
"Pas de conjugaison = plus simple !",
|
||||
"Écoutez la mélodie de la langue chinoise"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// === VOCABULAIRE FRANÇAIS POUR APPRENANTS CHINOIS (15+ mots) ===
|
||||
vocabulary: {
|
||||
"fleur": {
|
||||
user_language: "flower",
|
||||
type: "noun",
|
||||
pronunciation: "flœʁ",
|
||||
gender: "feminine"
|
||||
},
|
||||
"jardin": {
|
||||
user_language: "garden",
|
||||
type: "noun",
|
||||
pronunciation: "ʒaʁdɛ̃",
|
||||
gender: "masculine"
|
||||
},
|
||||
"arbre": {
|
||||
user_language: "tree",
|
||||
type: "noun",
|
||||
pronunciation: "aʁbʁ",
|
||||
gender: "masculine"
|
||||
},
|
||||
"petit": {
|
||||
user_language: "small/little",
|
||||
type: "adjective",
|
||||
pronunciation: "pəti"
|
||||
},
|
||||
"grand": {
|
||||
user_language: "big/large",
|
||||
type: "adjective",
|
||||
pronunciation: "ɡʁɑ̃"
|
||||
},
|
||||
"beau": {
|
||||
user_language: "beautiful/handsome",
|
||||
type: "adjective",
|
||||
pronunciation: "bo"
|
||||
},
|
||||
"rouge": {
|
||||
user_language: "red",
|
||||
type: "adjective",
|
||||
pronunciation: "ʁuʒ"
|
||||
},
|
||||
"vert": {
|
||||
user_language: "green",
|
||||
type: "adjective",
|
||||
pronunciation: "vɛʁ"
|
||||
},
|
||||
"je": {
|
||||
user_language: "I",
|
||||
type: "pronoun",
|
||||
pronunciation: "ʒə"
|
||||
},
|
||||
"tu": {
|
||||
user_language: "you",
|
||||
type: "pronoun",
|
||||
pronunciation: "ty"
|
||||
},
|
||||
"regarder": {
|
||||
user_language: "to look/watch",
|
||||
type: "verb",
|
||||
pronunciation: "ʁəɡaʁde"
|
||||
},
|
||||
"aimer": {
|
||||
user_language: "to love/like",
|
||||
type: "verb",
|
||||
pronunciation: "ɛme"
|
||||
},
|
||||
"très": {
|
||||
user_language: "very",
|
||||
type: "adverb",
|
||||
pronunciation: "tʁɛ"
|
||||
},
|
||||
"avoir": {
|
||||
user_language: "to have",
|
||||
type: "verb",
|
||||
pronunciation: "avwaʁ"
|
||||
},
|
||||
"chat": {
|
||||
user_language: "cat",
|
||||
type: "noun",
|
||||
pronunciation: "ʃa",
|
||||
gender: "masculine"
|
||||
},
|
||||
"mignon": {
|
||||
user_language: "cute/adorable",
|
||||
type: "adjective",
|
||||
pronunciation: "miɲɔ̃"
|
||||
},
|
||||
"maintenant": {
|
||||
user_language: "now",
|
||||
type: "adverb",
|
||||
pronunciation: "mɛ̃tnɑ̃"
|
||||
}
|
||||
},
|
||||
|
||||
// === STRUCTURE PAR LETTRES POUR LETTER DISCOVERY ===
|
||||
letters: {
|
||||
"A": [
|
||||
{
|
||||
word: "arbre",
|
||||
translation: "tree",
|
||||
pronunciation: "aʁbʁ",
|
||||
type: "noun",
|
||||
gender: "masculine"
|
||||
},
|
||||
{
|
||||
word: "aimer",
|
||||
translation: "to love/like",
|
||||
pronunciation: "ɛme",
|
||||
type: "verb"
|
||||
},
|
||||
{
|
||||
word: "avoir",
|
||||
translation: "to have",
|
||||
pronunciation: "avwaʁ",
|
||||
type: "verb"
|
||||
}
|
||||
],
|
||||
"B": [
|
||||
{
|
||||
word: "beau",
|
||||
translation: "beautiful/handsome",
|
||||
pronunciation: "bo",
|
||||
type: "adjective"
|
||||
},
|
||||
{
|
||||
word: "beaucoup",
|
||||
translation: "a lot/much",
|
||||
pronunciation: "boku",
|
||||
type: "adverb"
|
||||
}
|
||||
],
|
||||
"C": [
|
||||
{
|
||||
word: "chat",
|
||||
translation: "cat",
|
||||
pronunciation: "ʃa",
|
||||
type: "noun",
|
||||
gender: "masculine"
|
||||
}
|
||||
],
|
||||
"F": [
|
||||
{
|
||||
word: "fleur",
|
||||
translation: "flower",
|
||||
pronunciation: "flœʁ",
|
||||
type: "noun",
|
||||
gender: "feminine"
|
||||
}
|
||||
],
|
||||
"G": [
|
||||
{
|
||||
word: "grand",
|
||||
translation: "big/large",
|
||||
pronunciation: "ɡʁɑ̃",
|
||||
type: "adjective"
|
||||
}
|
||||
],
|
||||
"J": [
|
||||
{
|
||||
word: "jardin",
|
||||
translation: "garden",
|
||||
pronunciation: "ʒaʁdɛ̃",
|
||||
type: "noun",
|
||||
gender: "masculine"
|
||||
},
|
||||
{
|
||||
word: "je",
|
||||
translation: "I",
|
||||
pronunciation: "ʒə",
|
||||
type: "pronoun"
|
||||
}
|
||||
],
|
||||
"M": [
|
||||
{
|
||||
word: "mignon",
|
||||
translation: "cute/adorable",
|
||||
pronunciation: "miɲɔ̃",
|
||||
type: "adjective"
|
||||
},
|
||||
{
|
||||
word: "maintenant",
|
||||
translation: "now",
|
||||
pronunciation: "mɛ̃tnɑ̃",
|
||||
type: "adverb"
|
||||
}
|
||||
],
|
||||
"P": [
|
||||
{
|
||||
word: "petit",
|
||||
translation: "small/little",
|
||||
pronunciation: "pəti",
|
||||
type: "adjective"
|
||||
}
|
||||
],
|
||||
"R": [
|
||||
{
|
||||
word: "rouge",
|
||||
translation: "red",
|
||||
pronunciation: "ʁuʒ",
|
||||
type: "adjective"
|
||||
},
|
||||
{
|
||||
word: "regarder",
|
||||
translation: "to look/watch",
|
||||
pronunciation: "ʁəɡaʁde",
|
||||
type: "verb"
|
||||
}
|
||||
],
|
||||
"T": [
|
||||
{
|
||||
word: "tu",
|
||||
translation: "you",
|
||||
pronunciation: "ty",
|
||||
type: "pronoun"
|
||||
},
|
||||
{
|
||||
word: "très",
|
||||
translation: "very",
|
||||
pronunciation: "tʁɛ",
|
||||
type: "adverb"
|
||||
}
|
||||
],
|
||||
"V": [
|
||||
{
|
||||
word: "vert",
|
||||
translation: "green",
|
||||
pronunciation: "vɛʁ",
|
||||
type: "adjective"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// === HISTOIRE SIMPLE ===
|
||||
story: {
|
||||
title: "Le Jardin Magique - 魔法花园",
|
||||
totalSentences: 8,
|
||||
chapters: [
|
||||
{
|
||||
title: "第一章:美丽的花园 (Chapitre 1: Le Beau Jardin)",
|
||||
sentences: [
|
||||
{
|
||||
id: 1,
|
||||
original: "J'ai un petit jardin.",
|
||||
translation: "I have a small garden.",
|
||||
words: [
|
||||
{word: "J'", translation: "I", type: "pronoun", pronunciation: "ʒ"},
|
||||
{word: "ai", translation: "have", type: "verb", pronunciation: "e"},
|
||||
{word: "un", translation: "a", type: "article", pronunciation: "œ̃"},
|
||||
{word: "petit", translation: "small", type: "adjective", pronunciation: "pəti"},
|
||||
{word: "jardin", translation: "garden", type: "noun", pronunciation: "ʒaʁdɛ̃"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
original: "Dans le jardin, il y a beaucoup de belles fleurs.",
|
||||
translation: "花园里有很多美丽的花。",
|
||||
words: [
|
||||
{word: "Dans", translation: "在", type: "preposition", pronunciation: "dɑ̃"},
|
||||
{word: "le", translation: "这个", type: "article", pronunciation: "lə"},
|
||||
{word: "jardin", translation: "花园", type: "noun", pronunciation: "ʒaʁdɛ̃"},
|
||||
{word: "il y a", translation: "有", type: "verb", pronunciation: "il i a"},
|
||||
{word: "beaucoup", translation: "很多", type: "adverb", pronunciation: "boku"},
|
||||
{word: "de", translation: "的", type: "preposition", pronunciation: "də"},
|
||||
{word: "belles", translation: "美丽的", type: "adjective", pronunciation: "bɛl"},
|
||||
{word: "fleurs", translation: "花", type: "noun", pronunciation: "flœʁ"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
original: "Il y a des fleurs rouges et des arbres verts.",
|
||||
translation: "有红花和绿树。",
|
||||
words: [
|
||||
{word: "Il y a", translation: "有", type: "verb", pronunciation: "il i a"},
|
||||
{word: "des", translation: "一些", type: "article", pronunciation: "de"},
|
||||
{word: "fleurs", translation: "花", type: "noun", pronunciation: "flœʁ"},
|
||||
{word: "rouges", translation: "红色的", type: "adjective", pronunciation: "ʁuʒ"},
|
||||
{word: "et", translation: "和", type: "conjunction", pronunciation: "e"},
|
||||
{word: "des", translation: "一些", type: "article", pronunciation: "de"},
|
||||
{word: "arbres", translation: "树", type: "noun", pronunciation: "aʁbʁ"},
|
||||
{word: "verts", translation: "绿色的", type: "adjective", pronunciation: "vɛʁ"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
original: "J'aime beaucoup mon jardin.",
|
||||
translation: "我很喜欢我的花园。",
|
||||
words: [
|
||||
{word: "J'", translation: "我", type: "pronoun", pronunciation: "ʒ"},
|
||||
{word: "aime", translation: "喜欢", type: "verb", pronunciation: "ɛm"},
|
||||
{word: "beaucoup", translation: "很", type: "adverb", pronunciation: "boku"},
|
||||
{word: "mon", translation: "我的", type: "pronoun", pronunciation: "mɔ̃"},
|
||||
{word: "jardin", translation: "花园", type: "noun", pronunciation: "ʒaʁdɛ̃"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "第二章:小猫来了 (Chapitre 2: Le Petit Chat Arrive)",
|
||||
sentences: [
|
||||
{
|
||||
id: 5,
|
||||
original: "一天,一只小猫来到花园。",
|
||||
translation: "Un jour, un petit chat est venu dans le jardin.",
|
||||
words: [
|
||||
{word: "一天", translation: "un jour", type: "noun", pronunciation: "yì tiān"},
|
||||
{word: "一只", translation: "un (classificateur)", type: "number", pronunciation: "yì zhī"},
|
||||
{word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"},
|
||||
{word: "来到", translation: "venir à", type: "verb", pronunciation: "lái dào"},
|
||||
{word: "花园", translation: "jardin", type: "noun", pronunciation: "huā yuán"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
original: "小猫看花,我看小猫。",
|
||||
translation: "Le petit chat regarde les fleurs, moi je regarde le petit chat.",
|
||||
words: [
|
||||
{word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"},
|
||||
{word: "看", translation: "regarder", type: "verb", pronunciation: "kàn"},
|
||||
{word: "花", translation: "fleur", type: "noun", pronunciation: "huā"},
|
||||
{word: "我", translation: "je", type: "pronoun", pronunciation: "wǒ"},
|
||||
{word: "看", translation: "regarder", type: "verb", pronunciation: "kàn"},
|
||||
{word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
original: "小猫很可爱。",
|
||||
translation: "Le petit chat est très mignon.",
|
||||
words: [
|
||||
{word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"},
|
||||
{word: "很", translation: "très", type: "adverb", pronunciation: "hěn"},
|
||||
{word: "可爱", translation: "mignon", type: "adjective", pronunciation: "kě ài"}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
original: "现在,花园更美了。",
|
||||
translation: "Maintenant, le jardin est encore plus beau.",
|
||||
words: [
|
||||
{word: "现在", translation: "maintenant", type: "adverb", pronunciation: "xiàn zài"},
|
||||
{word: "花园", translation: "jardin", type: "noun", pronunciation: "huā yuán"},
|
||||
{word: "更", translation: "encore plus", type: "adverb", pronunciation: "gèng"},
|
||||
{word: "美", translation: "beau", type: "adjective", pronunciation: "měi"},
|
||||
{word: "了", translation: "particule d'aspect", type: "particle", pronunciation: "le"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// === EXERCICES DE COMPRÉHENSION ===
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "J'___ un petit jardin",
|
||||
options: ["ai", "es", "regarde", "très"],
|
||||
correctAnswer: "ai",
|
||||
explanation: "使用 'ai' 表示拥有 - Use 'ai' to express possession (I have)"
|
||||
},
|
||||
{
|
||||
sentence: "Le jardin est ___ beau",
|
||||
options: ["ai", "très", "dans", "de"],
|
||||
correctAnswer: "très",
|
||||
explanation: "使用 'très' + 形容词 - Use 'très' + adjective"
|
||||
},
|
||||
{
|
||||
sentence: "Le petit chat ___ les fleurs",
|
||||
options: ["regarde", "ai", "très", "de"],
|
||||
correctAnswer: "regarde",
|
||||
explanation: "'regarde' 意思是看/观察 - 'regarde' means to look/observe"
|
||||
},
|
||||
{
|
||||
sentence: "J'___ beaucoup mon jardin",
|
||||
options: ["aime", "beau", "petit", "rouge"],
|
||||
correctAnswer: "aime",
|
||||
explanation: "'aime' 表示喜爱 - 'aime' expresses liking"
|
||||
},
|
||||
{
|
||||
sentence: "Dans le jardin, il y ___ beaucoup de fleurs",
|
||||
options: ["a", "regarde", "très", "petit"],
|
||||
correctAnswer: "a",
|
||||
explanation: "'il y a' 表示存在 - 'il y a' expresses existence"
|
||||
}
|
||||
],
|
||||
|
||||
// === CORRECTIONS D'ERREURS ===
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "Je suis aimer le jardin",
|
||||
correct: "J'aime le jardin",
|
||||
explanation: "不需要 'suis',直接用动词 'aime' - No need for 'suis', use verb 'aime' directly"
|
||||
},
|
||||
{
|
||||
incorrect: "Le jardin beau",
|
||||
correct: "Le jardin est très beau",
|
||||
explanation: "需要动词 'est' 和副词 'très' - Need verb 'est' and adverb 'très'"
|
||||
},
|
||||
{
|
||||
incorrect: "Le petit chat a regardé les fleurs",
|
||||
correct: "Le petit chat regarde les fleurs",
|
||||
explanation: "简单动作用现在时 - Simple actions use present tense"
|
||||
}
|
||||
],
|
||||
|
||||
// === PHRASES D'EXEMPLE ===
|
||||
sentences: [
|
||||
{
|
||||
french: "J'ai un beau jardin",
|
||||
chinese: "我有一个美丽的花园",
|
||||
pronunciation: "ʒe œ̃ bo ʒaʁdɛ̃"
|
||||
},
|
||||
{
|
||||
french: "Le petit chat est très mignon",
|
||||
chinese: "小猫很可爱",
|
||||
pronunciation: "lə pəti ʃa ɛ tʁɛ miɲɔ̃"
|
||||
},
|
||||
{
|
||||
french: "Dans le jardin il y a des fleurs rouges et des arbres verts",
|
||||
chinese: "花园里有红花和绿树",
|
||||
pronunciation: "dɑ̃ lə ʒaʁdɛ̃ il i a de flœʁ ʁuʒ e dez‿aʁbʁ vɛʁ"
|
||||
},
|
||||
{
|
||||
french: "Je regarde le petit chat, le petit chat regarde les fleurs",
|
||||
chinese: "我看小猫,小猫看花",
|
||||
pronunciation: "ʒə ʁəɡaʁd lə pəti ʃa, lə pəti ʃa ʁəɡaʁd le flœʁ"
|
||||
}
|
||||
]
|
||||
};
|
||||
293
src/content/grammar-lesson-le.js
Normal file
293
src/content/grammar-lesson-le.js
Normal file
@ -0,0 +1,293 @@
|
||||
// === GRAMMAR LESSON: 了 ASPECT PARTICLE ===
|
||||
// Dedicated grammar course focused on the 了 particle in Chinese
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.GrammarLessonLe = {
|
||||
id: "grammar-lesson-le",
|
||||
name: "Grammar Lesson: 了 (le) Aspect Particle",
|
||||
description: "Complete lesson on the Chinese aspect particle 了 - completion and change of state",
|
||||
difficulty: "intermediate",
|
||||
language: "zh-CN",
|
||||
type: "grammar_course",
|
||||
|
||||
// === MAIN GRAMMAR LESSON ===
|
||||
grammar: {
|
||||
"le-aspect-particle": {
|
||||
title: "The 了 (le) Aspect Particle - 动态助词了",
|
||||
explanation: "了 is one of the most important particles in Chinese. It indicates completion of an action or a change of state. Unlike English past tense, 了 focuses on the aspect (how the action is viewed) rather than when it happened.",
|
||||
|
||||
mainRules: [
|
||||
"了 shows that an action has been completed",
|
||||
"了 indicates a change from one state to another",
|
||||
"了 can appear after the verb (了1) or at the end of sentence (了2)",
|
||||
"了 does NOT simply mean 'past tense' - it's about completion/change"
|
||||
],
|
||||
|
||||
detailedExplanation: {
|
||||
"completion": {
|
||||
title: "1. Completion of Action (动作完成)",
|
||||
explanation: "了 after a verb shows the action has been completed",
|
||||
pattern: "Subject + Verb + 了 + Object",
|
||||
examples: [
|
||||
{
|
||||
chinese: "我吃了饭",
|
||||
english: "I ate (have eaten) the meal",
|
||||
pronunciation: "wǒ chī le fàn",
|
||||
breakdown: "我(I) + 吃(eat) + 了(completed) + 饭(meal)",
|
||||
explanation: "The eating action is completed"
|
||||
},
|
||||
{
|
||||
chinese: "他买了一本书",
|
||||
english: "He bought a book",
|
||||
pronunciation: "tā mǎi le yì běn shū",
|
||||
breakdown: "他(he) + 买(buy) + 了(completed) + 一本书(a book)",
|
||||
explanation: "The buying action is finished"
|
||||
},
|
||||
{
|
||||
chinese: "老师讲了三个故事",
|
||||
english: "The teacher told three stories",
|
||||
pronunciation: "lǎo shī jiǎng le sān gè gù shì",
|
||||
breakdown: "老师(teacher) + 讲(tell) + 了(completed) + 三个故事(three stories)",
|
||||
explanation: "The telling action is complete"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"change-of-state": {
|
||||
title: "2. Change of State (状态变化)",
|
||||
explanation: "了 at the end of a sentence shows a change in situation or state",
|
||||
pattern: "Subject + Verb + Object + 了",
|
||||
examples: [
|
||||
{
|
||||
chinese: "天黑了",
|
||||
english: "It has gotten dark / It's dark now",
|
||||
pronunciation: "tiān hēi le",
|
||||
breakdown: "天(sky) + 黑(dark) + 了(change of state)",
|
||||
explanation: "Change from light to dark"
|
||||
},
|
||||
{
|
||||
chinese: "我饿了",
|
||||
english: "I'm hungry now / I've become hungry",
|
||||
pronunciation: "wǒ è le",
|
||||
breakdown: "我(I) + 饿(hungry) + 了(change of state)",
|
||||
explanation: "Change from not hungry to hungry"
|
||||
},
|
||||
{
|
||||
chinese: "下雨了",
|
||||
english: "It's raining now / It started to rain",
|
||||
pronunciation: "xià yǔ le",
|
||||
breakdown: "下雨(rain) + 了(change of state)",
|
||||
explanation: "Change from not raining to raining"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"double-le": {
|
||||
title: "3. Double 了 Construction",
|
||||
explanation: "Sometimes 了 appears both after the verb AND at the end of sentence",
|
||||
pattern: "Subject + Verb + 了 + Object + 了",
|
||||
examples: [
|
||||
{
|
||||
chinese: "我买了三本书了",
|
||||
english: "I have bought three books (and the situation has changed)",
|
||||
pronunciation: "wǒ mǎi le sān běn shū le",
|
||||
breakdown: "我 + 买了(completed buying) + 三本书 + 了(new situation)",
|
||||
explanation: "Action completed AND situation changed"
|
||||
},
|
||||
{
|
||||
chinese: "他吃了两个苹果了",
|
||||
english: "He has eaten two apples (and is now full/satisfied)",
|
||||
pronunciation: "tā chī le liǎng gè píng guǒ le",
|
||||
breakdown: "他 + 吃了(completed eating) + 两个苹果 + 了(new state)",
|
||||
explanation: "Eating completed AND state changed"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
commonMistakes: [
|
||||
{
|
||||
mistake: "Using 了 for all past actions",
|
||||
wrong: "我昨天了去学校",
|
||||
correct: "我昨天去了学校 / 我昨天去学校",
|
||||
explanation: "了 shows completion, not just past time. Don't add 了 randomly to past time expressions."
|
||||
},
|
||||
{
|
||||
mistake: "Forgetting 了 for completed actions",
|
||||
wrong: "我吃饭,现在很饱",
|
||||
correct: "我吃了饭,现在很饱",
|
||||
explanation: "Need 了 to show the eating is completed before being full"
|
||||
},
|
||||
{
|
||||
mistake: "Using 了 with ongoing actions",
|
||||
wrong: "我正在吃了饭",
|
||||
correct: "我正在吃饭",
|
||||
explanation: "Can't use 了 with 正在 (ongoing) - they're contradictory"
|
||||
}
|
||||
],
|
||||
|
||||
practicePoints: [
|
||||
"Ask yourself: Is the action completed? Use 了 after verb",
|
||||
"Ask yourself: Has the situation changed? Use 了 at end",
|
||||
"Remember: 了 ≠ past tense. It's about completion/change",
|
||||
"Pay attention to context - sometimes 了 is not needed even for past actions"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// === VOCABULARY FOR THE LESSON ===
|
||||
vocabulary: {
|
||||
"了": {
|
||||
translation: "aspect particle (completion/change)",
|
||||
type: "particle",
|
||||
pronunciation: "le",
|
||||
usage: "Shows completed action or change of state"
|
||||
},
|
||||
"吃": {
|
||||
translation: "to eat",
|
||||
type: "verb",
|
||||
pronunciation: "chī"
|
||||
},
|
||||
"买": {
|
||||
translation: "to buy",
|
||||
type: "verb",
|
||||
pronunciation: "mǎi"
|
||||
},
|
||||
"讲": {
|
||||
translation: "to tell/speak",
|
||||
type: "verb",
|
||||
pronunciation: "jiǎng"
|
||||
},
|
||||
"饭": {
|
||||
translation: "meal/food",
|
||||
type: "noun",
|
||||
pronunciation: "fàn"
|
||||
},
|
||||
"书": {
|
||||
translation: "book",
|
||||
type: "noun",
|
||||
pronunciation: "shū"
|
||||
},
|
||||
"故事": {
|
||||
translation: "story",
|
||||
type: "noun",
|
||||
pronunciation: "gù shì"
|
||||
},
|
||||
"天": {
|
||||
translation: "sky/day",
|
||||
type: "noun",
|
||||
pronunciation: "tiān"
|
||||
},
|
||||
"黑": {
|
||||
translation: "dark/black",
|
||||
type: "adjective",
|
||||
pronunciation: "hēi"
|
||||
},
|
||||
"饿": {
|
||||
translation: "hungry",
|
||||
type: "adjective",
|
||||
pronunciation: "è"
|
||||
},
|
||||
"下雨": {
|
||||
translation: "to rain",
|
||||
type: "verb",
|
||||
pronunciation: "xià yǔ"
|
||||
}
|
||||
},
|
||||
|
||||
// === FILL IN THE BLANKS EXERCISES ===
|
||||
fillInBlanks: [
|
||||
{
|
||||
sentence: "我吃___饭,现在很饱",
|
||||
options: ["了", "的", "在", "着"],
|
||||
correctAnswer: "了",
|
||||
explanation: "Use 了 to show the eating action is completed before being full",
|
||||
grammarFocus: "completion"
|
||||
},
|
||||
{
|
||||
sentence: "天黑___,我们回家吧",
|
||||
options: ["了", "的", "在", "着"],
|
||||
correctAnswer: "了",
|
||||
explanation: "Use 了 to show change of state - it has become dark",
|
||||
grammarFocus: "change-of-state"
|
||||
},
|
||||
{
|
||||
sentence: "他买___三本书___",
|
||||
options: ["了...了", "的...的", "在...在", "着...着"],
|
||||
correctAnswer: "了...了",
|
||||
explanation: "Double 了: action completed (买了) AND situation changed (了)",
|
||||
grammarFocus: "double-le"
|
||||
},
|
||||
{
|
||||
sentence: "我昨天___学校",
|
||||
options: ["去了", "了去", "去的", "的去"],
|
||||
correctAnswer: "去了",
|
||||
explanation: "了 comes after the verb to show completed action",
|
||||
grammarFocus: "word-order"
|
||||
},
|
||||
{
|
||||
sentence: "下雨___,路很湿",
|
||||
options: ["了", "的", "在", "着"],
|
||||
correctAnswer: "了",
|
||||
explanation: "Change of state: it has started raining (wasn't raining before)",
|
||||
grammarFocus: "change-of-state"
|
||||
}
|
||||
],
|
||||
|
||||
// === CORRECTION EXERCISES ===
|
||||
corrections: [
|
||||
{
|
||||
incorrect: "我昨天了去学校",
|
||||
correct: "我昨天去了学校",
|
||||
explanation: "了 should come after the verb, not before it",
|
||||
grammarFocus: "word-order"
|
||||
},
|
||||
{
|
||||
incorrect: "我正在吃了饭",
|
||||
correct: "我正在吃饭",
|
||||
explanation: "Cannot use 了 (completion) with 正在 (ongoing action)",
|
||||
grammarFocus: "aspect-conflict"
|
||||
},
|
||||
{
|
||||
incorrect: "我吃饭,现在很饱",
|
||||
correct: "我吃了饭,现在很饱",
|
||||
explanation: "Need 了 to show eating is completed before the result (being full)",
|
||||
grammarFocus: "completion"
|
||||
},
|
||||
{
|
||||
incorrect: "他很高了的人",
|
||||
correct: "他是很高的人",
|
||||
explanation: "Don't use 了 in descriptions with 的. 了 is for actions/changes, not permanent descriptions",
|
||||
grammarFocus: "inappropriate-usage"
|
||||
}
|
||||
],
|
||||
|
||||
// === TRANSLATION EXERCISES ===
|
||||
sentences: [
|
||||
{
|
||||
english: "I finished my homework",
|
||||
chinese: "我做完了作业",
|
||||
pronunciation: "wǒ zuò wán le zuò yè",
|
||||
grammarFocus: "completion"
|
||||
},
|
||||
{
|
||||
english: "It's gotten cold",
|
||||
chinese: "天气冷了",
|
||||
pronunciation: "tiān qì lěng le",
|
||||
grammarFocus: "change-of-state"
|
||||
},
|
||||
{
|
||||
english: "He bought two books and now has them",
|
||||
chinese: "他买了两本书了",
|
||||
pronunciation: "tā mǎi le liǎng běn shū le",
|
||||
grammarFocus: "double-le"
|
||||
},
|
||||
{
|
||||
english: "The teacher finished the lesson",
|
||||
chinese: "老师讲完了课",
|
||||
pronunciation: "lǎo shī jiǎng wán le kè",
|
||||
grammarFocus: "completion"
|
||||
}
|
||||
]
|
||||
};
|
||||
168
src/content/sbs-level-7-8-new.js
Normal file
168
src/content/sbs-level-7-8-new.js
Normal file
@ -0,0 +1,168 @@
|
||||
// === SBS LEVEL 7-8 VOCABULARY (LANGUAGE-AGNOSTIC FORMAT) ===
|
||||
|
||||
window.ContentModules = window.ContentModules || {};
|
||||
|
||||
window.ContentModules.SBSLevel78New = {
|
||||
name: "SBS Level 7-8 New",
|
||||
description: "Side by Side Level 7-8 vocabulary with language-agnostic format",
|
||||
difficulty: "intermediate",
|
||||
language: "en-US",
|
||||
|
||||
vocabulary: {
|
||||
// Housing and Places
|
||||
"central": { user_language: "中心的;中央的", type: "adjective" },
|
||||
"avenue": { user_language: "大街;林荫道", type: "noun" },
|
||||
"refrigerator": { user_language: "冰箱", type: "noun" },
|
||||
"closet": { user_language: "衣柜;壁橱", type: "noun" },
|
||||
"elevator": { user_language: "电梯", type: "noun" },
|
||||
"building": { user_language: "建筑物;大楼", type: "noun" },
|
||||
"air conditioner": { user_language: "空调", type: "noun" },
|
||||
"superintendent": { user_language: "主管;负责人", type: "noun" },
|
||||
"bus stop": { user_language: "公交车站", type: "noun" },
|
||||
"jacuzzi": { user_language: "按摩浴缸", type: "noun" },
|
||||
"machine": { user_language: "机器;设备", type: "noun" },
|
||||
"two and a half": { user_language: "两个半", type: "number" },
|
||||
"in the center of": { user_language: "在……中心", type: "preposition" },
|
||||
"town": { user_language: "城镇", type: "noun" },
|
||||
"a lot of": { user_language: "许多", type: "determiner" },
|
||||
"noise": { user_language: "噪音", type: "noun" },
|
||||
"sidewalks": { user_language: "人行道", type: "noun" },
|
||||
"all day and all night": { user_language: "整日整夜", type: "adverb" },
|
||||
"convenient": { user_language: "便利的", type: "adjective" },
|
||||
"upset": { user_language: "失望的", type: "adjective" },
|
||||
|
||||
// Clothing and Accessories
|
||||
"shirt": { user_language: "衬衫", type: "noun" },
|
||||
"coat": { user_language: "外套、大衣", type: "noun" },
|
||||
"dress": { user_language: "连衣裙", type: "noun" },
|
||||
"skirt": { user_language: "短裙", type: "noun" },
|
||||
"blouse": { user_language: "女式衬衫", type: "noun" },
|
||||
"jacket": { user_language: "夹克、短外套", type: "noun" },
|
||||
"sweater": { user_language: "毛衣、针织衫", type: "noun" },
|
||||
"suit": { user_language: "套装、西装", type: "noun" },
|
||||
"tie": { user_language: "领带", type: "noun" },
|
||||
"pants": { user_language: "裤子", type: "noun" },
|
||||
"jeans": { user_language: "牛仔裤", type: "noun" },
|
||||
"belt": { user_language: "腰带、皮带", type: "noun" },
|
||||
"hat": { user_language: "帽子", type: "noun" },
|
||||
"glove": { user_language: "手套", type: "noun" },
|
||||
"purse": { user_language: "手提包、女式小包", type: "noun" },
|
||||
"glasses": { user_language: "眼镜", type: "noun" },
|
||||
"pajamas": { user_language: "睡衣", type: "noun" },
|
||||
"socks": { user_language: "袜子", type: "noun" },
|
||||
"shoes": { user_language: "鞋子", type: "noun" },
|
||||
"bathrobe": { user_language: "浴袍", type: "noun" },
|
||||
"tee shirt": { user_language: "T恤", type: "noun" },
|
||||
"scarf": { user_language: "围巾", type: "noun" },
|
||||
"wallet": { user_language: "钱包", type: "noun" },
|
||||
"ring": { user_language: "戒指", type: "noun" },
|
||||
"sandals": { user_language: "凉鞋", type: "noun" },
|
||||
|
||||
// Body Parts and Health
|
||||
"throat": { user_language: "喉咙", type: "noun" },
|
||||
"shoulder": { user_language: "肩膀", type: "noun" },
|
||||
"chest": { user_language: "胸部", type: "noun" },
|
||||
"back": { user_language: "背部", type: "noun" },
|
||||
"arm": { user_language: "手臂", type: "noun" },
|
||||
"elbow": { user_language: "肘部", type: "noun" },
|
||||
"wrist": { user_language: "手腕", type: "noun" },
|
||||
"hip": { user_language: "髋部", type: "noun" },
|
||||
"thigh": { user_language: "大腿", type: "noun" },
|
||||
"knee": { user_language: "膝盖", type: "noun" },
|
||||
"shin": { user_language: "胫骨", type: "noun" },
|
||||
"ankle": { user_language: "脚踝", type: "noun" },
|
||||
"cough": { user_language: "咳嗽", type: "verb" },
|
||||
"sneeze": { user_language: "打喷嚏", type: "verb" },
|
||||
"wheeze": { user_language: "喘息", type: "verb" },
|
||||
"feel dizzy": { user_language: "感到头晕", type: "verb" },
|
||||
"feel nauseous": { user_language: "感到恶心", type: "verb" },
|
||||
"twist": { user_language: "扭伤", type: "verb" },
|
||||
"burn": { user_language: "烧伤", type: "verb" },
|
||||
"hurt": { user_language: "受伤", type: "verb" },
|
||||
"cut": { user_language: "割伤", type: "verb" },
|
||||
"sprain": { user_language: "扭伤", type: "verb" },
|
||||
"dislocate": { user_language: "脱臼", type: "verb" },
|
||||
"break": { user_language: "骨折", type: "verb" },
|
||||
|
||||
// Actions and Verbs
|
||||
"recommend": { user_language: "推荐", type: "verb" },
|
||||
"suggest": { user_language: "建议", type: "verb" },
|
||||
"insist": { user_language: "坚持", type: "verb" },
|
||||
"warn": { user_language: "警告", type: "verb" },
|
||||
"promise": { user_language: "承诺", type: "verb" },
|
||||
"apologize": { user_language: "道歉", type: "verb" },
|
||||
"complain": { user_language: "抱怨", type: "verb" },
|
||||
"discuss": { user_language: "讨论", type: "verb" },
|
||||
"argue": { user_language: "争论", type: "verb" },
|
||||
"disagree": { user_language: "不同意", type: "verb" },
|
||||
"agree": { user_language: "同意", type: "verb" },
|
||||
"decide": { user_language: "决定", type: "verb" },
|
||||
"choose": { user_language: "选择", type: "verb" },
|
||||
"prefer": { user_language: "偏爱", type: "verb" },
|
||||
"enjoy": { user_language: "享受", type: "verb" },
|
||||
"appreciate": { user_language: "欣赏", type: "verb" },
|
||||
"celebrate": { user_language: "庆祝", type: "verb" },
|
||||
"congratulate": { user_language: "祝贺", type: "verb" },
|
||||
|
||||
// Emotions and Feelings
|
||||
"worried": { user_language: "担心的", type: "adjective" },
|
||||
"concerned": { user_language: "关心的", type: "adjective" },
|
||||
"anxious": { user_language: "焦虑的", type: "adjective" },
|
||||
"nervous": { user_language: "紧张的", type: "adjective" },
|
||||
"excited": { user_language: "兴奋的", type: "adjective" },
|
||||
"thrilled": { user_language: "激动的", type: "adjective" },
|
||||
"delighted": { user_language: "高兴的", type: "adjective" },
|
||||
"pleased": { user_language: "满意的", type: "adjective" },
|
||||
"satisfied": { user_language: "满足的", type: "adjective" },
|
||||
"disappointed": { user_language: "失望的", type: "adjective" },
|
||||
"frustrated": { user_language: "沮丧的", type: "adjective" },
|
||||
"annoyed": { user_language: "恼怒的", type: "adjective" },
|
||||
"furious": { user_language: "愤怒的", type: "adjective" },
|
||||
"exhausted": { user_language: "筋疲力尽的", type: "adjective" },
|
||||
"overwhelmed": { user_language: "不知所措的", type: "adjective" },
|
||||
"confused": { user_language: "困惑的", type: "adjective" },
|
||||
"embarrassed": { user_language: "尴尬的", type: "adjective" },
|
||||
"proud": { user_language: "自豪的", type: "adjective" },
|
||||
"jealous": { user_language: "嫉妒的", type: "adjective" },
|
||||
"guilty": { user_language: "内疚的", type: "adjective" },
|
||||
|
||||
// Technology and Modern Life
|
||||
"website": { user_language: "网站", type: "noun" },
|
||||
"password": { user_language: "密码", type: "noun" },
|
||||
"username": { user_language: "用户名", type: "noun" },
|
||||
"download": { user_language: "下载", type: "verb" },
|
||||
"upload": { user_language: "上传", type: "verb" },
|
||||
"install": { user_language: "安装", type: "verb" },
|
||||
"update": { user_language: "更新", type: "verb" },
|
||||
"delete": { user_language: "删除", type: "verb" },
|
||||
"save": { user_language: "保存", type: "verb" },
|
||||
"print": { user_language: "打印", type: "verb" },
|
||||
"scan": { user_language: "扫描", type: "verb" },
|
||||
"copy": { user_language: "复制", type: "verb" },
|
||||
"paste": { user_language: "粘贴", type: "verb" },
|
||||
"search": { user_language: "搜索", type: "verb" },
|
||||
"browse": { user_language: "浏览", type: "verb" },
|
||||
"surf": { user_language: "网上冲浪", type: "verb" },
|
||||
"stream": { user_language: "流媒体", type: "verb" },
|
||||
"tweet": { user_language: "发推特", type: "verb" },
|
||||
"post": { user_language: "发布", type: "verb" },
|
||||
"share": { user_language: "分享", type: "verb" },
|
||||
"like": { user_language: "点赞", type: "verb" },
|
||||
"follow": { user_language: "关注", type: "verb" },
|
||||
"unfollow": { user_language: "取消关注", type: "verb" },
|
||||
"block": { user_language: "屏蔽", type: "verb" },
|
||||
"tag": { user_language: "标记", type: "verb" }
|
||||
},
|
||||
|
||||
// Compatibility methods for different games
|
||||
sentences: [], // For backward compatibility
|
||||
|
||||
// For Quiz and Memory games
|
||||
getVocabularyPairs() {
|
||||
return Object.entries(this.vocabulary).map(([word, data]) => ({
|
||||
english: word,
|
||||
translation: data.user_language,
|
||||
type: data.type
|
||||
}));
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user