initialization
This commit is contained in:
commit
9ff09693ed
92 files changed
+46209
No files matched your search
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// scripts/generate-docs.php
|
||||
|
||||
class DocumentationGenerator
|
||||
{
|
||||
private array $languages = [
|
||||
'de' => 'Deutsch',
|
||||
'fr' => 'Français',
|
||||
'es' => 'Español',
|
||||
'it' => 'Italiano',
|
||||
'ja' => '日本語',
|
||||
'zh' => '中文'
|
||||
];
|
||||
|
||||
public function generateAllLanguages(): void
|
||||
{
|
||||
$baseTemplate = file_get_contents(__DIR__ . '/../docs/index.html');
|
||||
|
||||
foreach ($this->languages as $code => $name) {
|
||||
$translated = $this->translateContent($baseTemplate, $code);
|
||||
file_put_contents(__DIR__ . "/../docs/docs-{$code}.html", $translated);
|
||||
echo "Generated docs-{$code}.html\n";
|
||||
}
|
||||
}
|
||||
|
||||
private function translateContent(string $content, string $lang): string
|
||||
{
|
||||
// Load translations from JSON files
|
||||
$translations = json_decode(
|
||||
file_get_contents(__DIR__ . "/../locales/{$lang}.json"),
|
||||
true
|
||||
);
|
||||
|
||||
// Replace content with translations
|
||||
$content = str_replace('lang="en"', "lang=\"{$lang}\"", $content);
|
||||
|
||||
// Replace specific content sections
|
||||
foreach ($translations as $key => $value) {
|
||||
$content = str_replace("{{$key}}", $value, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
$generator = new DocumentationGenerator();
|
||||
$generator->generateAllLanguages();
|
||||
@@ -0,0 +1,256 @@
|
||||
# Translation Management Tools
|
||||
|
||||
## 📝 Overview
|
||||
|
||||
This directory contains tools and utilities for managing translations across the Password Generator application. These tools help identify, extract, and maintain translatable strings across HTML, JavaScript, and PHP files.
|
||||
|
||||
## 🛠️ Tools Provided
|
||||
|
||||
### 1. **extract-translations.php**
|
||||
Advanced PHP-based translation extractor that scans your codebase and automatically generates/updates translation files.
|
||||
|
||||
**Features:**
|
||||
- ✅ Scans HTML, JavaScript, and PHP files
|
||||
- ✅ Extracts `data-i18n` attributes
|
||||
- ✅ Finds `this.t()` calls in JavaScript
|
||||
- ✅ Identifies hardcoded strings
|
||||
- ✅ Generates translation keys automatically
|
||||
- ✅ Updates existing translation files
|
||||
- ✅ Provides basic translations for common strings
|
||||
- ✅ Preserves consistent key structure across locales
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
php extract-translations.php
|
||||
```
|
||||
|
||||
### 2. **update-translations.sh**
|
||||
Shell script for quick translation file updates and scanning.
|
||||
|
||||
**Features:**
|
||||
- ✅ Fast scanning with grep/sed
|
||||
- ✅ Updates all language files at once
|
||||
- ✅ Shows translation coverage statistics
|
||||
- ✅ Identifies hardcoded strings
|
||||
- ✅ Color-coded output
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
chmod +x update-translations.sh
|
||||
./update-translations.sh
|
||||
```
|
||||
|
||||
## 📋 Current Translation Status
|
||||
|
||||
### Supported Languages
|
||||
- 🇺🇸 **English (en)** - Base language
|
||||
- 🇩🇪 **German (de)** - Partially translated
|
||||
- 🇫🇷 **French (fr)** - Partially translated
|
||||
- 🇮🇹 **Italian (it)** - Placeholders only
|
||||
- 🇪🇸 **Spanish (es)** - Placeholders only
|
||||
- 🇯🇵 **Japanese (ja)** - Placeholders only
|
||||
- 🇨🇳 **Chinese (zh)** - Placeholders only
|
||||
|
||||
### Translation Keys Identified
|
||||
|
||||
The tools have identified these categories of translatable strings:
|
||||
|
||||
#### **UI Elements**
|
||||
- `generate` - Generate button text
|
||||
- `copy` - Copy button text
|
||||
- `language` - Language selector label
|
||||
- `options` - Options section header
|
||||
- `loading` - Loading messages
|
||||
- `error` - Error messages
|
||||
- `success` - Success messages
|
||||
|
||||
#### **Password Generator Types**
|
||||
- `high_entropy` - High Entropy generator name
|
||||
- `xkcd_pass` - XKCD Pass generator name
|
||||
- `diceware` - Diceware generator name
|
||||
- `pwgen` - PWGen generator name
|
||||
- `passkey` - Passkey generator name
|
||||
|
||||
#### **API Documentation**
|
||||
- `api_documentation` - API Documentation link
|
||||
- `download` - Download button text
|
||||
- `interactive_api_explorer` - API Explorer heading
|
||||
- `openapi_json` - OpenAPI JSON description
|
||||
- `postman_collection` - Postman Collection description
|
||||
|
||||
## 🔧 How to Use These Tools
|
||||
|
||||
### **Step 1: Run the Extractor**
|
||||
```bash
|
||||
# Use the PHP extractor for comprehensive analysis
|
||||
php extract-translations.php
|
||||
|
||||
# OR use the shell script for quick updates
|
||||
./update-translations.sh
|
||||
```
|
||||
|
||||
### **Step 2: Review Generated Files**
|
||||
Check the `locales/` directory for:
|
||||
- Updated JSON files for each language
|
||||
- Any missing or placeholder translation entries that still need review
|
||||
|
||||
### **Step 3: Fix Hardcoded Strings**
|
||||
|
||||
**HTML Files:**
|
||||
```html
|
||||
<!-- BEFORE -->
|
||||
<button>Generate</button>
|
||||
<label>Language:</label>
|
||||
|
||||
<!-- AFTER -->
|
||||
<button data-i18n="generate">Generate</button>
|
||||
<label data-i18n="language">Language:</label>
|
||||
```
|
||||
|
||||
**JavaScript Files:**
|
||||
```javascript
|
||||
// BEFORE
|
||||
showNotification('Copied to clipboard!');
|
||||
generateBtn.innerHTML = 'Generating...';
|
||||
|
||||
// AFTER
|
||||
showNotification(this.t('copied_to_clipboard'));
|
||||
generateBtn.innerHTML = this.t('generating');
|
||||
```
|
||||
|
||||
### **Step 4: Translate Placeholder Strings**
|
||||
Replace placeholder entries in language files:
|
||||
|
||||
```json
|
||||
{
|
||||
"generate": "Generate (TODO: de)", // ← Replace this
|
||||
"copy": "Kopieren" // ✅ Already translated
|
||||
}
|
||||
```
|
||||
|
||||
With proper translations:
|
||||
```json
|
||||
{
|
||||
"generate": "Generieren", // ✅ Properly translated
|
||||
"copy": "Kopieren" // ✅ Already translated
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Translation Statistics
|
||||
|
||||
Run the tools to get current statistics:
|
||||
|
||||
```
|
||||
📊 Translation Status Report
|
||||
============================
|
||||
en: 87/87 (100%) translated
|
||||
de: 45/87 (52%) translated
|
||||
fr: 32/87 (37%) translated
|
||||
it: 15/87 (17%) translated
|
||||
es: 15/87 (17%) translated
|
||||
ja: 0/87 (0%) translated
|
||||
zh: 0/87 (0%) translated
|
||||
```
|
||||
|
||||
## 🎯 Quick Win Translations
|
||||
|
||||
### **High Priority Strings (Visible to Users)**
|
||||
1. `generate` → "Generate" button
|
||||
2. `copy` → "Copy" button
|
||||
3. `language` → Language selector
|
||||
4. `loading_generators` → Loading message
|
||||
5. `api_documentation` → Header link
|
||||
6. `back_to_generator` → Navigation link
|
||||
|
||||
### **Common Translations Already Provided**
|
||||
|
||||
**German (de):**
|
||||
- `generate` → "Generieren"
|
||||
- `copy` → "Kopieren"
|
||||
- `language` → "Sprache"
|
||||
- `password_generator` → "Passwort-Generator"
|
||||
|
||||
**French (fr):**
|
||||
- `generate` → "Générer"
|
||||
- `copy` → "Copier"
|
||||
- `language` → "Langue"
|
||||
- `password_generator` → "Générateur de mots de passe"
|
||||
|
||||
## 🚨 Common Issues & Solutions
|
||||
|
||||
### **Issue: Missing data-i18n attributes**
|
||||
**Solution:** Add `data-i18n="key"` to HTML elements
|
||||
```html
|
||||
<h1 data-i18n="app_title">Password Generator</h1>
|
||||
```
|
||||
|
||||
### **Issue: Hardcoded JavaScript strings**
|
||||
**Solution:** Use `this.t()` method
|
||||
```javascript
|
||||
const message = this.t('copied_to_clipboard');
|
||||
```
|
||||
|
||||
### **Issue: Translation not appearing**
|
||||
**Solution:** Check that:
|
||||
1. Translation key exists in JSON file
|
||||
2. `data-i18n` attribute is correct
|
||||
3. `updateUI()` is called after content changes
|
||||
|
||||
## 📈 Improvement Roadmap
|
||||
|
||||
### **Phase 1: Core UI (Current)**
|
||||
- ✅ Main navigation
|
||||
- ✅ Generator cards
|
||||
- ✅ Button text
|
||||
- ✅ Form labels
|
||||
|
||||
### **Phase 2: API Documentation**
|
||||
- 🔄 Swagger page content
|
||||
- 🔄 Download descriptions
|
||||
- 🔄 Example text
|
||||
|
||||
### **Phase 3: Advanced Features**
|
||||
- ⏳ Error messages
|
||||
- ⏳ Validation text
|
||||
- ⏳ Help tooltips
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
### **Translation Keys**
|
||||
- Use `snake_case` format
|
||||
- Be descriptive but concise
|
||||
- Group related keys with prefixes:
|
||||
- `error_*` for error messages
|
||||
- `button_*` for button text
|
||||
- `label_*` for form labels
|
||||
|
||||
### **String Extraction**
|
||||
- Run tools regularly during development
|
||||
- Check for new hardcoded strings after UI changes
|
||||
- Update translations before releases
|
||||
|
||||
### **Quality Assurance**
|
||||
- Test UI in multiple languages
|
||||
- Check for text overflow in different languages
|
||||
- Verify special characters display correctly
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
1. **Run the extractor:**
|
||||
```bash
|
||||
php extract-translations.php
|
||||
```
|
||||
|
||||
2. **Fix the highest-priority strings first**
|
||||
|
||||
3. **Test in multiple languages:**
|
||||
```bash
|
||||
# Change language in the UI dropdown
|
||||
# Or visit: http://localhost:8080/?lang=de
|
||||
```
|
||||
|
||||
4. **Iterate and improve**
|
||||
|
||||
Your translation management system is now ready to ensure a fully internationalized Password Generator application! 🌍✨
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
$output = [];
|
||||
exec('which pwgen 2>/dev/null', $output, $returnVar);
|
||||
|
||||
var_dump($output, $returnVar);
|
||||
echo "pwgen available: " . ($returnVar === 0 && !empty($output[0]) ? 'yes' : 'no') . "\n";
|
||||
@@ -0,0 +1,723 @@
|
||||
<?php
|
||||
/**
|
||||
* Translation Key Extraction Tool v3.1
|
||||
*
|
||||
* Comprehensive translation key extraction with multiple sources:
|
||||
* 1. Generator API-driven keys (from generator definitions)
|
||||
* 2. JavaScript files ($this.t() calls)
|
||||
* 3. PHP files (static i18n keys)
|
||||
* 4. Backup of existing translation keys
|
||||
*
|
||||
* Output format: JSON only
|
||||
*
|
||||
* Usage:
|
||||
* php bin/extract-translations.php [options]
|
||||
*
|
||||
* Options:
|
||||
* --output=file Output file path
|
||||
* --context Include context information
|
||||
* --template Generate template with placeholders
|
||||
* --language=en Language for template generation
|
||||
* --backup Create backup of existing translations
|
||||
* --merge Merge with existing translations
|
||||
* --js-path=path Path to JavaScript files (default: public/assets/js)
|
||||
* --php-path=path Path to PHP files (default: src)
|
||||
*
|
||||
* Examples:
|
||||
* php bin/extract-translations.php --output=locales/keys.json
|
||||
* php bin/extract-translations.php --backup --merge --output=locales/en.json
|
||||
* php bin/extract-translations.php --context --output=locales/keys-context.json
|
||||
*
|
||||
* @author Password Generator Project
|
||||
* @version 3.1.0
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Managers\PasswordGeneratorManager;
|
||||
|
||||
/**
|
||||
* Main extraction class
|
||||
*/
|
||||
class TranslationKeyExtractor
|
||||
{
|
||||
private PasswordGeneratorManager $generatorManager;
|
||||
private array $collectedKeys = [];
|
||||
private array $keysWithContext = [];
|
||||
private array $existingTranslations = [];
|
||||
private array $stats = [
|
||||
'generator_keys' => 0,
|
||||
'js_keys' => 0,
|
||||
'php_keys' => 0,
|
||||
'existing_keys' => 0,
|
||||
'new_keys' => 0,
|
||||
'obsolete_keys' => 0
|
||||
];
|
||||
|
||||
public function __construct(PasswordGeneratorManager $generatorManager)
|
||||
{
|
||||
$this->generatorManager = $generatorManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all i18n keys from all sources
|
||||
*/
|
||||
public function extractAll(array $options = []): array
|
||||
{
|
||||
$jsPath = $options['js_path'] ?? 'public/assets/js';
|
||||
$phpPath = $options['php_path'] ?? 'src';
|
||||
$existingFile = $options['existing_file'] ?? null;
|
||||
|
||||
echo "=== Extraction Sources ===\n";
|
||||
echo "JavaScript path: {$jsPath}\n";
|
||||
echo "PHP path: {$phpPath}\n";
|
||||
|
||||
if ($existingFile && file_exists($existingFile)) {
|
||||
echo "Existing translations: {$existingFile}\n";
|
||||
$this->loadExistingTranslations($existingFile);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
// Extract from generators (API-driven)
|
||||
echo "=== Extracting from Password Generators ===\n";
|
||||
$this->extractFromGenerators();
|
||||
|
||||
// Extract from JavaScript files
|
||||
echo "\n=== Extracting from JavaScript Files ===\n";
|
||||
$this->extractFromJavaScriptFiles($jsPath);
|
||||
|
||||
// Extract from PHP files
|
||||
echo "\n=== Extracting from PHP Files ===\n";
|
||||
$this->extractFromPhpFiles($phpPath);
|
||||
|
||||
// Compare with existing
|
||||
if (!empty($this->existingTranslations)) {
|
||||
echo "\n=== Comparing with Existing Translations ===\n";
|
||||
$this->compareWithExisting();
|
||||
}
|
||||
|
||||
// Sort keys alphabetically
|
||||
ksort($this->collectedKeys);
|
||||
ksort($this->keysWithContext);
|
||||
|
||||
return [
|
||||
'keys' => $this->collectedKeys,
|
||||
'keysWithContext' => $this->keysWithContext,
|
||||
'stats' => $this->getStatistics(),
|
||||
'existingTranslations' => $this->existingTranslations
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load existing translations for comparison/merging
|
||||
*/
|
||||
private function loadExistingTranslations(string $file): void
|
||||
{
|
||||
try {
|
||||
if (!str_ends_with($file, '.json')) {
|
||||
throw new Exception('Only JSON format is supported');
|
||||
}
|
||||
|
||||
$content = file_get_contents($file);
|
||||
$this->existingTranslations = json_decode($content, true) ?? [];
|
||||
|
||||
// Remove comment keys
|
||||
$this->existingTranslations = array_filter(
|
||||
$this->existingTranslations,
|
||||
fn($key) => !str_starts_with($key, '_comment'),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
|
||||
$this->stats['existing_keys'] = count($this->existingTranslations);
|
||||
echo "✅ Loaded {$this->stats['existing_keys']} existing translations\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "⚠️ Failed to load existing translations: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract keys from password generators using API-driven approach
|
||||
*/
|
||||
private function extractFromGenerators(): void
|
||||
{
|
||||
$generators = $this->generatorManager->getAvailableTypes();
|
||||
|
||||
foreach ($generators as $generator) {
|
||||
$this->extractFromGenerator($generator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract keys from a single generator
|
||||
*/
|
||||
private function extractFromGenerator($generator): void
|
||||
{
|
||||
$generatorId = $generator->getId();
|
||||
echo "📦 {$generatorId}...\n";
|
||||
|
||||
// Get all keys using the generator's built-in method
|
||||
$keys = $generator->getAllI18nKeys();
|
||||
|
||||
// Add keys with context information
|
||||
foreach ($keys as $key) {
|
||||
$this->addKey($key, 'generator', $generatorId);
|
||||
}
|
||||
|
||||
$this->stats['generator_keys'] += count($keys);
|
||||
echo " → Found " . count($keys) . " keys\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract i18n keys from JavaScript files
|
||||
* Looks for: this.t('key'), $this.t('key'), app.t('key')
|
||||
*/
|
||||
private function extractFromJavaScriptFiles(string $basePath): void
|
||||
{
|
||||
if (!is_dir($basePath)) {
|
||||
echo "⚠️ JavaScript path not found: {$basePath}\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$files = $this->findFiles($basePath, ['js']);
|
||||
echo "Found " . count($files) . " JavaScript files\n";
|
||||
|
||||
foreach ($files as $file) {
|
||||
$this->extractFromJavaScriptFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract keys from a single JavaScript file
|
||||
*/
|
||||
private function extractFromJavaScriptFile(string $file): void
|
||||
{
|
||||
$content = file_get_contents($file);
|
||||
$filename = basename($file);
|
||||
|
||||
// Pattern for this.t('key'), $this.t('key'), this.t("key")
|
||||
$patterns = [
|
||||
'/(?:this|\\$this)\\.t\\([\'"]([^\'"]+)[\'"]\\)/',
|
||||
'/app\\.t\\([\'"]([^\'"]+)[\'"]\\)/',
|
||||
'/t\\([\'"]([^\'"]+)[\'"]\\)/', // Simple t() calls
|
||||
];
|
||||
|
||||
$foundKeys = [];
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$foundKeys = array_merge($foundKeys, $matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$foundKeys = array_unique($foundKeys);
|
||||
|
||||
if (!empty($foundKeys)) {
|
||||
echo " 📄 {$filename}: " . count($foundKeys) . " keys\n";
|
||||
|
||||
foreach ($foundKeys as $key) {
|
||||
$this->addKey($key, 'javascript', $filename);
|
||||
}
|
||||
|
||||
$this->stats['js_keys'] += count($foundKeys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract i18n keys from PHP files
|
||||
* Looks for static string keys in specific patterns
|
||||
*/
|
||||
private function extractFromPhpFiles(string $basePath): void
|
||||
{
|
||||
if (!is_dir($basePath)) {
|
||||
echo "⚠️ PHP path not found: {$basePath}\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$files = $this->findFiles($basePath, ['php']);
|
||||
echo "Found " . count($files) . " PHP files\n";
|
||||
|
||||
foreach ($files as $file) {
|
||||
$this->extractFromPhpFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract keys from a single PHP file
|
||||
* Looks for static keys in comments or constants
|
||||
*/
|
||||
private function extractFromPhpFile(string $file): void
|
||||
{
|
||||
$content = file_get_contents($file);
|
||||
$filename = basename($file);
|
||||
|
||||
// Pattern for static keys in comments like: // i18n: key_name
|
||||
$patterns = [
|
||||
'/\\/\\/\\s*i18n:\\s*([a-z0-9_]+)/i',
|
||||
'/\\/\\*\\*.*?@i18n\\s+([a-z0-9_]+).*?\\*\\//s',
|
||||
// Add more patterns as needed
|
||||
];
|
||||
|
||||
$foundKeys = [];
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$foundKeys = array_merge($foundKeys, $matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$foundKeys = array_unique($foundKeys);
|
||||
|
||||
if (!empty($foundKeys)) {
|
||||
echo " 📄 {$filename}: " . count($foundKeys) . " keys\n";
|
||||
|
||||
foreach ($foundKeys as $key) {
|
||||
$this->addKey($key, 'php', $filename);
|
||||
}
|
||||
|
||||
$this->stats['php_keys'] += count($foundKeys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find files with specific extensions recursively
|
||||
*/
|
||||
private function findFiles(string $dir, array $extensions): array
|
||||
{
|
||||
$files = [];
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$ext = strtolower($file->getExtension());
|
||||
if (in_array($ext, $extensions)) {
|
||||
$files[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a key to the collection with context
|
||||
*/
|
||||
private function addKey(string $key, string $source, string $sourceDetail = ''): void
|
||||
{
|
||||
// Initialize with existing translation if available
|
||||
if (!isset($this->collectedKeys[$key])) {
|
||||
$this->collectedKeys[$key] = $this->existingTranslations[$key] ?? '';
|
||||
}
|
||||
|
||||
// Add or update context
|
||||
if (!isset($this->keysWithContext[$key])) {
|
||||
$this->keysWithContext[$key] = [
|
||||
'key' => $key,
|
||||
'sources' => [],
|
||||
'type' => $this->getKeyType($key),
|
||||
'description' => $this->getKeyDescription($key)
|
||||
];
|
||||
}
|
||||
|
||||
// Add source information
|
||||
$this->keysWithContext[$key]['sources'][] = [
|
||||
'type' => $source,
|
||||
'detail' => $sourceDetail
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key type from key pattern
|
||||
*/
|
||||
private function getKeyType(string $key): string
|
||||
{
|
||||
if (str_ends_with($key, '_name')) {
|
||||
return 'generator_metadata';
|
||||
}
|
||||
|
||||
if (str_ends_with($key, '_description')) {
|
||||
return 'generator_metadata';
|
||||
}
|
||||
|
||||
if (str_contains($key, '_option_') && str_ends_with($key, '_label')) {
|
||||
return 'option_label';
|
||||
}
|
||||
|
||||
if (str_contains($key, '_option_') && str_ends_with($key, '_description')) {
|
||||
return 'option_description';
|
||||
}
|
||||
|
||||
if (str_contains($key, '_option_') && str_contains($key, '_choice_')) {
|
||||
return 'option_choice_label';
|
||||
}
|
||||
|
||||
if (str_ends_with($key, '_validation_error')) {
|
||||
return 'validation_error';
|
||||
}
|
||||
|
||||
if (str_contains($key, '_options_group_') && str_ends_with($key, '_title')) {
|
||||
return 'group_title';
|
||||
}
|
||||
|
||||
return 'general';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key description
|
||||
*/
|
||||
private function getKeyDescription(string $key): string
|
||||
{
|
||||
$type = $this->getKeyType($key);
|
||||
|
||||
switch ($type) {
|
||||
case 'generator_metadata':
|
||||
return str_ends_with($key, '_name') ? 'Generator display name' : 'Generator description';
|
||||
|
||||
case 'option_label':
|
||||
preg_match('/_option_(.+)_label$/', $key, $matches);
|
||||
return 'Label for option: ' . ($matches[1] ?? 'unknown');
|
||||
|
||||
case 'option_description':
|
||||
preg_match('/_option_(.+)_description$/', $key, $matches);
|
||||
return 'Description for option: ' . ($matches[1] ?? 'unknown');
|
||||
|
||||
case 'option_choice_label':
|
||||
preg_match('/_option_(.+)_choice_(.+)$/', $key, $matches);
|
||||
return 'Choice label for: ' . ($matches[1] ?? 'unknown') . ' = ' . ($matches[2] ?? 'unknown');
|
||||
|
||||
case 'validation_error':
|
||||
preg_match('/_option_(.+)_validation_error$/', $key, $matches);
|
||||
return 'Validation error for: ' . ($matches[1] ?? 'unknown');
|
||||
|
||||
case 'group_title':
|
||||
preg_match('/_options_group_(.+)_title$/', $key, $matches);
|
||||
return 'Group title for: ' . ($matches[1] ?? 'unknown');
|
||||
|
||||
default:
|
||||
return 'General UI text';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare extracted keys with existing translations
|
||||
*/
|
||||
private function compareWithExisting(): void
|
||||
{
|
||||
$extractedKeys = array_keys($this->collectedKeys);
|
||||
$existingKeys = array_keys($this->existingTranslations);
|
||||
|
||||
$newKeys = array_diff($extractedKeys, $existingKeys);
|
||||
$obsoleteKeys = array_diff($existingKeys, $extractedKeys);
|
||||
|
||||
$this->stats['new_keys'] = count($newKeys);
|
||||
$this->stats['obsolete_keys'] = count($obsoleteKeys);
|
||||
|
||||
if (!empty($newKeys)) {
|
||||
echo "🆕 New keys: " . count($newKeys) . "\n";
|
||||
foreach (array_slice($newKeys, 0, 10) as $key) {
|
||||
echo " + {$key}\n";
|
||||
}
|
||||
if (count($newKeys) > 10) {
|
||||
echo " ... and " . (count($newKeys) - 10) . " more\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($obsoleteKeys)) {
|
||||
echo "\n⚠️ Obsolete keys (not found in code): " . count($obsoleteKeys) . "\n";
|
||||
foreach (array_slice($obsoleteKeys, 0, 10) as $key) {
|
||||
echo " - {$key}\n";
|
||||
}
|
||||
if (count($obsoleteKeys) > 10) {
|
||||
echo " ... and " . (count($obsoleteKeys) - 10) . " more\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extraction statistics
|
||||
*/
|
||||
private function getStatistics(): array
|
||||
{
|
||||
$byType = [];
|
||||
$bySource = [];
|
||||
|
||||
foreach ($this->keysWithContext as $keyData) {
|
||||
$type = $keyData['type'];
|
||||
$byType[$type] = ($byType[$type] ?? 0) + 1;
|
||||
|
||||
foreach ($keyData['sources'] as $source) {
|
||||
$sourceType = $source['type'];
|
||||
$bySource[$sourceType] = ($bySource[$sourceType] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($this->stats, [
|
||||
'total_keys' => count($this->collectedKeys),
|
||||
'by_type' => $byType,
|
||||
'by_source' => $bySource
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create backup of existing translations
|
||||
*/
|
||||
public function createBackup(string $file): bool
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$backupFile = $file . '.backup.' . date('Y-m-d_His');
|
||||
|
||||
if (copy($file, $backupFile)) {
|
||||
echo "💾 Created backup: {$backupFile}\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export keys to JSON format
|
||||
*/
|
||||
public function exportToJson(): string
|
||||
{
|
||||
return json_encode($this->collectedKeys, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export keys with context to JSON format
|
||||
*/
|
||||
public function exportToJsonWithContext(): string
|
||||
{
|
||||
return json_encode([
|
||||
'keys' => $this->collectedKeys,
|
||||
'context' => array_values($this->keysWithContext),
|
||||
'stats' => $this->getStatistics()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate template translation file with placeholders
|
||||
*/
|
||||
public function generateTemplate(string $language = 'en'): string
|
||||
{
|
||||
$template = [];
|
||||
|
||||
$lastGenerator = '';
|
||||
foreach ($this->keysWithContext as $keyData) {
|
||||
$key = $keyData['key'];
|
||||
|
||||
// Add section comments for generators
|
||||
if (str_contains($key, '_') && !str_starts_with($key, '_comment')) {
|
||||
$generatorId = explode('_', $key)[0];
|
||||
if ($generatorId !== $lastGenerator) {
|
||||
$template["_comment_{$generatorId}"] = strtoupper($generatorId) . " Generator";
|
||||
$lastGenerator = $generatorId;
|
||||
}
|
||||
}
|
||||
|
||||
// Use existing translation or generate placeholder
|
||||
$value = $this->existingTranslations[$key] ?? $this->generatePlaceholder($keyData);
|
||||
$template[$key] = $value;
|
||||
}
|
||||
|
||||
return json_encode($template, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate placeholder text for a key
|
||||
*/
|
||||
private function generatePlaceholder(array $keyData): string
|
||||
{
|
||||
$type = $keyData['type'];
|
||||
$key = $keyData['key'];
|
||||
|
||||
// Extract meaningful part from key
|
||||
if (preg_match('/_option_([^_]+)_/', $key, $matches)) {
|
||||
$field = str_replace('_', ' ', $matches[1]);
|
||||
$field = ucwords($field);
|
||||
} else {
|
||||
$field = 'unknown';
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'generator_metadata':
|
||||
return str_ends_with($key, '_name')
|
||||
? 'Generator Name'
|
||||
: 'Generator description text';
|
||||
|
||||
case 'option_label':
|
||||
return $field;
|
||||
|
||||
case 'option_description':
|
||||
return "Description for {$field}";
|
||||
|
||||
case 'option_choice_label':
|
||||
if (preg_match('/_choice_([^_]+)$/', $key, $matches)) {
|
||||
return ucfirst(str_replace('_', ' ', $matches[1]));
|
||||
}
|
||||
return ucfirst($field);
|
||||
|
||||
case 'validation_error':
|
||||
return "Invalid value for {$field}";
|
||||
|
||||
case 'group_title':
|
||||
return $field . " Options";
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI Interface
|
||||
*/
|
||||
class CLI
|
||||
{
|
||||
private array $options = [];
|
||||
|
||||
public function __construct(array $argv)
|
||||
{
|
||||
$this->parseArguments($argv);
|
||||
}
|
||||
|
||||
private function parseArguments(array $argv): void
|
||||
{
|
||||
for ($i = 1; $i < count($argv); $i++) {
|
||||
if (str_starts_with($argv[$i], '--')) {
|
||||
$parts = explode('=', substr($argv[$i], 2), 2);
|
||||
$this->options[$parts[0]] = $parts[1] ?? true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getOption(string $name, $default = null)
|
||||
{
|
||||
return $this->options[$name] ?? $default;
|
||||
}
|
||||
|
||||
public function hasOption(string $name): bool
|
||||
{
|
||||
return isset($this->options[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution
|
||||
*/
|
||||
try {
|
||||
echo "=============================================\n";
|
||||
echo "Translation Key Extraction Tool v3.1\n";
|
||||
echo "JSON Format Only\n";
|
||||
echo "=============================================\n\n";
|
||||
|
||||
$cli = new CLI($argv);
|
||||
|
||||
// Get options
|
||||
$output = $cli->getOption('output');
|
||||
$withContext = $cli->hasOption('context');
|
||||
$template = $cli->hasOption('template');
|
||||
$language = $cli->getOption('language', 'en');
|
||||
$backup = $cli->hasOption('backup');
|
||||
$merge = $cli->hasOption('merge');
|
||||
$jsPath = $cli->getOption('js-path', 'public/assets/js');
|
||||
$phpPath = $cli->getOption('php-path', 'src');
|
||||
|
||||
// Initialize generator manager
|
||||
$generatorManager = new PasswordGeneratorManager();
|
||||
|
||||
// Extract keys
|
||||
$extractor = new TranslationKeyExtractor($generatorManager);
|
||||
|
||||
$extractOptions = [
|
||||
'js_path' => $jsPath,
|
||||
'php_path' => $phpPath
|
||||
];
|
||||
|
||||
// Load existing translations if merging or backing up
|
||||
if (($merge || $backup) && $output && file_exists($output)) {
|
||||
$extractOptions['existing_file'] = $output;
|
||||
|
||||
// Create backup if requested
|
||||
if ($backup) {
|
||||
$extractor->createBackup($output);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $extractor->extractAll($extractOptions);
|
||||
|
||||
// Display statistics
|
||||
echo "\n=== Extraction Statistics ===\n";
|
||||
echo "Total keys: {$result['stats']['total_keys']}\n";
|
||||
echo "Generator keys: {$result['stats']['generator_keys']}\n";
|
||||
echo "JavaScript keys: {$result['stats']['js_keys']}\n";
|
||||
echo "PHP keys: {$result['stats']['php_keys']}\n";
|
||||
|
||||
if ($result['stats']['existing_keys'] > 0) {
|
||||
echo "Existing keys: {$result['stats']['existing_keys']}\n";
|
||||
echo "New keys: {$result['stats']['new_keys']}\n";
|
||||
echo "Obsolete keys: {$result['stats']['obsolete_keys']}\n";
|
||||
}
|
||||
|
||||
echo "\nBy type:\n";
|
||||
foreach ($result['stats']['by_type'] as $type => $count) {
|
||||
echo " - {$type}: {$count}\n";
|
||||
}
|
||||
|
||||
echo "\nBy source:\n";
|
||||
foreach ($result['stats']['by_source'] as $source => $count) {
|
||||
echo " - {$source}: {$count}\n";
|
||||
}
|
||||
|
||||
// Generate output
|
||||
$outputContent = '';
|
||||
|
||||
if ($template) {
|
||||
echo "\n=== Generating template file ===\n";
|
||||
$outputContent = $extractor->generateTemplate($language);
|
||||
} else {
|
||||
echo "\n=== Generating JSON output ===\n";
|
||||
$outputContent = $withContext
|
||||
? $extractor->exportToJsonWithContext()
|
||||
: $extractor->exportToJson();
|
||||
}
|
||||
|
||||
// Save or display
|
||||
if ($output) {
|
||||
file_put_contents($output, $outputContent);
|
||||
echo "✅ Saved to: {$output}\n";
|
||||
} else {
|
||||
echo "\n=== Output ===\n";
|
||||
echo $outputContent;
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo "\n✅ Extraction complete!\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "ERROR: {$e->getMessage()}\n";
|
||||
echo $e->getTraceAsString() . "\n";
|
||||
echo "\nUsage:\n";
|
||||
echo " php bin/extract-translations.php [options]\n\n";
|
||||
echo "Options:\n";
|
||||
echo " --output=file Output file path (JSON only)\n";
|
||||
echo " --context Include context information\n";
|
||||
echo " --template Generate template with placeholders\n";
|
||||
echo " --language=en Language for template generation\n";
|
||||
echo " --backup Create backup of existing translations\n";
|
||||
echo " --merge Merge with existing translations\n";
|
||||
echo " --js-path=path Path to JavaScript files (default: public/assets/js)\n";
|
||||
echo " --php-path=path Path to PHP files (default: src)\n\n";
|
||||
echo "Examples:\n";
|
||||
echo " php bin/extract-translations.php --output=locales/keys.json\n";
|
||||
echo " php bin/extract-translations.php --backup --merge --output=locales/en.json\n";
|
||||
echo " php bin/extract-translations.php --context --output=locales/keys-context.json\n";
|
||||
echo " php bin/extract-translations.php --template --output=locales/en-template.json\n";
|
||||
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Api\OpenApiGenerator;
|
||||
|
||||
$baseUrl = $_ENV['APP_PUBLIC_BASE_URL'] ?? $_ENV['APP_URL'] ?? 'http://localhost:8080';
|
||||
if (isset($argv[1])) {
|
||||
$baseUrl = $argv[1];
|
||||
}
|
||||
|
||||
$destination = __DIR__ . '/../public/pwgenerator-swagger-spec.json';
|
||||
OpenApiGenerator::write($baseUrl, $destination);
|
||||
|
||||
echo "OpenAPI spec generated at {$destination}\n";
|
||||
echo "Base URL: {$baseUrl}\n";
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Used to collect words and their definitions from the Swiss German Slängikon page.
|
||||
https://zuri.net/de/zurich/slängikon.htm
|
||||
*/
|
||||
|
||||
$(document).ready(function() {
|
||||
var output = [];
|
||||
$('h2').each(function() {
|
||||
var heading = $(this).text().trim();
|
||||
var headingElem = this; // Native DOM element
|
||||
var nextSibling = headingElem.nextSibling;
|
||||
var listText = '';
|
||||
while (nextSibling) {
|
||||
if (nextSibling.nodeType === 3) { // Text node
|
||||
var text = nextSibling.textContent.trim();
|
||||
if (text) {
|
||||
listText += text + ' ';
|
||||
}
|
||||
} else if (nextSibling.nodeType === 1 && (nextSibling.tagName === 'HR' || nextSibling.tagName === 'H2')) {
|
||||
break; // Stop at <hr> or next <h2>
|
||||
}
|
||||
nextSibling = nextSibling.nextSibling;
|
||||
}
|
||||
// Process the collected listText
|
||||
if (listText) {
|
||||
var words = listText.split(/ ?\| ?/); // Split by ' | ' or '|'
|
||||
words.forEach(function(word) {
|
||||
word = word.trim();
|
||||
if (!word) return;
|
||||
// Extract content in parentheses at the end
|
||||
var explanation = '';
|
||||
var match = word.match(/\s*\(([^)]+)\)$/);
|
||||
if (match) {
|
||||
explanation = match[1].trim();
|
||||
word = word.replace(/\s*\([^)]+\)$/, '').trim();
|
||||
}
|
||||
// Remove leading articles 'e ', 'es ', 'en '
|
||||
word = word.replace(/^(e |es |en |d |de |s )\b/i, '');
|
||||
// Construct definition: heading + (explanation if present)
|
||||
var definition = heading;
|
||||
if (explanation) {
|
||||
definition += ' (' + explanation + ')';
|
||||
}
|
||||
// Add to output
|
||||
output.push(word + '\t' + definition);
|
||||
});
|
||||
}
|
||||
});
|
||||
// Output the list to console, one word per line
|
||||
if (output.length > 0) {
|
||||
var outputText = output.join('\n');
|
||||
console.log(outputText);
|
||||
// Copy to clipboard
|
||||
navigator.clipboard.writeText(outputText).then(function() {
|
||||
console.log('Output successfully copied to clipboard.');
|
||||
}).catch(function(err) {
|
||||
console.error('Failed to copy to clipboard: ', err);
|
||||
console.log('You can manually copy the output from the console above.');
|
||||
});
|
||||
} else {
|
||||
console.log('No words found. Please ensure jQuery is loaded and verify the page structure.');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,229 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Translation Update Script
|
||||
# Automatically extracts and updates translation strings
|
||||
|
||||
set -e
|
||||
|
||||
echo "🌍 Translation Update Utility"
|
||||
echo "============================="
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
LOCALES_DIR="../locales"
|
||||
TEMPLATES_DIR="../templates"
|
||||
ASSETS_DIR="../assets"
|
||||
SRC_DIR="../src"
|
||||
|
||||
# Create locales directory if it doesn't exist
|
||||
mkdir -p "$LOCALES_DIR"
|
||||
|
||||
echo -e "${BLUE}📁 Scanning for untranslated strings...${NC}"
|
||||
|
||||
# Find all HTML files and extract data-i18n attributes
|
||||
echo -e "${YELLOW}🔍 Scanning HTML templates...${NC}"
|
||||
find $TEMPLATES_DIR -name "*.html" -exec grep -oE 'data-i18n="[^"]*"' {} \; | \
|
||||
sed 's/data-i18n="//g' | sed 's/"//g' | sort | uniq > /tmp/html_keys.txt
|
||||
|
||||
find $TEMPLATES_DIR -name "*.html" -exec grep -oE 'data-i18n-placeholder="[^"]*"' {} \; | \
|
||||
sed 's/data-i18n-placeholder="//g' | sed 's/"//g' | sort | uniq >> /tmp/html_keys.txt
|
||||
|
||||
find $TEMPLATES_DIR -name "*.html" -exec grep -oE 'data-i18n-title="[^"]*"' {} \; | \
|
||||
sed 's/data-i18n-title="//g' | sed 's/"//g' | sort | uniq >> /tmp/html_keys.txt
|
||||
|
||||
# Find all PHP files and extract data-i18n attributes
|
||||
echo -e "${YELLOW}🔍 Scanning PHP templates...${NC}"
|
||||
find $SRC_DIR -name "*.php" -exec grep -oE 'data-i18n="[^"]*"' {} \; | \
|
||||
sed 's/data-i18n="//g' | sed 's/"//g' | sort | uniq > /tmp/php_keys.txt
|
||||
|
||||
find $SRC_DIR -name "*.php" -exec grep -oE 'data-i18n-placeholder="[^"]*"' {} \; | \
|
||||
sed 's/data-i18n-placeholder="//g' | sed 's/"//g' | sort | uniq >> /tmp/php_keys.txt
|
||||
|
||||
find $SRC_DIR -name "*.php" -exec grep -oE 'data-i18n-title="[^"]*"' {} \; | \
|
||||
sed 's/data-i18n-title="//g' | sed 's/"//g' | sort | uniq >> /tmp/php_keys.txt
|
||||
|
||||
# Find JavaScript files and extract this.t() calls
|
||||
echo -e "${YELLOW}🔍 Scanning JavaScript files...${NC}"
|
||||
find $ASSETS_DIR -name "*.js" -exec grep -oE "this\.t\('[^']*'\)" {} \; | \
|
||||
sed "s/this\.t('//g" | sed "s/')//g" | sort | uniq > /tmp/js_keys.txt
|
||||
|
||||
find $ASSETS_DIR -name "*.js" -exec grep -oE 'this\.t\("[^"]*"\)' {} \; | \
|
||||
sed 's/this\.t("//g' | sed 's/")//g' | sort | uniq >> /tmp/js_keys.txt
|
||||
|
||||
# Combine all keys
|
||||
cat /tmp/html_keys.txt /tmp/js_keys.txt /tmp/php_keys.txt | sort | uniq > /tmp/all_keys.txt
|
||||
|
||||
echo -e "${BLUE}📊 Found $(cat /tmp/all_keys.txt | wc -l) unique translation keys${NC}"
|
||||
|
||||
# Function to create/update language file
|
||||
update_language_file() {
|
||||
local lang=$1
|
||||
local file="$LOCALES_DIR/$lang.json"
|
||||
|
||||
echo -e "${YELLOW}📝 Updating $lang.json...${NC}"
|
||||
|
||||
# Load existing translations or create empty object
|
||||
if [ -f "$file" ]; then
|
||||
cp "$file" "/tmp/${lang}_backup.json"
|
||||
existing_keys=$(cat "$file" | jq -r 'keys[]' 2>/dev/null || echo "")
|
||||
else
|
||||
echo "{}" > "$file"
|
||||
existing_keys=""
|
||||
fi
|
||||
|
||||
# Create temporary file with all required keys
|
||||
echo "{" > "/tmp/${lang}_new.json"
|
||||
first=true
|
||||
|
||||
while IFS= read -r key; do
|
||||
if [ ! -z "$key" ]; then
|
||||
# Check if key already exists
|
||||
existing_value=$(cat "$file" | jq -r ".[\"$key\"] // empty" 2>/dev/null)
|
||||
|
||||
if [ ! -z "$existing_value" ] && [ "$existing_value" != "null" ]; then
|
||||
# Keep existing translation
|
||||
value="$existing_value"
|
||||
else
|
||||
# Add new key with placeholder or translation
|
||||
case $lang in
|
||||
"en")
|
||||
# Convert snake_case to human readable
|
||||
value=$(echo "$key" | sed 's/_/ /g' | sed 's/\b\(.\)/\u\1/g')
|
||||
;;
|
||||
"de")
|
||||
case $key in
|
||||
"generate") value="Generieren" ;;
|
||||
"copy") value="Kopieren" ;;
|
||||
"language") value="Sprache" ;;
|
||||
"options") value="Optionen" ;;
|
||||
"password_generator") value="Passwort-Generator" ;;
|
||||
"api_documentation") value="API-Dokumentation" ;;
|
||||
"loading") value="Wird geladen..." ;;
|
||||
"error") value="Fehler" ;;
|
||||
"success") value="Erfolgreich" ;;
|
||||
"download") value="Herunterladen" ;;
|
||||
"back") value="Zurück" ;;
|
||||
"home") value="Startseite" ;;
|
||||
"settings") value="Einstellungen" ;;
|
||||
*) value="$(echo "$key" | sed 's/_/ /g' | sed 's/\b\(.\)/\u\1/g') (DE)" ;;
|
||||
esac
|
||||
;;
|
||||
"fr")
|
||||
case $key in
|
||||
"generate") value="Générer" ;;
|
||||
"copy") value="Copier" ;;
|
||||
"language") value="Langue" ;;
|
||||
"options") value="Options" ;;
|
||||
"password_generator") value="Générateur de mots de passe" ;;
|
||||
"api_documentation") value="Documentation API" ;;
|
||||
"loading") value="Chargement..." ;;
|
||||
"error") value="Erreur" ;;
|
||||
"success") value="Succès" ;;
|
||||
"download") value="Télécharger" ;;
|
||||
"back") value="Retour" ;;
|
||||
"home") value="Accueil" ;;
|
||||
"settings") value="Paramètres" ;;
|
||||
*) value="$(echo "$key" | sed 's/_/ /g' | sed 's/\b\(.\)/\u\1/g') (FR)" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
value="$(echo "$key" | sed 's/_/ /g' | sed 's/\b\(.\)/\u\1/g') (TODO: $lang)"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Add to JSON
|
||||
if [ "$first" = true ]; then
|
||||
first=false
|
||||
else
|
||||
echo "," >> "/tmp/${lang}_new.json"
|
||||
fi
|
||||
|
||||
# Escape quotes in value
|
||||
escaped_value=$(echo "$value" | sed 's/"/\\"/g')
|
||||
echo " \"$key\": \"$escaped_value\"" >> "/tmp/${lang}_new.json"
|
||||
fi
|
||||
done < /tmp/all_keys.txt
|
||||
|
||||
echo "" >> "/tmp/${lang}_new.json"
|
||||
echo "}" >> "/tmp/${lang}_new.json"
|
||||
|
||||
# Format JSON nicely
|
||||
cat "/tmp/${lang}_new.json" | jq '.' > "$file"
|
||||
|
||||
# Count translations
|
||||
total_keys=$(cat /tmp/all_keys.txt | wc -l)
|
||||
translated_keys=$(cat "$file" | jq -r 'to_entries[] | select(.value | test("\\(TODO:|\\(DE\\)|\\(FR\\)|\\(IT\\)|\\(ES\\)|\\(JA\\)|\\(ZH\\))") | not) | .key' | wc -l)
|
||||
|
||||
echo -e "${GREEN}✅ Updated $lang.json: $translated_keys/$total_keys translated${NC}"
|
||||
}
|
||||
|
||||
# Update all language files
|
||||
languages=("en" "de" "fr" "it" "es" "ja" "zh")
|
||||
|
||||
for lang in "${languages[@]}"; do
|
||||
update_language_file "$lang"
|
||||
done
|
||||
|
||||
# Generate report
|
||||
echo ""
|
||||
echo -e "${BLUE}📊 Translation Status Report${NC}"
|
||||
echo "============================"
|
||||
|
||||
for lang in "${languages[@]}"; do
|
||||
file="$LOCALES_DIR/$lang.json"
|
||||
if [ -f "$file" ]; then
|
||||
total=$(cat /tmp/all_keys.txt | wc -l)
|
||||
translated=$(cat "$file" | jq -r 'to_entries[] | select(.value | test("\\(TODO:|\\(DE\\)|\\(FR\\)|\\(IT\\)|\\(ES\\)|\\(JA\\)|\\(ZH\\))") | not) | .key' | wc -l 2>/dev/null || echo "0")
|
||||
percentage=$((translated * 100 / total))
|
||||
|
||||
echo -e "$lang: $translated/$total ($percentage%) translated"
|
||||
fi
|
||||
done
|
||||
|
||||
# Find hardcoded strings that need data-i18n attributes
|
||||
echo ""
|
||||
echo -e "${YELLOW}🔍 Finding hardcoded strings that need localization...${NC}"
|
||||
|
||||
# Check HTML files for hardcoded text
|
||||
echo -e "${BLUE}HTML files with potential hardcoded text:${NC}"
|
||||
find $TEMPLATES_DIR -name "*.html" -exec grep -l '[>][A-Z][^<]*[a-z][^<]*[<]' {} \; | while read file; do
|
||||
echo " 📄 $file"
|
||||
grep -oE '>[A-Z][^<]*[a-z][^<]*<' "$file" | sed 's/^>//; s/<$//' | head -5 | while read text; do
|
||||
if [[ ! "$text" =~ ^(GET|POST|PUT|DELETE|HTTP|JSON|API|URL|ID|OK|CSS|JS|HTML)$ ]]; then
|
||||
echo " → \"$text\" (needs data-i18n)"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Check JavaScript files for hardcoded strings
|
||||
echo -e "${BLUE}JavaScript files with potential hardcoded text:${NC}"
|
||||
find $ASSETS_DIR -name "*.js" -exec grep -l "'[A-Z][^']*[a-z][^']*'" {} \; | while read file; do
|
||||
echo " 📄 $file"
|
||||
grep -oE "'[A-Z][^']*[a-z][^']*'" "$file" | sed "s/^'//; s/'$//" | head -5 | while read text; do
|
||||
if [[ ! "$text" =~ ^(GET|POST|PUT|DELETE|HTTP|JSON|API|URL|ID|OK|CSS|JS|HTML|Content-Type|application/json)$ ]]; then
|
||||
echo " → \"$text\" (needs this.t())"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Generate migration instructions
|
||||
echo ""
|
||||
echo -e "${GREEN}📋 Next Steps:${NC}"
|
||||
echo "1. Review the generated translation files in $LOCALES_DIR/"
|
||||
echo "2. Replace placeholder translations (TODO: lang) with proper translations"
|
||||
echo "3. Add data-i18n attributes to hardcoded HTML strings"
|
||||
echo "4. Replace hardcoded JavaScript strings with this.t() calls"
|
||||
echo "5. Test the application in different languages"
|
||||
|
||||
# Clean up temporary files
|
||||
rm -f /tmp/html_keys.txt /tmp/js_keys.txt /tmp/all_keys.txt /tmp/*_new.json
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ Translation update completed!${NC}"
|
||||
@@ -0,0 +1,156 @@
|
||||
# Wiktionary Random Word Generator - Verfeinerte Version
|
||||
|
||||
Eine **hochoptimierte Version** des Wiktionary Random Word Generators mit verbesserter Definition-Extraktion und erweiterten Filteroptionen.
|
||||
|
||||
## 🔧 Neue Verfeinerungen
|
||||
|
||||
### Saubere Definition-Extraktion
|
||||
- **Entfernt führende Nummerierungen**: `1]`, `[2]`, `3)` werden automatisch entfernt
|
||||
- **Bereinigt Formatierungen**: Entfernt `:`, `•`, `>`, `(`, `"` am Anfang
|
||||
- **Intelligente Kleinschreibung**: Definitionen beginnen mit Kleinbuchstaben
|
||||
- **Bessere Lesbarkeit**: Cleaner, natürlicher Text ohne Wiki-Markup
|
||||
|
||||
### Zahlen-Filter (Neu)
|
||||
- **`--exclude-numbers` Option**: Schließt Wörter mit Ziffern aus
|
||||
- **Intelligente Erkennung**: Filtert `5-mal`, `Jahr2024`, `3D-Drucker`
|
||||
- **Erhält Wörter**: `dreimal`, `hundert`, `eins` bleiben erlaubt
|
||||
- **Flexible Nutzung**: Optional aktivierbar per Parameter
|
||||
|
||||
## 📋 Verwendung
|
||||
|
||||
### Standard (Empfohlen)
|
||||
```bash
|
||||
# 50 deutsche Wörter mit sauberen Definitionen
|
||||
python wiktionary_random_refined.py -c 50 -l de
|
||||
```
|
||||
|
||||
### Mit Zahlen-Ausschluss
|
||||
```bash
|
||||
# Keine Wörter mit Ziffern (5-mal, Jahr2024, etc.)
|
||||
python wiktionary_random_refined.py -c 30 --exclude-numbers
|
||||
|
||||
# Kombiniert mit Wortart-Filter
|
||||
python wiktionary_random_refined.py -c 25 --include-pos verb adjektiv --exclude-numbers
|
||||
```
|
||||
|
||||
### Erweiterte Kombinationen
|
||||
```bash
|
||||
# Englische Wörter, Namen erlaubt, aber keine Zahlen
|
||||
python wiktionary_random_refined.py -c 40 -l en --include-names --exclude-numbers
|
||||
|
||||
# Nur Substantive und Verben, ohne Zahlen
|
||||
python wiktionary_random_refined.py -c 35 --include-pos substantiv verb --exclude-numbers
|
||||
```
|
||||
|
||||
## 🎯 Definition-Bereinigung (Beispiele)
|
||||
|
||||
### Vorher → Nachher
|
||||
```
|
||||
1] eine große Pflanze mit Stamm und Ästen
|
||||
→ eine große Pflanze mit Stamm und Ästen
|
||||
|
||||
[2] übertragen: etwas Großes und Stabiles
|
||||
→ übertragen: etwas Großes und Stabiles
|
||||
|
||||
: [1] positive Eigenschaft oder Zustand
|
||||
→ positive Eigenschaft oder Zustand
|
||||
|
||||
• erste Bedeutung: schön anzusehen
|
||||
→ erste Bedeutung: schön anzusehen
|
||||
|
||||
(1) Gebäude zum Wohnen
|
||||
→ gebäude zum Wohnen
|
||||
```
|
||||
|
||||
## 🔢 Zahlen-Filter (Beispiele)
|
||||
|
||||
### Ausgeschlossen (mit --exclude-numbers)
|
||||
- `5-mal` → ❌ (enthält Ziffer)
|
||||
- `Jahr2024` → ❌ (enthält Ziffer)
|
||||
- `3D-Drucker` → ❌ (enthält Ziffer)
|
||||
- `42fach` → ❌ (enthält Ziffer)
|
||||
|
||||
### Erlaubt
|
||||
- `dreimal` → ✅ (keine Ziffern)
|
||||
- `hundert` → ✅ (ausgeschrieben)
|
||||
- `eins` → ✅ (ausgeschrieben)
|
||||
- `Baum` → ✅ (keine Zahlen)
|
||||
|
||||
## 🔧 Alle Parameter
|
||||
|
||||
| Parameter | Kurz | Beschreibung | Standard |
|
||||
|-----------|------|--------------|----------|
|
||||
| `--count` | `-c` | Anzahl Wörter | 50 |
|
||||
| `--language` | `-l` | Sprache (de/en/fr/es/it) | de |
|
||||
| `--output` | `-o` | Output-Datei | refined_wordlist.csv |
|
||||
| `--include-pos` | - | Wortarten-Filter | adjektiv verb substantiv |
|
||||
| `--include-names` | - | Namen einschließen | False |
|
||||
| `--exclude-numbers` | - | **NEU**: Zahlen ausschließen | False |
|
||||
| `--verbose` | `-v` | Ausführliche Ausgabe | - |
|
||||
|
||||
## 📊 Beispiel-Output
|
||||
|
||||
```
|
||||
🎲 Wiktionary Random Word Generator (Verfeinerte Version)
|
||||
===========================================================================
|
||||
Sprache: DE
|
||||
Anzahl: 25
|
||||
Wortarten: adjektiv, verb, substantiv (Standard)
|
||||
Namen einschließen: Nein
|
||||
Zahlen ausschließen: Ja
|
||||
Output: refined_wordlist.csv
|
||||
===========================================================================
|
||||
|
||||
Sammle Wörter aus Kategorien: adjektiv, verb, substantiv
|
||||
Lade adjektiv aus Kategorie:Adjektiv (Deutsch)...
|
||||
→ 167 Wörter gefunden
|
||||
Lade verb aus Kategorie:Verb (Deutsch)...
|
||||
→ 234 Wörter gefunden
|
||||
Lade substantiv aus Kategorie:Substantiv (Deutsch)...
|
||||
→ 445 Wörter gefunden
|
||||
Gesamt gesammelt: 846 Wörter
|
||||
|
||||
Verarbeite Kandidaten...
|
||||
1/25: schön
|
||||
2/25: laufen
|
||||
3/25: Baum
|
||||
[...]
|
||||
25/25: denken
|
||||
|
||||
✅ 25 Wörter erfolgreich generiert!
|
||||
💾 Wortliste gespeichert in: refined_wordlist.csv
|
||||
|
||||
📖 Beispiele aus der generierten Liste:
|
||||
-----------------------------------------------------------------
|
||||
1. schön → ansprechend und angenehm in der Erscheinung...
|
||||
2. laufen → sich zu Fuß mit schnellen Schritten fortbewegen...
|
||||
3. Baum → großes, meist mehrjähriges Gewächs mit Stamm...
|
||||
4. intelligent → geistig begabt, klug und aufgeweckt...
|
||||
5. sprechen → mündlich kommunizieren, Worte äußern...
|
||||
... und 20 weitere Wörter
|
||||
```
|
||||
|
||||
## 🚀 Verbesserungen gegenüber vorherigen Versionen
|
||||
|
||||
### Definition-Qualität
|
||||
1. **Sauberer Text**: Keine störenden `1]`, `[2]` Präfixe mehr
|
||||
2. **Natürliche Sprache**: Definitionen lesen sich flüssiger
|
||||
3. **Konsistente Formatierung**: Einheitlicher Stil ohne Wiki-Markup
|
||||
4. **Bessere UX**: Professionellere, verwendbare Ausgabe
|
||||
|
||||
### Filter-Präzision
|
||||
1. **Zahlen-Kontrolle**: Optionaler Ausschluss numerischer Begriffe
|
||||
2. **Flexible Konfiguration**: Alle Filter kombinierbar
|
||||
3. **Intelligente Erkennung**: Unterscheidet Ziffern vs. ausgeschriebene Zahlen
|
||||
4. **Mehrschichtiger Ansatz**: Basis- + Kategorie- + Zahlen-Filter
|
||||
|
||||
### Performance-Optimierungen
|
||||
1. **Effizientere Regex**: Optimierte Pattern für Definition-Bereinigung
|
||||
2. **Reduzierte API-Calls**: Weniger Kategorien-Checks durch Vorfilterung
|
||||
3. **Bessere Kandidaten-Auswahl**: Höhere Erfolgsquote bei finaler Auswahl
|
||||
|
||||
Das verfeinerte Script ist besonders geeignet für:
|
||||
- **Professionelle Anwendungen**: Saubere, verwendbare Definitionen
|
||||
- **Sprachlernen**: Klare, verständliche Erklärungen ohne Formatierungs-Chaos
|
||||
- **Datenqualität**: Konsistente, strukturierte Ausgabe
|
||||
- **Flexible Filterung**: Präzise Kontrolle über Inhaltstypen
|
||||
@@ -0,0 +1,418 @@
|
||||
meiden transitiv, gehoben: etwas dafür tun, einer Person oder Sache nicht zu begegnen
|
||||
sturmgepeitscht gehoben: vom Sturm heftig bewegt, aufgewirbelt und/oder geschleudert werdend
|
||||
Tonkünstlerin gehoben, Musik: Künstlerin, die musikalische Werke schafft
|
||||
duress gehoben, meist Rechtssprache: Nötigung, Zwang
|
||||
Wohlgestalt gehoben veraltend: schöne, ansprechende Gestalt
|
||||
Gezweig gehoben: Gesamtheit aller Zweige (einer Hecke, eines Baumes oder Ähnlichem)
|
||||
hinabwerfen transitiv; gehoben: von (hier) oben nach (dort) unten werfen (weg vom Sprechenden)
|
||||
Verblichener gehoben: männliche Person, die gestorben ist
|
||||
aufkündigen einen Vertrag/eine Vertragsbeziehung beenden
|
||||
straßauf, straßab adverbiell; gehoben: auf/in vielen Straßen, durch mehrere Straßen
|
||||
entströmen intransitiv, unpersönlich, gehoben: aus etwas in Strömen herauskommen
|
||||
durchkreuzen transitiv: Wörter, Buchstaben in einem Text mit Kreuzen (×) unleserlich, ungültig machen
|
||||
nimmermüde gehoben: nicht ermüdend, unermüdlich
|
||||
Heermeister gehoben oder veraltet: Oberbefehlshaber (eines Teils) der Streitkräfte, eines Heeres
|
||||
Frieden kein Plural: vertraglich gesichertes Miteinander verschiedener Staaten, das durch Abwesenheit von Gewalt (und speziell von Krieg) gekennzeichnet ist
|
||||
späterhin gehoben: zu einem Zeitpunkt, der nach einem Ereignis liegt, auf etwas folgt, noch in der Zukunft liegt
|
||||
Stachel im Fleisch gehoben: immerwährende eindringliche Erinnerung an etwas
|
||||
Tondichterin gehoben, Musik: Künstlerin, die musikalische Werke schafft
|
||||
Verstorbene gehoben: weibliche Person, die nicht mehr am Leben, sondern tot ist
|
||||
übersonnt gehoben; auch übertragen: von der Sonne hell erleuchtet, von Sonnenstrahlen beschienen
|
||||
engen transitiv, selten, gehoben: etwas schmäler machen
|
||||
hungrig ein Gefühl des Hungers verspürend, Hunger habend
|
||||
Dotation gehoben: finanzielle und/oder materielle Zuwendung
|
||||
emporgehen nach oben gehen
|
||||
verstummen intransitiv, von Personen: (plötzlich) aufhören, die Stimme zu gebrauchen
|
||||
unflätig gehoben, abwertend: so, dass es nicht den üblichen Regeln des guten Benehmens oder Anstandes entspricht
|
||||
Widerfahrnis veraltet, gehoben, sonst fachsprachlich, Psychologie: dasjenige (nicht Beeinflussbare), was (einem, jemandem) widerfährt
|
||||
Fittich poetisch, gehoben: Körperteil, das einem Tier das Fliegen ermöglicht
|
||||
Trottoir österreichisch gehoben, schweizerisch, süddeutsch, sonst veraltend oder landschaftlich: (durch eine Bordsteinkante von der Fahrbahn abgegrenzter, erhöhter) Weg für Fußgänger und Fußgängerinnen, der entlang einer Straße führt
|
||||
durchpflügen transitiv: eine Fläche sehr stark und gründlich mit dem Pflug bearbeiten
|
||||
in Flammen aufgehen gehoben: durch Feuer vollständig zerstört werden, verbrennen
|
||||
ekel gehoben, sonst veraltend: (moralisch oder physisch) Ekel erregend
|
||||
Bildnerin veraltet: weibliche Person, die eine Person oder Sache formt, ausbildet
|
||||
guter Dinge in einer heiteren, vergnügten Stimmung
|
||||
bisweilen gehoben: unterschiedlich häufig, mehr oder minder oft
|
||||
herniederbrennen intransitiv, gehoben, Hilfsverb haben, von der Sonne: große Hitze, gleißendes Licht nach unten ausstrahlen
|
||||
beargwöhnen gehoben: misstrauisch betrachten
|
||||
partizipieren gehoben: an etwas teilhaben; an etwas seinen Anteil haben
|
||||
zurücksenden transitiv; gehoben: etwas wieder an einen bestimmten Ort/zu einer bestimmten Person gelangen lassen
|
||||
dahinwelken intransitiv; gehoben: vom blühenden in einen welken Zustand übergehen
|
||||
anbequemen reflexiv, veraltend, sonst gehoben: sich mit seinem Verhalten, seinen Handlungen an jemandem oder etwas orientieren, sich danach richten
|
||||
epochal gehoben: von großer Bedeutung, bedeutsam für eine Epoche
|
||||
heimführen jemanden in dessen Zuhause bringen
|
||||
ausharren intransitiv; gehoben: (trotz Widrigkeiten) mit Geduld abwarten, warten
|
||||
emporwirbeln transitiv, Hilfsverb haben: kleine Teilchen (zum Beispiel Sand, Schnee) so in Bewegung versetzen, dass sie wild drehend, ungeordnet nach oben fliegen
|
||||
pasteur gehoben: Hirt, Senner
|
||||
Landesvater gehoben, historisch: Fürst eines Landes
|
||||
Tonschöpfer gehoben, Musik: Künstler, der musikalische Werke schafft
|
||||
zurückbegehren transitiv, gehoben: etwas wieder in seinem Besitz, bei sich haben wollen
|
||||
Tonmaler gehoben, Musik: Künstler, der musikalische Werke schafft
|
||||
erfließen österreichisch, Amtssprache, gehoben: daraus folgen, sich ergeben, entstehen
|
||||
einmütig gehoben: hinsichtlich der Meinung übereinstimmend, völlig gleich
|
||||
Lustbarkeit gehoben, veraltend: Veranstaltung, die jemand zur Unterhaltung, zur Vergnügung besucht; allgemein auch: etwas, was Freude bereiten soll
|
||||
Ansinnen gehoben: oft als Zumutung empfundenes Ersuchen, Gesuch, Bitte
|
||||
Dämmer gehoben: (Tages-)Licht, das allmählich hell beziehungsweise dunkel wird
|
||||
hinabgehen intransitiv, gehoben: nach unten gehen
|
||||
wohlauf gehoben: gesund, in guter Verfassung
|
||||
hinausschauen regional: aus einem bestimmten Bereich, von einer bestimmten Stelle (hier drinnen) in einen anderen Bereich, an eine andere Stelle (dort draußen) blicken (schauen) (in Richtung vom Sprecher weg)
|
||||
äquivalent gehoben: gleichwertig, entsprechend
|
||||
Anverwandter gehoben: Person, die zur Verwandtschaft gehört
|
||||
den Tod finden gehoben: infolge von etwas (Unfall, Krankheit, Ermordung oder dergleichen) aufhören zu leben
|
||||
mit doppelter Zunge sprechen gehoben: Unwahres oder Widersprüchliches sagen; heuchlerisch reden; fuchsschwänzen
|
||||
Verelendung gehoben: das Geraten in sehr schlechte Lebensumstände
|
||||
obzwar gehoben, veraltend: leitet einen Konzessivsatz ein, der ein Gegenargument gegen das zuvor Gesagte enthält; obwohl
|
||||
Bacchus huldigen gehoben, euphemistisch: Wein trinken
|
||||
Edelmut gehoben: edle Gesinnung, die egoistische Motive hintenan stellt
|
||||
hoffärtig gehoben abwertend: besonders hochmütig, andere Personen verletzend
|
||||
sonach gehoben: eine Folgerung einleitend: folglich, also
|
||||
Todespein gehoben: tödliche Qualen, Schmerzen
|
||||
weiterreichen transitiv, gehoben: etwas, das man von jemandem erhalten hat, einem anvertraut wurde, einem anderen (händisch) geben, in dessen Verantwortung überstellen
|
||||
Mund Anatomie: Öffnung im unteren Teil des (hauptsächlich menschlichen) Gesichtes, hauptsächlich zur Nahrungsaufnahme und zur Lautbildung genutzt
|
||||
Notschrei gehoben, veraltend: lauter Ruf nach Hilfe aus einer Notsituation
|
||||
nicken intransitiv: (zur Bejahung, Zustimmung, als Geste des Verstehens, als Gruß oder dergleichen) den Kopf kurz hintereinander senken und heben
|
||||
herabkommen intransitiv, gehoben: (von dort oben) nach (hier) unten kommen (in Richtung zum Sprechenden)
|
||||
voranschreiten vor jemand anders gehen
|
||||
Anverwandte gehoben: weibliche Person, die zur Verwandtschaft gehört
|
||||
betören gehoben: jemanden faszinieren, in sich verliebt machen
|
||||
Unnatur gehoben: etwas, das der Natur/der natürlichen Veranlagung nicht gemäß ist
|
||||
winden transitiv: etwas drehen, wickeln
|
||||
durchtosen transitiv, gehoben, veraltend: mit heftiger, wilder Bewegung und dröhnendem Geräusch durchqueren
|
||||
feilbieten transitiv, gehoben: etwas zum Kauf anbieten
|
||||
nachgerade umgangssprachlich, mit Betonung auf Ende/Konsequenz eines Geschehens/Prozesses: endlich, letztlich
|
||||
edelgeboren veraltet gehoben: von vornehmer/adliger Herkunft
|
||||
mit gespaltener Zunge reden gehoben: Unwahres oder Widersprüchliches sagen, heuchlerisch reden, fuchsschwänzen
|
||||
emporarbeiten sich unter Anstrengungen, Mühen von einer unten gelegenen Stelle nach oben bewegen (vom Sprecher weg)
|
||||
anfliegen in einem Luftfahrzeug, mit Flügeln, durch die Luft näher kommen
|
||||
auserlesen gehoben: qualitativ besonders gut/hochwertig
|
||||
goldig selten, gehoben: die Farbe und den Glanz von Gold habend (auch durch Vergolden)
|
||||
fernhin gehoben: in weitem Umkreis, bis in weite Ferne
|
||||
Abgeschiedener gehoben: (kürzlich) verstorbene (männliche) Person
|
||||
Wehe veraltet gehoben: seelischer Schmerz
|
||||
Beilager gehoben veraltet: sexuelle Vereinigung
|
||||
antizipieren gehoben, bildungssprachlich: etwas erkennen, bevor es eingetreten ist
|
||||
mit gespaltener Zunge sprechen gehoben: Unwahres oder Widersprüchliches sagen; heuchlerisch reden; fuchsschwänzen
|
||||
Trottoirfläche österreichisch gehoben, schweizerisch, süddeutsch, sonst veraltend oder landschaftlich: befestigte und meist gepflasterte Fläche am Rand einer Straße, die für Fußgänger vorgesehen ist
|
||||
Kronjuwel kostbares Schmuckstück, das sich im Besitz des Herrscherhauses befindet
|
||||
gürten gehoben: jemandem/sich einen Gürtel oder eine andere Art Riemen oder Band umlegen, umschnallen; etwas mit einem Gürtel oder Ähnlichem versehen
|
||||
leichten Herzens gehoben: ohne dass es jemandem Anstrengung, Sorgen bereitet, jemanden Überwindung kostet, ohne Bedenken, Zweifel; sehr gern; völlig unbekümmert
|
||||
ersterben aufhören
|
||||
empordienen reflexiv, gehoben: sich durch Fleiß, beständiges Handeln/Tätigsein im Beruf oder Ähnliches nach und nach stufenweise in eine höhergestellte Position bringen
|
||||
Augenweite gehoben oder veraltet: mit den Augen einsehbare Entfernung
|
||||
aufblühen intransitiv: sich blühend öffnen, die Blüten (langsam) auseinanderfalten; anfangen, sich zu voller Blüte zu entfalten, allmählich in Blüte kommen
|
||||
abdarben transitiv, gehoben, sich etwas abdarben: sich etwas (bis zum Grad des Notleidens) entziehen (um es für Anderweitiges aufzusparen)
|
||||
tagen gehoben: Tag werden
|
||||
anreihen transitiv: (etwas) einer bereits vorhandenen Reihe hinzufügen
|
||||
aufbrechen auch bildlich: etwas, das verschlossen ist, mit Gewalt öffnen
|
||||
geizen mit der Präposition „mit“: in übertriebener, unangemessener Weise sparen, geizig mit etwas sein
|
||||
umfriedigen gehoben, selten: einrahmend, abgrenzend umgeben
|
||||
niederstrecken transitiv; gehoben, auch übertragen: einen Menschen oder ein Tier schwer verletzen oder töten und so zu Boden bringen
|
||||
ausgleiten intransitiv; auch übertragen: auf einer glatten Fläche aus dem Gleichgewicht kommen, keinen Halt finden (und hinfallen)
|
||||
distinguiert gehoben: vornehm; sich durch betont gepflegtes Auftreten oder Ähnliches von anderen abhebend
|
||||
Kunde jemand, der bei einem bestimmten Geschäft einkauft; der Käufer einer Ware; derjenige, der eine Dienstleistung in Anspruch nimmt; jeder, der für etwas zahlt (auch wenn die Leistung an einen Dritten geht)
|
||||
Allgütiger gehoben: derjenige, der allgütig ist: Gott
|
||||
Gemahl gehoben: der vermählte Mann
|
||||
die Hülle und Fülle gehoben: in großer Menge, im Überfluss
|
||||
Freveltat gehoben: einzelne Handlung, die einen Frevel gegen etwas darstellt; frevelhafte Tat
|
||||
beschatten gehoben: jemandem/etwas einen Schatten verleihen
|
||||
schmachbeladen gehoben: sehr erniedrigt, in seiner Würde stark herabgesetzt, in seinem Stolz sehr verletzt, Schande auf sich geladen habend
|
||||
entschwinden intransitiv, gehoben: sich von etwas entfernen
|
||||
allein als Adjektiv: für sich, getrennt, einsam, einzeln sein; nur; ohne Beistand, Zeugen, Anwesenheit von anderen Personen, ohne Hilfe sein
|
||||
zurückverlangen von jemandem die Rückgabe von etwas fordern, das einem gehört
|
||||
ergötzlich gehoben: in unterhaltsamer Art und Weise Gefallen/Vergnügen bereitend
|
||||
erkühnen reflexiv, gehoben: so wagemutig und frech (kühn) sein, dass man etwas eigentlich Unerlaubtes/Unzulässiges versucht
|
||||
dergestalt gehoben: derart, dermaßen
|
||||
anfechten die Gültigkeit/Rechtmäßigkeit einer Entscheidung anzweifeln und dann dagegen vorgehen
|
||||
über Gebühr gehoben: über das angemessene Maß hinausgehend, mehr als erforderlich
|
||||
Hall gehoben: akustisch wahrnehmbares, unspezifisches Ereignis
|
||||
fortrühren gehoben, reflexiv, meistens verneint: sich wegbewegen; sich vom eigenen Aufenthaltsort entfernen
|
||||
Inbrunst gehoben: starke Hingabe an ein Gefühl oder an ein Vorhaben
|
||||
anspeien transitiv, gehoben: jemanden oder etwas (absichtlich) mit Spucke oder Erbrochenem beschmutzen
|
||||
Begräbnisstätte gehoben: Stelle, wo jemand begraben liegt
|
||||
Land der Morgenröte poetisch, gehoben: Bezeichnung und Selbstbezeichnung für China
|
||||
Beute durch Diebstahl, Raub oder Plünderung angeeignete Güter
|
||||
Diskutant gehoben: Person, die sich an einer Diskussion beteiligt
|
||||
bedürfen gehoben, mit Genitiv: etwas benötigen, etwas brauchen, auf etwas angewiesen sein
|
||||
entsühnen transitiv, auch reflexiv, gehoben: jemanden, sich durch Sühne von Sünde, Schuld befreien
|
||||
Degout gehoben: starke Abneigung gegen jemanden oder etwas
|
||||
entzünden transitiv: in Brand setzen
|
||||
Quisquilie gehoben, meist Plural: Kleinigkeit, Nichtigkeit, Bagatelle
|
||||
äffen transitiv, gehoben, veraltete Bedeutung: jemanden durch Unwahrheiten irreführen, täuschen
|
||||
hernieder gehoben: aus der Richtung von irgendwo oben nach unten, meist zum Sprecher, Erzähler oder Handelnden hin
|
||||
so weit das Auge reicht gehoben: so weit man blicken/sehen kann; überall
|
||||
einstmals gehoben: früher, vor langer Zeit
|
||||
sinnesfroh gehoben: mit Sinnenfreude; sich am sinnlichen Genuss erfreuend
|
||||
nachforschen intransitiv: sich intensiv mit dem Sammeln von Informationen über etwas beschäftigen
|
||||
abspeisen transitiv, umgangssprachlich: jemandes Hunger stillen
|
||||
umfrieden gehoben: einrahmend, abgrenzend umgeben
|
||||
Wegesrand gehoben: seitliche Grenze eines Weges
|
||||
rapide gehoben, oft bildlich: äußerst schnell
|
||||
nachkommen intransitiv: jemandem, einer Sache nach einiger Zeit folgen; einige Zeit später hinterherkommen; auf etwas (später) als Konsequenz folgen
|
||||
zuwider gehoben, jemandem ist etwas zuwider: Ablehnung und unangenehme Gefühle hervorrufend
|
||||
divinatorisch gehoben: die Fähigkeit zur Voraussage von Ereignissen besitzend
|
||||
wohlgemut gehoben: heiter gestimmt
|
||||
Affront gehoben: öffentlicher verbaler oder schriftlicher Angriff bzw. Beschuldigung
|
||||
zurücksehnen reflexiv: wieder an einen bestimmten Ort/in eine bestimmte Zeit/zu einer bestimmten Person zurückversetzt sein wollen
|
||||
zuinnerst sehr tief in der Gedanken- und Gefühlswelt einer Person
|
||||
zu Tode kommen gehoben: infolge von Unfall, Krankheit, Ermordung oder dergleichen aufhören zu leben
|
||||
erwürgen jemanden ermorden, indem man ihm (mit den Händen) den Hals zusammendrückt, um die Luftzufuhr abzuschneiden, sodass das Blut nicht mehr zum Kopf zirkulieren kann
|
||||
überstrahlen transitiv, gehoben: auf der gesamten Fläche bestrahlen
|
||||
mitnichten veraltend, gehoben: auf keinen Fall, in keiner Weise
|
||||
fortsterben intransitiv, gehoben oder veraltete Bedeutung: (allmählich, aber auch plötzlich) aufhören zu leben
|
||||
zurückbeben intransitiv, gehoben oder veraltete Bedeutung: mit einer kurzen, heftigen Bewegung vor etwas oder jemandem zurückweichen
|
||||
mit doppelter Zunge reden gehoben: Unwahres oder Widersprüchliches sagen, heuchlerisch reden, fuchsschwänzen
|
||||
spartanisch keine Steigerung: das antike Sparta betreffend
|
||||
Dolmetsch besonders in Österreich: Person, die (gesprochene) Texte in eine andere Sprache übersetzt
|
||||
Unflat gehoben, veraltend: abstoßend wirkender Schmutz
|
||||
ausweinen reflexiv: (bei jemandem) Erleichterung suchen durch Weinen, sich jemandem anvertrauen, jemandem von seinen Sorgen erzählen
|
||||
vorwalten gehoben, veraltend: vorhanden sein; wirksam sein
|
||||
seligen Angedenkens veraltet bis veraltend, sonst gehoben verhüllend: Beistellung, die die erwähnte Person als verstorben kennzeichnet
|
||||
Reich der Mitte poetisch, gehoben, literatursprachlich: Bezeichnung und Selbstbezeichnung für China; wörtliche Übersetzung der chinesischen Schriftzeichen: zhong guo; wird insbesondere historisch und außerhalb Chinas in Poesie und Prosa verwendet
|
||||
Strahlkraft gehoben: starke Wirkung auf andere
|
||||
hinausgeleiten transitiv; gehoben: jemanden (von hier drinnen) nach dort draußen begleiten/führen (weg vom Sprechenden)
|
||||
umwittern veraltet: mit schlechtem Wetter oder Unwetter umgeben
|
||||
hoffnungsfroh gehoben: der Zukunft zuversichtlich entgegensehend
|
||||
Zutrunk gehoben veraltet: das Erheben eines (mit Alkohol gefüllten) Glases, um einer Person die Ehre zu erweisen oder ihr Gutes zu wünschen
|
||||
Akteur gehoben: Person, die handelt, die etwas tut
|
||||
ungesättigt gehoben: Hunger verspürend
|
||||
Weh gehoben: seelischer Schmerz
|
||||
martervoll gehoben: mit großen Schmerzen, nach schmerzhaften Qualen
|
||||
vermessen übermäßig ehrgeizig und deshalb riskant
|
||||
heißen genannt werden, den Namen haben
|
||||
Sinnenrausch gehoben: rauschhafter Zustand der Sinne
|
||||
Mehrer gehoben: Person, die eine größere Menge von etwas anhäuft
|
||||
dahinschwinden intransitiv, gehoben, unpersönlich: (allmählich) weniger werden
|
||||
voreinst gehoben oder veraltet: vor langer/längerer Zeit
|
||||
niederlassen veraltende Bedeutung: einen Gegenstand senken/auf den Boden legen
|
||||
einhellig gehoben: ohne Gegenstimmen
|
||||
bekleiden transitiv, reflexiv: mit Bekleidung versehen
|
||||
Blattkrone gehoben: Baumkrone
|
||||
widereinander gehoben, von Personen oder Dingen: im Widerstreit zueinander, in einem konkurrierenden Verhältnis zueinander
|
||||
Mondesfinsternis Astronomie, Österreich, gehoben: Ereignis, bei dem der Mond durch den Kernschatten der Erde wandert
|
||||
Gezweige gehoben: Gesamtheit aller Zweige (einer Hecke, eines Baumes oder Ähnliches)
|
||||
Apotheose Religion, gehoben: die Erklärung eines Menschen zu einer Gottheit
|
||||
Œuvre gehoben, bildungssprachlich: Gesamtheit aller Einzelwerke eines Künstlers
|
||||
bejahrt gehoben: in vorgerücktem Alter
|
||||
mondhell gehoben: durch das vom Erdmond reflektierte Licht erhellt
|
||||
Ichsucht gehoben, veraltend: Haltung, die darauf aus ist, sich selbst immer zuerst zu berücksichtigen
|
||||
gebrechen gehoben, unpersönlich, jemandem (Dativ) gebricht es an etwas (Dativ); persönlich nur veraltend: fehlen, mangeln
|
||||
Furor gehoben: Wildheit, Raserei
|
||||
Bringerin veraltend, gehoben: Überbringerin von etwas
|
||||
Billigkeit Eigenschaft, preisgünstig zu sein
|
||||
abdorren gehoben: trocken werden (und dadurch absterben)
|
||||
Lüsternheit gehoben: Zustand, lüstern/sexuell begehrlich zu sein
|
||||
Buhle gehoben, veraltet: Geliebter
|
||||
aufzehren transitiv, auch übertragen: etwas vollständig verzehren, aufbrauchen
|
||||
landauf, landab adverbiell; gehoben: im ganzen Land
|
||||
kundmachen transitiv, österreichische Amtssprache, sonst gehoben: öffentlich bekanntgeben
|
||||
affirmativ gehoben: Zustimmung/Affirmation erkennen lassend
|
||||
meinige gehoben, veraltend: Nebenform von mein
|
||||
Leichnam gehoben: Körper eines Verstorbenen
|
||||
rotwangig gehoben: mit roten Wangen
|
||||
blicklos gehoben: mit offenen Augen, aber nichts wahrnehmend
|
||||
delegitimieren gehoben: jemanden oder etwas rechtlich nicht anerkennen; die Legitimation von etwas oder jemandem aufheben
|
||||
ephemerisch gehoben: nur einen Tag dauernd, nur kurze Zeit bestehend, rasch vorübergehend
|
||||
fortan gehoben: ab jetzt, von jetzt an, ab sofort, in Zukunft
|
||||
entbehren transitiv, gehoben: das Nichtvorhandensein einer Person beziehungsweise einer Sache als persönlichen Mangel empfindend erdulden müssen
|
||||
grosso modo gehoben: insgesamt betrachtet
|
||||
nottun gehoben, noch landschaftlich: nötig, vonnöten sein
|
||||
Aberwitz gehoben: etwas völlig (absurd) Unsinniges; Handeln oder Verhalten, das abwegig, unvernünftig, bisweilen zudem gefährlich ist
|
||||
junonisch gehoben: sehr schön (wie die Göttin Juno)
|
||||
unziemlich gehoben: den sozialen Normen nicht entsprechend
|
||||
anbefehlen jemandem etwas ausdrücklich befehlen, dringend anraten
|
||||
zurückgelangen intransitiv, gehoben: wieder an einem bestimmten Ort/bei einem bestimmten Ziel eintreffen, erneut zu diesem kommen
|
||||
guten Mutes sein gehoben: in einer fröhlichen Stimmung sein; voller Zuversicht sein
|
||||
Sturmwind dichterisch, gehoben: mit großer Wucht einhergehender, sehr starker Wind; Sturm
|
||||
freund gehoben veraltend: freundlich gegenüber jemandem/etwas eingestellt, Zuneigung gegenüber jemandem/etwas hegend
|
||||
aufrühren durch Umrühren etwas nach oben steigen lassen
|
||||
hinuntergeleiten transitiv, gehoben: (jemanden) von einem höhergelegenen Ort nach unten führen, begleiten (vom Sprecher weg)
|
||||
alert gehoben: munter, aufgeweckt, rege, frisch
|
||||
kontrafaktisch gehoben: gegen das Gängige und Gewohnte gerichtet
|
||||
ausatmen intransitiv: die Atemluft ausstoßen
|
||||
jüngsthin gehoben oder veraltet: vor Kurzem, vor nicht langer Zeit
|
||||
Himmelsbote dichterisch, gehoben: zwischen Himmel und Erde verkehrendes übernatürliches Wesen als Überbringer göttlicher Botschaften
|
||||
sonnenbeschienen gehoben: im Sonnenschein befindlich
|
||||
dahinsterben intransitiv, gehoben: (allmählich, aber auch plötzlich) aufhören zu leben
|
||||
Tondichter gehoben, Musik: Künstler, der musikalische Werke schafft
|
||||
entraten veraltet, gehoben: auf etwas Verzicht üben, ohne etwas zurechtkommen
|
||||
seimig veraltet, gehoben: so, dass es nur langsam und schwer fließt; von eher zäher Konsistenz
|
||||
Segen bringend gehoben: Positives bewirkend, Vorteile für viele bietend
|
||||
Gepflogenheit gehoben: eine nicht verbindliche Übereinkunft, die durch häufiges Wiederholen zur Konvention geworden ist
|
||||
Dulderin gehoben: weibliche Person, die etwas duldet/erträgt
|
||||
zurückbegeben reflexiv, gehoben: den Ort wieder aufsuchen, von dem man kommt, aufgebrochen ist
|
||||
Nebelmeer gehoben, dichterisch: dicht und weit wallender Nebel
|
||||
Strauß (kunstvoll gestaltete, häufig gebundene) Zusammenstellung von abgeschnittenen oder gepflückten Blumen, Halmgräsern, Zweigen oder dergleichen
|
||||
Tonschöpferin gehoben, Musik: Künstlerin, die musikalische Werke schafft
|
||||
weh gehoben: erfüllt, geprägt von Kummer, Leid, (seelischem) Schmerz, Weh
|
||||
herzgeliebt gehoben: sehr, innig geliebt
|
||||
Hort dichterisch, sonst veraltet: (vor allem aus Gold beziehungsweise goldenen Gegenständen zusammengesetzter) Schatz
|
||||
Abendvisite Arztbesuch während der Abendzeit
|
||||
rechten intransitiv oder mit Präpositionalobjekt; gehoben: sich (verbal) mit jemandem über oder um etwas streiten (um sein Recht zu behaupten); für sein Recht einstehen, sein Recht von jemandem fordern
|
||||
anschicken reflexiv, gehoben: sich dazu bereit machen, etwas zu tun; im Begriff sein, etwas zu tun
|
||||
lüstern begierig nach etwas
|
||||
eingeben jemandem etwas (vor allem eine Arznei in einer bestimmten festgesetzten Menge) zum Hinunterschlucken langsam in den Mund geben, jemanden etwas vorsichtig einnehmen lassen
|
||||
flocken intransitiv, Hilfsverb haben, Hilfsverb sein, gehoben: Flocken bilden; flockig werden
|
||||
Abendglanz gehoben: glänzendes und strahlendes Licht während der Abendstunden, insbesondere in Verbindung mit einem schönen Abendhimmel
|
||||
abkargen reflexiv, veraltet, gehoben: etwas, meist Geld, unter Verzicht auf anderes für die spätere Verwendung ansammeln
|
||||
ohne Zahl gehoben: sehr viele; in solch einer Fülle, die sich nicht zählen lässt
|
||||
entschlagen mit Genitiv, veraltet, selten, gehoben, reflexiv: sich innerlich frei machen von einer Sache, sich von etwas befreien, auf etwas verzichten
|
||||
reparabel gehoben: so beschaffen, dass es repariert werden kann
|
||||
aberhundert gehoben: mehrere hundert, noch einmal hundert
|
||||
ein Kind des Todes sein gehoben: sterben müssen; (angesichts einer Todesgefahr) bald sterben werden
|
||||
Sukkus Österreich, gehoben: Zusammenfassung des Wesentlichen; Ergebnis
|
||||
hineilen zu einem bestimmten Ort laufen; sich schnell wegbewegen
|
||||
Fürsorger veraltend: Person, die sich im Auftrag einer Fürsorgeeinrichtung beruflich um Hilfsbedürftige kümmert
|
||||
verhehlen transitiv, gehoben: jemandem etwas (besonders Gefühle und Gedanken) verheimlichen, verbergen
|
||||
Mildtätigkeit gehoben: Bereitschaft, sich um in Not befindliche Menschen zu kümmern oder (meist finanziell) großzügig zu unterstützen
|
||||
zuweilen gehoben: zu bestimmten Zeiten, aber nicht regelmäßig; für eine Weile und später wieder für eine Weile
|
||||
Walplatz gehoben oder veraltet: Platz, an dem ein kriegerischer Kampf stattfindet/stattgefunden hat
|
||||
Mägdlein dichterisch, gehoben oder veraltet: junge weibliche Person
|
||||
erdreisten reflexiv, gehoben: sich etwas herausnehmen, etwas Unerwünschtes tun
|
||||
profund gehoben: gründlich, tiefgreifend, ganz umfassend
|
||||
wehret den Anfängen gehoben: Mahnung, bereits frühzeitig gegen (für die Gesellschaft) gefährliche Entwicklungen, Strömungen vorzugehen
|
||||
augenscheinlich gehoben: (auf den ersten Blick) klar erkennbar, klar ersichtlich
|
||||
anmutlos gehoben veraltet: ohne jegliche Grazie, Eleganz
|
||||
Applikation veraltete Bedeutung: ein Gesuch, eine Bittschrift
|
||||
dem Bacchus huldigen gehoben, euphemistisch: Wein trinken
|
||||
damasten attributiv, gehoben: aus Damast gemacht
|
||||
segenbringend gehoben: Positives bewirkend, Vorteile für viele bietend
|
||||
entfliehen intransitiv: aus einer Gefahr, aus einer Zwangslage entkommen
|
||||
ein Raub der Flammen werden gehoben: durch Feuer vollständig zerstört werden, verbrennen
|
||||
einstweilen gehoben: fürs Erste, bis auf Weiteres
|
||||
dürsten intransitiv, gehoben: Durst haben
|
||||
Mägdelein dichterisch gehoben oder veraltet: junge weibliche Person
|
||||
Verstorbener gehoben: Person, die verstorben ist
|
||||
gottigkeit österreichisch, gehoben, veraltet, zur Beteuerung, parenthetisch verwendet: gewissermaßen, sozusagen, gleichsam, wirklich, bei Gott, will sagen
|
||||
hintanhalten gehoben, sonst besonders Österreich: (durch entsprechende Maßnahmen) bewirken, dass etwas (für eine Weile) nicht geschehen kann, von jemandem (für eine Weile) nicht getan werden kann
|
||||
röten transitiv, gehoben: etwas rot färben, rot erscheinen lassen
|
||||
anrühren jemand oder etwas anfassen, berühren
|
||||
verdämmern intransitiv, gehoben: untätig sein, nicht teilnehmen, keine Anteilnahme, kein Mitgefühl zeigen
|
||||
anwandeln transitiv, gehoben: (jemanden) unvermittelt erfassen, (jemandem) plötzlich in den Sinn kommen (eine Empfindung, Erinnerung, Idee oder Anderes)
|
||||
Ungestüm gehoben: überraschende, nicht logisch erklärbare Wildheit
|
||||
zagen intransitiv, gehoben: ängstlich zögern, bangen
|
||||
wild Botanik, Zoologie, Gegenwort zu zahm oder veredelt: nicht gezähmt; frei in der Natur vorkommend; nicht veredelt
|
||||
türein, türaus adverbiell; gehoben: durch eine Tür hinein und (durch dieselbe oder eine andere) wieder hinaus; von einem Raum, Zimmer, einer Amtsstube, Haustür (oder dergleichen) zur nächsten
|
||||
erquicklich gehoben: so, dass es Freude, Gefallen bereitet, erweckt
|
||||
Hymnus Keine Definition gefunden
|
||||
Toilette eine sanitäre Vorrichtung oder Anlage zur Aufnahme von Körperausscheidungen
|
||||
schweren Herzens gehoben: so dass es jemandem Anstrengung, Sorgen bereitet, jemanden Überwindung kostet; mit Bedenken, Zweifel; sehr ungern; ganz bekümmert; im Widerspruch zu jemandes Gefühl
|
||||
einstens vor langer/längerer Zeit
|
||||
immerdar gehoben, poetisch: zu jedem Zeitpunkt in der Zukunft
|
||||
andachtsvoll gehoben: in Andacht versunken, am Gebet/Gottesdienst/einem spirituellen Inhalt innerlich beteiligt
|
||||
temperieren die Temperatur von etwas so verändern, wie sie gewünscht wird/wie sie als angenehm/passend empfunden wird
|
||||
unerfindlich gehoben: sich nicht erklären lassend, rätselhaft erscheinend
|
||||
in Rauch und Flammen aufgehen gehoben: durch Feuer vollständig zerstört werden, verbrennen
|
||||
Stachel im Fleische gehoben: immerwährende eindringliche Erinnerung an etwas
|
||||
Disputant gehoben: Person, die sich an einem Disput beteiligt
|
||||
Dafürhalten gehoben: Einstellung, dass etwas richtig/zutreffend ist
|
||||
Küchlein gehoben, veraltet: Jungtier von Geflügel
|
||||
anempfinden reflexiv, gehoben: sich in jemandes Gefühlswelt hineinversetzen und sich dessen Empfindungen zu eigen machen
|
||||
unter Waffen stehen gehoben: bewaffnet (und kampfbereit) sein
|
||||
schärfen transitiv: etwas scharf oder schärfer machen
|
||||
au contraire gehoben: im Gegenteil
|
||||
fehlschießen gehoben: einen Schuss abgeben, der das Ziel verfehlt
|
||||
überliefern etwas, beispielsweise eine Geschichte, einen Brauch, an die Nachkommen weitererzählen/weitergeben
|
||||
Dolmetschin besonders in Österreich: weibliche Person, die (gesprochene) Texte in eine andere Sprache übersetzt
|
||||
Dunkel gehoben: lichtarmer oder völlig lichtloser Zustand, in dem sich etwas befindet oder der etwas auszeichnet
|
||||
bleiern aus Blei bestehend; hergestellt aus Blei
|
||||
Blutzoll gehoben: Verluste bei bewaffneten Konflikten
|
||||
Silberbart gehoben: Bart mit hellem, silbrig schimmerndem Grauton
|
||||
Paradiesgarten Religion: Ort, an den selige Menschen nach dem Tod kommen
|
||||
Angedenken veraltet: kleiner Gegenstand, der Erinnerungen an ein bestimmtes Ereignis oder eine bestimmte Person hervorrufen soll
|
||||
zerscherben gehoben: in einzelne Teile (Scherben) zerbrechen (lassen)
|
||||
Begebnis gehoben oder veraltet: etwas, was geschieht/passiert/sich begibt
|
||||
Ruhestatt gehoben: Platz/Stelle der Bestattung
|
||||
zwischenein gehoben, zeitlich: in unregelmäßigen zeitlichen Abständen (ein Geschehen kurz unterbrechend)
|
||||
Aperçu gehoben: geistreiche Bemerkung
|
||||
hinwegfegen Hilfsverb sein: schnell über etwas hinwegbewegen
|
||||
gelüsten unpersönlich, gehoben oder scherzhaft: großes Verlangen nach etwas haben, Lust auf etwas verspüren
|
||||
blutenden Herzens gehoben: so dass es jemandem Anstrengung, Sorgen bereitet, jemanden Überwindung kostet; mit Bedenken, Zweifel; sehr ungern; ganz bekümmert; im Widerspruch zu jemandes Gefühl
|
||||
Ich-Sucht gehoben, veraltend: Haltung, die darauf aus ist, sich selbst immer zuerst zu berücksichtigen
|
||||
nunmehrig österreichisch, sonst gehoben: jetzt stattfindend; sich auf die unmittelbare Gegenwart beziehend
|
||||
herabwerfen transitiv; gehoben: von (dort) oben nach (hier) unten werfen (in Richtung zum Sprechenden)
|
||||
kundgeben transitiv, gehoben: etwas öffentlich bekannt geben/machen, jemanden etwas wissen lassen
|
||||
hinzuaddieren transitiv, gehoben: (etwas) zu einer bekannten Zahl rechnerisch hinzufügen
|
||||
dartun gehoben: etwas erläuternd darlegen, etwas zum Ausdruck bringen
|
||||
einbekennen österreichisch, sonst gehoben: eingestehen
|
||||
Sinnesverwirrung gehoben: (vorübergehende) falsche Interpretation von Sinneseindrücken (zum Beispiel durch Drogenkonsum oder Krankheit)
|
||||
Spross Botanik: junger Trieb an einer Pflanze, neu wachsender Teil einer Pflanze
|
||||
auslesen etwas aus einer Menge desselben heraussuchen und entfernen (weil es unbrauchbar [geworden] ist)
|
||||
behagen intransitiv; gehoben, unpersönlich, mit Dativ: ein wohliges, still vergnügliches Gefühl bereiten
|
||||
Brüderpaar gehoben: zwei Brüder
|
||||
Schimpf gehoben, meist Singular: Verletzung der Selbstachtung
|
||||
sinnenfroh gehoben: mit Sinnenfreude; sich am sinnlichen Genuss erfreuend
|
||||
abschreiten transitiv, Hilfsverb haben oder Hilfsverb sein: eine Strecke zur Kontrolle, prüfend oder feierlich entlanggehen
|
||||
Otterngezücht gehoben abwertend übertragen: boshafte, verachtenswerte Menschen
|
||||
Sprachmittler gehoben: Person, die Text aus einer anderen Sprache übersetzt
|
||||
davonstieben intransitiv, gehoben, von einer Gruppe oder Menge: rasch, fluchtartig, sich (in verschiedene Richtungen) zerstreuend, weglaufen
|
||||
Bildner veraltet: jemand, der eine Person oder Sache formt, ausbildet
|
||||
Disput gehoben: Gespräch oder (seltener schriftlicher) Gedankenaustausch zwischen zwei oder mehr Personen, mit dem Ziel, die jeweilige Gegenseite von den eigenen Argumenten zu überzeugen
|
||||
nächtlicherweile gehoben: während es Nacht ist, in der Nacht, bei Nacht
|
||||
wehklagen gehoben: seelische Not äußern
|
||||
Blütenschnee dichterisch oder gehoben; übertragen: Menge weißer/weißlicher Blüten (die wie Schnee langsam niederfallen, sich wie dieser auf dem Boden ausgebreitet haben, …)
|
||||
Grundgütiger gehoben, meist als Ausruf: der einzige, allmächtige, unfehlbare Herr
|
||||
obschon gehoben: einräumende Subjunktion
|
||||
Band dasjenige, was verbindet oder verknüpft
|
||||
prostituieren gehoben, pejorativ: etwas Anrüchiges, Unpassendes, Verwerfliches begehen oder jemandem vorwerfen
|
||||
schlaftrunken gehoben: noch vom Schlaf benommen; durch Müdigkeit hervorgerufenes Gefühl, als sei man betrunken
|
||||
unlängst gehoben: vor nicht allzu langer Zeit
|
||||
allerorten gehoben oder veraltet: an jedem Ort, überall
|
||||
abschweifen gehoben: von einer Richtung abweichen, die Richtung ändern
|
||||
Berufsethos gehoben: moralisches Bewusstsein, verantwortungsbewusste Einstellung einer Berufsgruppe hinsichtlich der einzuhaltenden Pflichten, Normen bei der Ausübung des Berufs
|
||||
zerrinnen intransitiv, gehoben: flüssig werden und zerfließen
|
||||
Wegscheide gehoben: Stelle, an der sich ein Weg gabelt
|
||||
einherschreiten gehoben: vor jemandes Augen eher langsam und gemessenen Schrittes umhergehen oder vorbeigehen
|
||||
traun gehoben veraltet: in der Tat, wirklich, fürwahr, wahrlich
|
||||
Fels kein Plural: zusammenhängende Masse festen Gesteins (Singular 1)
|
||||
hintanstehen intransitiv; gehoben: sich zugunsten von jemand/etwas anderem zurückhalten; zugunsten von jemand/etwas anderem auf jemanden/etwas verzichten; zugunsten von jemand/etwas anderem unberücksichtigt, vernachlässigt werden
|
||||
kundtun gehoben, veraltend: etwas anderen mitteilen
|
||||
unveräußerlich so, dass es nicht beseitigt/aberkannt werden darf/kann
|
||||
ichsüchtig gehoben: übermäßig auf die eigene Person fixiert, nur zum eigenen Vorteil handelnd
|
||||
benehmen reflexiv: sich verhalten, sich auf bestimmte Weise in der Umwelt zeigen
|
||||
Plazet gehoben: Erklärung des Einverständnisses
|
||||
durchschneiden transitiv, gehoben: einen Raum oder eine Fläche in einer geraden Linie durchqueren
|
||||
verwinden gehoben: psychisch über etwas hinwegkommen
|
||||
innewohnen intransitiv, gehoben, mit Dativ: fester Bestandteil sein von etwas; grundsätzlich dazugehören; typische Charakteristik von etwas sein
|
||||
vulgo bildungssprachlich: im Allgemeinen/auch unter der Bezeichnung x geläufig (oft mit abwertendem, drastischem Ton)
|
||||
Disputantin gehoben: weibliche Person, die sich an einem Disput beteiligt
|
||||
herüberbemühen transitiv, gehoben: jemanden darum ersuchen, die Mühe auf sich zu nehmen, um aus einiger Entfernung zum Sprecher hin zu kommen
|
||||
erharren transitiv, selten, gehoben: auf etwas Ersehntes warten
|
||||
süffisant gehoben: selbstgefällig, genüsslich überlegene Distanz zeigend, ein Gefühl von meist geistiger Überlegenheit zur Schau tragend, spöttisch-überheblich
|
||||
Maiennacht dichterisch, gehoben: Nacht im Mai
|
||||
Jüngling gehoben: junger, fast erwachsener Mann
|
||||
hinfallen intransitiv: (mit Wucht) zu Boden, auf die Erde fallen
|
||||
mondbeschienen dichterisch, gehoben: im Mondschein befindlich
|
||||
woselbst veraltend, gehoben: dort an diesem Ort, an dieser Stelle
|
||||
einbegreifen gehoben: auch beinhalten, dazunehmen
|
||||
jüngstens gehoben oder veraltet: vor Kurzem, vor nicht langer Zeit
|
||||
wegleugnen transitiv; umgangssprachlich: (etwas, eine Sache) durch Leugnen bereinigen, endgültig beseitigen
|
||||
Aufschwung allgemein, gehoben: mehr Schwung, Auftrieb, Fortschritt
|
||||
Erlesenheit gehoben, ohne Plural: außerordentlich gute, exquisite Qualität
|
||||
preiswürdig gehoben: lobenswert, hervorragend • Diese Bedeutungsangabe sollte ergänzt werden.
|
||||
Ferne große räumliche Distanz
|
||||
Zauberbann dichterisch und gehoben, auch übertragen: auf jemanden oder etwas Einfluss nehmende, unwiderstehliche Kraft eines Zaubers
|
||||
dahingehen intransitiv; gehoben: jemanden, etwas gehend passieren, sich an jemanden, etwas gehend vorbeibewegen; (ohne erkennbares, festes Ziel) von einer Stelle zu einer anderen gehen
|
||||
davongehen sich zu Fuß von einem bestimmten Ort/vom Sprechenden entfernen
|
||||
Flucht rasches Entfliehen von einer Sache, die Gefahr oder Unglück verheißt
|
||||
zwiefältig gehoben, veraltend: zweimal so oft oder so viel
|
||||
untersagen transitiv, gehoben: ein Verbot aussprechen
|
||||
Tonkünstler gehoben, Musik: Künstler, der musikalische Werke schafft
|
||||
Selbst gehoben: die eigene Person in ihrer Gesamtheit
|
||||
gotikeit österreichisch, gehoben, veraltet, zur Beteuerung, parenthetisch verwendet: gewissermaßen, sozusagen, gleichsam, wirklich, bei Gott, will sagen
|
||||
Menetekel gehoben: (geheimnisvolles) warnendes Vorzeichen eines drohenden Unheils, ernstes Warnzeichen
|
||||
bewegen die Lage oder Stellung von etwas im Raum verändern; etwas von einem Ort zum anderen schaffen
|
||||
Placet gehoben: Erklärung des Einverständnisses
|
||||
Morgentoilette gehoben, kein Plural: Körperreinigung, Körperpflege und Ankleiden am Morgen
|
||||
Diskutantin gehoben: weibliche Person, die sich an einer Diskussion beteiligt
|
||||
schöpfen etwas aus einer großen Menge Flüssigkeit entnehmen
|
||||
zurückgeleiten transitiv, gehoben: jemanden oder etwas wieder dorthin geleiten, wo er/sie/es zuvor hergekommen ist
|
||||
gesunden nach einer Krankheit wieder gesund werden
|
||||
abhold gehoben: abgeneigt, ungünstig
|
||||
anheben ein wenig in die Höhe heben
|
||||
soupieren intransitiv, gehoben: festlich/in großem Rahmen/mit Gästen zu Abend essen
|
||||
leidlich gehoben: gerade noch ausreichend, akzeptabel
|
||||
Bedenken tragen gehoben: noch unentschlossen sein, zagen, zaudern; Vorbehalte (gegen etwas) haben
|
||||
geifern intransitiv: Speichel aus dem Mund fließen lassen
|
||||
widerfahren gehoben; intransitiv, unpersönlich, mit Dativ: (jemandem) geschehen (besonders etwas Unausweichliches, scheinbar vom Schicksal Bestimmtes)
|
||||
Verblichene gehoben: weibliche Person, die gestorben ist
|
||||
zupasskommen gehoben: für jemanden gerade recht kommen, jemandem äußerst gelegen kommen, jemandem sehr willkommen sein
|
||||
|
@@ -0,0 +1 @@
|
||||
Wort Definition Sprache Zeitstempel
|
||||
|
@@ -0,0 +1,101 @@
|
||||
Wort Definition Sprache Zeitstempel
|
||||
abbummeln eine zuvor außerhalb der regulären/vereinbarten Arbeitszeiten geleistete Arbeit (Mehrarbeit) durch Freizeit anstatt Bezahlung ausgleichen de 2025-09-29 18:26:54
|
||||
abschussbereit vorbereitet sein, zu schießen oder etwas abzuschießen de 2025-09-29 18:26:27
|
||||
Aalfischerei das gewerbliche, wirtschaftliche Betreiben des Aalfangs de 2025-09-29 18:27:02
|
||||
abendlich zum Abend gehörend; am Abend, abends stattfindend de 2025-09-29 18:26:37
|
||||
abhandenkommen verloren gehen, nicht mehr auffindbar sein de 2025-09-29 18:26:51
|
||||
abfieseln mit den Zähnen, mit den Fingern oder mit einem Messer Fleisch (gründlich) von den Knochen lösen de 2025-09-29 18:26:27
|
||||
A-Saite musik]]:'' Saite, die auf den Ton A oder a gestimmt ist de 2025-09-29 18:26:26
|
||||
abgelegen weit weg und schlecht zugänglich de 2025-09-29 18:26:06
|
||||
abhorreszieren abscheu gegen etwas oder jemanden empfinden de 2025-09-29 18:26:10
|
||||
abackern etwas durch Bearbeitung der Erde absondern und somit aus dem Besitz einer anderen Person entziehen de 2025-09-29 18:26:07
|
||||
abdikativ sich auf eine Abdikation beziehend oder sie bewirkend de 2025-09-29 18:27:03
|
||||
ablandig geographie]], Hydrologie, Meteorologie, Klimatologie:'' hinsichtlich auf Luftströmungen und Meeresströmungen bezogen: vom Lande wegführend de 2025-09-29 18:26:43
|
||||
abendfüllend den gesamten oder zumindest einen Großteil des Abends andauernd de 2025-09-29 18:26:20
|
||||
abkarten eine heimliche Vereinbarung treffen, etwas heimlich zu jemandes Nachteil aushandeln de 2025-09-29 18:27:08
|
||||
abdüsen sich schnell von einem Ort entfernen, meist mithilfe eines Fortbewegungsmittels de 2025-09-29 18:26:13
|
||||
a-Schwa vokal, der zwischen einem [a] und einem [ə] (Schwa) liegt und im Deutschen nur unbetont vorkommt de 2025-09-29 18:26:46
|
||||
abchecken überprüfen]], kontrollieren de 2025-09-29 18:27:09
|
||||
abhörsicher von Räumen oder Geräten:'' vor Abhöraktionen geschützt; so beschaffen, dass es nicht abgehört werden kann de 2025-09-29 18:26:24
|
||||
abhauen etwas abschlagen, abtrennen (starke und schwache Konjugation: hieb/haute ab und abgehauen/abgehaut) de 2025-09-29 18:27:07
|
||||
abbestellen etwas abbestellen:'' eine Bestellung rückgängig machen, etwas kündigen de 2025-09-29 18:26:11
|
||||
abkrüppeln sterben, abkratzen, verrecken de 2025-09-29 18:26:45
|
||||
abhinfallen veraltet; bairisch, südalemannisch:'' vom Sprecher weg fallen de 2025-09-29 18:26:36
|
||||
abbehalten (eine Kopfbedeckung) nach dem Abnehmen (vorerst) nicht erneut aufsetzen de 2025-09-29 18:26:44
|
||||
abgemergelt in schlechter körperlicher Verfassung, durch Arbeit erschöpft de 2025-09-29 18:26:34
|
||||
abflachen flach(er) machen de 2025-09-29 18:26:28
|
||||
aboral vom Mund weg gelegen de 2025-09-29 18:26:32
|
||||
abdreschen stroh ausdreschen, um das Getreide zu erhalten; das Getreide aus dem Stroh dreschen de 2025-09-29 18:26:50
|
||||
abbussen süddeutsch, österreichisch, umgangssprachlich:'' über und über mit Küssen bedecken de 2025-09-29 18:26:49
|
||||
abern einwände anbringen, erheben de 2025-09-29 18:27:06
|
||||
aasfräßig sich von Aas ernährend de 2025-09-29 18:26:40
|
||||
Aalstechen art des Aalfanges mittels Stechen nach den Aalen mit Aalspeeren de 2025-09-29 18:26:42
|
||||
Aalmöwe im Ostatlantik und den nordeuropäischen Küsten lebender Barschartiger, de 2025-09-29 18:27:10
|
||||
abenteuerhungrig übertragen:'' hungrig auf Abenteuer; ein Abenteuer vermissend, erwartend, sich nach einem Abenteuer sehnend de 2025-09-29 18:26:30
|
||||
Aalbaum veraltet]], Botanik:'' Geißblattgewächs mit eierförmigen Blättern, gelben Blüten und roten, für den Menschen ungenießbaren Früchten de 2025-09-29 18:27:02
|
||||
abendländisch das Abendland betreffend, zu ihm gehörend, charakteristisch für das Abendland de 2025-09-29 18:26:47
|
||||
abfretten sich abmühen de 2025-09-29 18:27:04
|
||||
abgelehnt zurückgewiesen, nicht genehmigt de 2025-09-29 18:26:26
|
||||
abfohlen ein Jungtier (Fohlen) zur Welt bringen de 2025-09-29 18:26:09
|
||||
abgrätschen mit gestreckten und gespreizten Beinen von einem Turngerät springen, Abgang von einem Gerät mit einer Grätsche de 2025-09-29 18:27:05
|
||||
abfieren etwas am Tau herunterlassen de 2025-09-29 18:26:34
|
||||
abkömmlich nicht zwingend benötigt; verzichtbar, unnotwendig, disponibel de 2025-09-29 18:27:06
|
||||
abrufbar so, dass man Zugriff darauf hat; so, dass einem etwas zur Verfügung steht de 2025-09-29 18:26:32
|
||||
abgekämpft erschöpft von sehr großer Anstrengung de 2025-09-29 18:26:30
|
||||
abgerissen in einem schlechten Zustand befindlich, zerlumpt, mitgenommen de 2025-09-29 18:27:00
|
||||
abbimsen zur Glättung/Reinigung mit einem Bimsstein abreiben de 2025-09-29 18:26:58
|
||||
abberufen jemanden von seinem Amt oder seiner Funktion entheben de 2025-09-29 18:26:55
|
||||
abbitten jemanden für etwas um Verzeihung bitten de 2025-09-29 18:26:29
|
||||
Aakerbeere mehrjährige Pflanze mit leuchtend roten bis purpurnen Blüten und roten, essbaren Früchten, die im Norden Eurasiens und in Nordamerika vorkommt () de 2025-09-29 18:26:41
|
||||
abfetzen etwas (in Fetzen) nach unten reißen de 2025-09-29 18:27:08
|
||||
abalienieren entäußern, veräußern de 2025-09-29 18:26:38
|
||||
abgegrenzt voneinander getrennt de 2025-09-29 18:26:57
|
||||
Aalmolch familie]] der Schwanzlurche, die aalförmig gestreckt sind de 2025-09-29 18:26:52
|
||||
abjustieren abgleichen]] de 2025-09-29 18:26:16
|
||||
Aalsuppe suppe aus Aal de 2025-09-29 18:26:07
|
||||
abbellen (zum Beispiel eine Rede) laut, aber unverständlich, mit heiserer Stimme vortragen de 2025-09-29 18:26:50
|
||||
abjudizieren jemandem etwas (gerichtlich) aberkennen, absprechen de 2025-09-29 18:26:48
|
||||
Aalenium geologie]]:'' geologisch unterste Stufe des Mittleren Jura Braunjura de 2025-09-29 18:26:59
|
||||
Aalspeer gerät]], das einem Speer ähnelt, jedoch über mehrere Zinken verfügt und zum Aalstechen benutzt wird de 2025-09-29 18:26:54
|
||||
aasfressend aas fressend, sich von toten Organismen ernährend de 2025-09-29 18:26:29
|
||||
abblitzen von jemandem zurückgewiesen werden, jemandem gegenüber keine Chance zur Kommunikation haben de 2025-09-29 18:26:22
|
||||
abenteuersüchtig mit einem übersteigerten Verlangen nach Abenteuern de 2025-09-29 18:26:25
|
||||
aargauisch den Kanton Aargau oder dessen Bewohner betreffend, aus dem Aargau kommend de 2025-09-29 18:26:20
|
||||
Aalfischer person, die Aale fängt de 2025-09-29 18:26:42
|
||||
abschlägig ein Ansinnen verweigernd de 2025-09-29 18:27:01
|
||||
abfechten jemandem etwas durch eine Kampfhandlung abgewinnen de 2025-09-29 18:26:18
|
||||
abgasentgiftet so beschaffen, dass keine giftigen Abgase vorhanden sind de 2025-09-29 18:26:45
|
||||
abdecken eine Decke entfernen de 2025-09-29 18:26:19
|
||||
abfiedeln etwas mit einem stumpfen Schneidwerkzeug schneidend entfernen de 2025-09-29 18:26:12
|
||||
abnorm von der Norm oder dem Üblichen (Normalen) abweichend de 2025-09-29 18:26:21
|
||||
abblassen farblich]] weniger intensiv werden, blass werden de 2025-09-29 18:26:53
|
||||
Aap kleines Stagsegel auf Segelschiffen de 2025-09-29 18:26:35
|
||||
abgefuckt jargon]], derb, vulgär:'' in schlechtem, üblem Zustand befindlich de 2025-09-29 18:26:08
|
||||
abdorren trocken werden (und dadurch absterben) de 2025-09-29 18:26:08
|
||||
abgelenkt geistig]] abwesend, nicht auf eine Sache konzentriert de 2025-09-29 18:26:33
|
||||
abduschen etwas oder sich durch Duschen reinigen de 2025-09-29 18:26:12
|
||||
Aalrutte knochenfisch aus der Familie der Quappen de 2025-09-29 18:26:24
|
||||
abfangen jemanden oder etwas auf seinem Weg aufhalten de 2025-09-29 18:27:03
|
||||
abteilungsübergreifend mehr als eine Abteilung betreffend; mehrere Abteilungen umfassend de 2025-09-29 18:26:36
|
||||
absichtlich die Auswirkung seiner Handlung wollend de 2025-09-29 18:26:23
|
||||
abducken den Schlag eines Gegners durch Ducken ins Leere gehen lassen de 2025-09-29 18:26:39
|
||||
aalähnlich wie ein Aal, an einen Aal erinnernd de 2025-09-29 18:26:48
|
||||
abbaken den Fahrweg mit Baken oder eingeschlagenen Pfählen markieren de 2025-09-29 18:26:59
|
||||
abfallarm so beschaffen, dass wenig Abfall entsteht de 2025-09-29 18:26:58
|
||||
abhandeln mit jemandem über etwas verhandeln, sprechen, mit dem Ziel, dass man von ihm etwas bekommt de 2025-09-29 18:27:05
|
||||
abbalzen die Balz beenden de 2025-09-29 18:26:33
|
||||
abmarschbereit zum Abmarsch bereit de 2025-09-29 18:26:13
|
||||
abkanzeln mit harschen Worten, von oben herab, kritisieren de 2025-09-29 18:26:31
|
||||
abbreiten mit einem Breithammer (in bestimmtem Maße) breit schlagen de 2025-09-29 18:26:55
|
||||
abbrüchig so beschaffen, dass es leicht abbricht de 2025-09-29 18:26:17
|
||||
abdingbar durch andere Vereinbarungen abänderbar de 2025-09-29 18:26:57
|
||||
abkommen veraltet]]:'' von (etwas, jemandem) stammen, auf (etwas, jemanden) zurückgehen de 2025-09-29 18:26:15
|
||||
abborgen jemandem etwas abborgen:'' mit dem Versprechen der baldigen Rückgabe vorübergehend in seinen Besitz nehmen de 2025-09-29 18:27:02
|
||||
abflusslos keinen Abfluss habend de 2025-09-29 18:26:59
|
||||
abgasarm nur eine relativ kleine Menge an (schädlichen) Abgasen ausstoßend de 2025-09-29 18:26:18
|
||||
abgaunern jemandem etwas durch List, Betrug, Täuschung widerrechtlich wegnehmen de 2025-09-29 18:27:05
|
||||
abkindern einen Ehekredit durch das Zeugen von Kindern tilgen de 2025-09-29 18:26:39
|
||||
abermalig nochmalig, wiederholt de 2025-09-29 18:26:17
|
||||
Aalstreif zoologie]]:'' schwarzer Streifen längs auf dem Rücken eines Tieres, meist eines Pferdes de 2025-09-29 18:26:47
|
||||
abachen sich bei etwas bis zur Erschöpfung anstrengen und ächzen de 2025-09-29 18:26:56
|
||||
abasinisch auf das Volk der Abasiner/Abasinen bezogen, von ihm stammend, zu ihm gehörend de 2025-09-29 18:26:25
|
||||
|
@@ -0,0 +1,489 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
class WiktionaryClient {
|
||||
private $lang;
|
||||
private $api_url;
|
||||
private $debug;
|
||||
private $is_cli;
|
||||
private $category_cache = [];
|
||||
|
||||
public function __construct($lang = 'en', $debug = false, $is_cli = true) {
|
||||
$this->lang = $lang;
|
||||
$this->api_url = "https://{$lang}.wiktionary.org/w/api.php";
|
||||
$this->debug = $debug;
|
||||
$this->is_cli = $is_cli;
|
||||
}
|
||||
|
||||
private function debug_print($msg) {
|
||||
if ($this->debug) {
|
||||
echo "DEBUG: $msg\n";
|
||||
}
|
||||
}
|
||||
|
||||
private function progress_print($msg) {
|
||||
if ($this->is_cli) {
|
||||
echo "\r$msg";
|
||||
} else {
|
||||
echo "$msg<br>";
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_category_members($category) {
|
||||
if (isset($this->category_cache[$category])) {
|
||||
$this->debug_print("Cache-Hit für Kategorie '$category': " . count($this->category_cache[$category]) . " Member");
|
||||
return $this->category_cache[$category];
|
||||
}
|
||||
|
||||
$members = [];
|
||||
$params = [
|
||||
'action' => 'query',
|
||||
'list' => 'categorymembers',
|
||||
'cmtitle' => "Category:$category",
|
||||
'cmtype' => 'page',
|
||||
'cmlimit' => '500',
|
||||
'format' => 'json'
|
||||
];
|
||||
$this->debug_print("Starte Abruf für Kategorie '$category' mit URL: {$this->api_url} und Params: " . http_build_query($params));
|
||||
$cmcontinue = '';
|
||||
$page = 1;
|
||||
$total_members = 0;
|
||||
while (true) {
|
||||
$query = http_build_query($params) . ($cmcontinue ? "&cmcontinue=$cmcontinue" : '');
|
||||
$ch = curl_init("{$this->api_url}?$query");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'GrokScript/1.0');
|
||||
$response = curl_exec($ch);
|
||||
$curl_error = curl_error($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($curl_error) {
|
||||
echo "Fehler beim API-Aufruf für $category: $curl_error\n";
|
||||
$this->debug_print("Curl Error Details: $curl_error");
|
||||
break;
|
||||
}
|
||||
$this->debug_print("Response Status: $status_code, Content-Length: " . strlen($response));
|
||||
if ($status_code != 200) {
|
||||
echo "Fehler beim Abrufen von $category: $status_code\n";
|
||||
$this->debug_print("Response Text: " . substr($response, 0, 500) . "...");
|
||||
break;
|
||||
}
|
||||
$data = json_decode($response, true);
|
||||
$this->debug_print("Data Keys: " . implode(', ', array_keys($data)));
|
||||
$new_members = [];
|
||||
foreach ($data['query']['categorymembers'] ?? [] as $member) {
|
||||
$new_members[] = $member['title'];
|
||||
}
|
||||
$members = array_merge($members, $new_members);
|
||||
$total_members += count($new_members);
|
||||
$this->progress_print("Kategorie '$category' Paging: Seite $page abgerufen (" . count($new_members) . " neue, total $total_members)");
|
||||
if (isset($data['continue']['cmcontinue'])) {
|
||||
$cmcontinue = $data['continue']['cmcontinue'];
|
||||
$this->debug_print("Paginierung fortsetzen mit cmcontinue: $cmcontinue");
|
||||
$page++;
|
||||
} else {
|
||||
if ($this->is_cli) echo "\n"; else echo "<br>";
|
||||
break;
|
||||
}
|
||||
sleep(0.5); // Pause
|
||||
}
|
||||
$this->category_cache[$category] = $members;
|
||||
$this->debug_print("Abgerufene Member für '$category' (cached): " . count($members));
|
||||
return $members;
|
||||
}
|
||||
|
||||
public function extract_definition_and_variant($extract, $preferred_variant = null) {
|
||||
$lines = explode("\n", $extract);
|
||||
$definition = 'Keine Definition gefunden';
|
||||
$variant = '';
|
||||
$in_bedeutungen = false;
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, 'Bedeutungen:') !== false) {
|
||||
$in_bedeutungen = true;
|
||||
continue;
|
||||
}
|
||||
if ($in_bedeutungen) {
|
||||
if (strpos($line, '[1] ') === 0) {
|
||||
$definition = trim(substr($line, 4));
|
||||
break;
|
||||
} elseif (strpos($line, '[1]') === 0) {
|
||||
$definition = trim(substr($line, strpos($line, '[1]') + 3));
|
||||
break;
|
||||
} elseif (preg_match('/^\[\d+\]/', $line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Alternative Schreibweise suchen
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, 'Alternative Schreibweisen:') !== false || strpos($line, 'Nebenformen:') !== false) {
|
||||
// Split nach Labels wie "Schweizerische Schreibweise:"
|
||||
$parts = preg_split('/\w+ische Schreibweise:/i', $line);
|
||||
foreach ($parts as $index => $part) {
|
||||
if ($index > 0) { // Nach dem ersten Label
|
||||
preg_match_all('/[\w\-]+/', $part, $matches);
|
||||
if (!empty($matches[0])) {
|
||||
if ($preferred_variant && stripos($line, $preferred_variant) !== false) {
|
||||
$variant = $matches[0][0];
|
||||
break 2; // Beende äußere Schleife
|
||||
} elseif (!$preferred_variant) {
|
||||
$variant = $matches[0][0];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$definition, $variant];
|
||||
}
|
||||
|
||||
public function get_definitions_batch($words) {
|
||||
$definitions = [];
|
||||
$variants = [];
|
||||
foreach ($words as $idx => $word) {
|
||||
$params = [
|
||||
'action' => 'query',
|
||||
'prop' => 'extracts',
|
||||
'titles' => $word,
|
||||
'explaintext' => '1',
|
||||
'exsectionformat' => 'plain',
|
||||
'format' => 'json'
|
||||
];
|
||||
$query = http_build_query($params);
|
||||
$this->debug_print("Starte Definitionsabruf für Wort " . ($idx + 1) . "/" . count($words) . ": '$word' mit URL: {$this->api_url} und Params: $query");
|
||||
$ch = curl_init("{$this->api_url}?$query");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'GrokScript/1.0');
|
||||
$response = curl_exec($ch);
|
||||
$curl_error = curl_error($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($curl_error) {
|
||||
echo "Fehler beim Definitionsabruf für '$word': $curl_error\n";
|
||||
$this->debug_print("Curl Error Details: $curl_error");
|
||||
$definitions[$word] = 'Keine Definition gefunden';
|
||||
$variants[$word] = '';
|
||||
continue;
|
||||
}
|
||||
$this->debug_print("Response Status: $status_code, Content-Length: " . strlen($response));
|
||||
if ($status_code != 200) {
|
||||
echo "Fehler beim Abrufen von Definition für '$word': $status_code\n";
|
||||
$this->debug_print("Response Text: " . substr($response, 0, 500) . "...");
|
||||
$definitions[$word] = 'Keine Definition gefunden';
|
||||
$variants[$word] = '';
|
||||
continue;
|
||||
}
|
||||
$data = json_decode($response, true);
|
||||
$this->debug_print("Data Keys: " . implode(', ', array_keys($data)));
|
||||
$page = reset($data['query']['pages'] ?? []) ?? [];
|
||||
$extract = $page['extract'] ?? '';
|
||||
list($defn, $var) = $this->extract_definition_and_variant($extract, $this->preferred_variant);
|
||||
$definitions[$word] = $defn;
|
||||
$variants[$word] = $var;
|
||||
$this->progress_print("Definitionen abrufen: " . ($idx + 1) . "/" . count($words) . " abgeschlossen ($word)");
|
||||
sleep(0.5); // Pause pro Request zur Rate-Limiting-Vermeidung
|
||||
}
|
||||
if ($this->is_cli) echo "\n"; else echo "<br>";
|
||||
$this->debug_print("Abgerufene Definitionen: " . count($definitions));
|
||||
return [$definitions, $variants];
|
||||
}
|
||||
|
||||
public function list_all_categories($filter_str = null) {
|
||||
$categories = [];
|
||||
$params = [
|
||||
'action' => 'query',
|
||||
'list' => 'allcategories',
|
||||
'aclimit' => '500',
|
||||
'format' => 'json'
|
||||
];
|
||||
$this->debug_print("Starte Abruf aller Kategorien mit URL: {$this->api_url} und Params: " . http_build_query($params));
|
||||
$accontinue = '';
|
||||
$page = 1;
|
||||
$total_categories = 0;
|
||||
while (true) {
|
||||
$query = http_build_query($params) . ($accontinue ? "&accontinue=$accontinue" : '');
|
||||
$ch = curl_init("{$this->api_url}?$query");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'GrokScript/1.0');
|
||||
$response = curl_exec($ch);
|
||||
$curl_error = curl_error($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($curl_error) {
|
||||
echo "Fehler beim API-Aufruf für Kategorien: $curl_error\n";
|
||||
$this->debug_print("Curl Error Details: $curl_error");
|
||||
break;
|
||||
}
|
||||
$this->debug_print("Response Status: $status_code, Content-Length: " . strlen($response));
|
||||
if ($status_code != 200) {
|
||||
echo "Fehler beim Abrufen der Kategorien: $status_code\n";
|
||||
$this->debug_print("Response Text: " . substr($response, 0, 500) . "...");
|
||||
break;
|
||||
}
|
||||
$data = json_decode($response, true);
|
||||
$this->debug_print("Data Keys: " . implode(', ', array_keys($data)));
|
||||
$new_categories = [];
|
||||
foreach ($data['query']['allcategories'] ?? [] as $cat) {
|
||||
$new_categories[] = $cat['*'];
|
||||
}
|
||||
$categories = array_merge($categories, $new_categories);
|
||||
$total_categories += count($new_categories);
|
||||
$this->progress_print("Kategorien Paging: Seite $page abgerufen (" . count($new_categories) . " neue, total $total_categories)");
|
||||
if (isset($data['continue']['accontinue'])) {
|
||||
$accontinue = $data['continue']['accontinue'];
|
||||
$this->debug_print("Paginierung fortsetzen mit accontinue: $accontinue");
|
||||
$page++;
|
||||
} else {
|
||||
if ($this->is_cli) echo "\n"; else echo "<br>";
|
||||
break;
|
||||
}
|
||||
sleep(0.5); // Pause
|
||||
}
|
||||
|
||||
if ($filter_str) {
|
||||
$filter_lower = strtolower($filter_str);
|
||||
$categories = array_filter($categories, function($cat) use ($filter_lower) {
|
||||
return strpos(strtolower($cat), $filter_lower) !== false;
|
||||
});
|
||||
$this->debug_print("Gefiltert nach '$filter_str': " . count($categories) . " Kategorien gefunden");
|
||||
}
|
||||
|
||||
echo "Vorhandene Kategorien" . ($filter_str ? " (gefiltert nach '$filter_str')" : "") . ":\n";
|
||||
sort($categories);
|
||||
foreach ($categories as $cat) {
|
||||
echo "$cat\n";
|
||||
}
|
||||
$this->debug_print("Gesamte Kategorien: " . count($categories));
|
||||
return $categories;
|
||||
}
|
||||
|
||||
public function parse_include_logic($include_str) {
|
||||
if (empty($include_str)) {
|
||||
return [];
|
||||
}
|
||||
$groups = explode(',', $include_str);
|
||||
$parsed_groups = [];
|
||||
foreach ($groups as $group) {
|
||||
$cats = array_unique(explode('+', $group)); // Unique in Gruppe
|
||||
$parsed_groups[] = array_filter(array_map('trim', $cats));
|
||||
}
|
||||
return array_filter($parsed_groups);
|
||||
}
|
||||
|
||||
public function get_words_from_logic($include_groups) {
|
||||
$all_words = [];
|
||||
foreach ($include_groups as $group) {
|
||||
if (empty($group)) continue;
|
||||
$group_members = [];
|
||||
foreach ($group as $cat) {
|
||||
$group_members[] = $this->get_category_members($cat);
|
||||
}
|
||||
if (!empty($group_members)) {
|
||||
$intersection = call_user_func_array('array_intersect', $group_members);
|
||||
$all_words = array_merge($all_words, $intersection);
|
||||
}
|
||||
}
|
||||
return array_unique($all_words);
|
||||
}
|
||||
|
||||
public function generate_wordlist($include, $exclude, $num_words, $output_file, $preferred_variant = null, $headers_flag = false, $format = 'csv') {
|
||||
$this->preferred_variant = $preferred_variant;
|
||||
$include_groups = $this->parse_include_logic($include);
|
||||
if (empty($include_groups)) {
|
||||
echo "Fehler: --include muss angegeben werden, da 'alle' nicht unterstützt wird.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$all_words = $this->get_words_from_logic($include_groups);
|
||||
$this->debug_print("Include-Wörter nach Logik: " . count($all_words));
|
||||
|
||||
$exclude_cats = array_unique(array_map('trim', explode(',', $exclude)));
|
||||
$exclude_words = [];
|
||||
foreach ($exclude_cats as $cat) {
|
||||
if (empty($cat)) continue;
|
||||
echo "Abrufen von Kategorie (exclude): $cat\n";
|
||||
$members = $this->get_category_members($cat);
|
||||
$exclude_words = array_merge($exclude_words, $members);
|
||||
}
|
||||
$this->debug_print("Exclude-Wörter: " . count($exclude_words));
|
||||
|
||||
$all_words = array_diff($all_words, $exclude_words);
|
||||
$this->debug_print("Finale Wörter nach Exclude: " . count($all_words));
|
||||
|
||||
if (empty($all_words)) {
|
||||
echo "Keine Wörter gefunden.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
shuffle($all_words); // Hohe Zufälligkeit
|
||||
$selected_words = array_slice($all_words, 0, $num_words);
|
||||
$this->debug_print("Ausgewählte Wörter: " . count($selected_words));
|
||||
|
||||
echo "Abrufen von Definitionen für " . count($selected_words) . " Wörter...\n";
|
||||
list($definitions, $variants) = $this->get_definitions_batch($selected_words);
|
||||
|
||||
if ($format === 'html') {
|
||||
$output = "<!DOCTYPE html>\n<html lang='en'>\n<head>\n<meta charset='UTF-8'>\n";
|
||||
$output .= "<title>Wiktionary Wortliste</title>\n";
|
||||
$output .= "<link href='https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css' rel='stylesheet'>\n";
|
||||
$output .= "</head>\n<body class='bg-gray-100 p-4'>\n";
|
||||
$output .= "<div class='container mx-auto'>\n";
|
||||
$output .= "<h1 class='text-2xl font-bold mb-4'>Wiktionary Wortliste ({$this->lang})</h1>\n";
|
||||
$output .= "<table class='min-w-full bg-white border border-gray-300'>\n";
|
||||
if ($headers_flag) {
|
||||
$output .= "<thead><tr class='bg-gray-200'><th class='py-2 px-4 border'>Begriff</th><th class='py-2 px-4 border'>Erste Bedeutung</th></tr></thead>\n";
|
||||
}
|
||||
$output .= "<tbody>\n";
|
||||
foreach ($selected_words as $word) {
|
||||
$var = $variants[$word] ?? '';
|
||||
$begriff = $var ? htmlspecialchars($var . ', ' . $word) : htmlspecialchars($word);
|
||||
$defn = htmlspecialchars($definitions[$word] ?? 'Keine Definition gefunden');
|
||||
$output .= "<tr><td class='py-2 px-4 border'>$begriff</td><td class='py-2 px-4 border'>$defn</td></tr>\n";
|
||||
}
|
||||
$output .= "</tbody>\n</table>\n</div>\n</body>\n</html>";
|
||||
if ($this->is_cli) {
|
||||
file_put_contents($output_file, $output);
|
||||
echo "HTML-Tabelle gespeichert in $output_file\n";
|
||||
} else {
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
echo $output;
|
||||
}
|
||||
} else {
|
||||
$file = fopen($output_file, 'w');
|
||||
if ($headers_flag) {
|
||||
fputcsv($file, ['Begriff', 'Erste Bedeutung'], "\t");
|
||||
}
|
||||
foreach ($selected_words as $word) {
|
||||
$var = $variants[$word] ?? '';
|
||||
$begriff = $var ? ($var . ', ' . $word) : $word;
|
||||
$defn = $definitions[$word] ?? 'Keine Definition gefunden';
|
||||
fputcsv($file, [$begriff, $defn], "\t");
|
||||
}
|
||||
fclose($file);
|
||||
echo "Tabelle gespeichert in $output_file\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function print_help() {
|
||||
$help_text = <<<EOT
|
||||
Wiktionary Wortliste Generator
|
||||
|
||||
Dieses Skript generiert eine Wortliste mit Begriffen und deren ersten Bedeutungen aus einer Wiktionary-Instanz
|
||||
oder listet verfügbare Kategorien auf.
|
||||
|
||||
Verfügbare Argumente:
|
||||
--lang, -l <Sprachcode> Sprachcode für die Wiktionary-Instanz (z.B. 'de' für de.wiktionary.org).
|
||||
Default: 'en'. Erforderlich.
|
||||
--include, -i <Logik> Logik für einzubeziehende Kategorien (z.B. 'A+B,C' für (A UND B) ODER C).
|
||||
Erforderlich für Wortlisten (keine Includes führt zu Fehler), ignoriert bei --list-categories.
|
||||
--exclude, -e <Kategorien> Komma-separierte Liste auszuschließender Kategorien (OR-Logik).
|
||||
Optional, ignoriert bei --list-categories.
|
||||
--preferred-variant <Name> Bevorzugte Schreibweise (z.B. 'Schweizerische' – extrahiert aus Excerpt und als erstes ausgegeben).
|
||||
Optional.
|
||||
--filter, -f <Text> Textfilter für Kategorien (Substring-Match, case-insensitive).
|
||||
Optional, nur bei --list-categories relevant.
|
||||
--format, -o <Format> Ausgabeformat: 'csv' (Standard) oder 'html' (Tabelle mit Tailwind CSS).
|
||||
Optional.
|
||||
--debug, -d Aktiviert ausführliche Debug-Ausgaben für API-Aufrufe und Prozesse.
|
||||
Optional.
|
||||
--headers, -r Aktiviert die Ausgabe der Spaltenüberschrift in der Ausgabe (default: deaktiviert).
|
||||
Optional, nur für Wortlisten relevant.
|
||||
--list-categories, -c Listet alle Kategorien der Wiktionary-Instanz (optional mit --filter).
|
||||
Wenn gesetzt, werden andere Parameter (außer --lang, --filter, --debug) ignoriert.
|
||||
--help, -h Zeigt diese Hilfe an und beendet das Skript.
|
||||
num_words Anzahl der Wörter für die Wortliste (Integer).
|
||||
Erforderlich für Wortlisten, ignoriert bei --list-categories.
|
||||
output_file Ausgabedatei für die Wortliste (CSV oder HTML).
|
||||
Erforderlich für Wortlisten, ignoriert bei --list-categories. Default: 'output.csv'.
|
||||
|
||||
Argumentkombinationen:
|
||||
1. Wortliste generieren:
|
||||
- Erforderlich: --lang, --include, num_words, output_file
|
||||
- Optional: --exclude, --preferred-variant, --format, --debug, --headers
|
||||
- Beispiel: php script.php --lang de --include "Substantiv (Deutsch)+Deutsch,Verb (Deutsch)" --exclude "Veraltete Substantive (Deutsch)" --preferred-variant "Schweizerische" --format html 10000 output.html --debug --headers
|
||||
2. Kategorien auflisten:
|
||||
- Erforderlich: --lang, --list-categories
|
||||
- Optional: --filter, --debug
|
||||
- Beispiel: php script.php --lang de --list-categories --filter "Deutsch" --debug
|
||||
|
||||
Hinweise:
|
||||
- Include-Logik: + für UND, , für ODER. Keine Includes führt zu Fehler (da 'alle' nicht unterstützt).
|
||||
- Duplikate in Kategorien werden automatisch entfernt und gecached, um redundante Abfragen zu vermeiden.
|
||||
- Die API-Aufrufe enthalten Pausen (0.5s), um Rate-Limits zu respektieren.
|
||||
- Große Kategorien oder Wortlisten (>10.000) können lange dauern.
|
||||
- Stelle sicher, dass die Kategorien korrekt benannt sind (z.B. 'Substantiv (Deutsch)' für de.wiktionary.org).
|
||||
EOT;
|
||||
echo $help_text . "\n";
|
||||
}
|
||||
|
||||
$is_cli = php_sapi_name() == 'cli';
|
||||
|
||||
if ($is_cli) {
|
||||
$shortopts = "l:i:e:preferred-variant:f:o:d:c:r h";
|
||||
$longopts = ["lang:", "include:", "exclude:", "preferred-variant:", "filter:", "format:", "debug", "list-categories", "headers", "help"];
|
||||
$options = getopt($shortopts, $longopts);
|
||||
|
||||
// Positionale Argumente für CLI
|
||||
$non_opt_args = array_filter($argv, function($arg) { return $arg[0] !== '-'; });
|
||||
$non_opt_args = array_values(array_slice($non_opt_args, 1)); // Entferne Script-Namen
|
||||
$options['num_words'] = $non_opt_args[0] ?? null;
|
||||
$options['output_file'] = $non_opt_args[1] ?? null;
|
||||
|
||||
if (isset($options['help']) || isset($options['h'])) {
|
||||
print_help();
|
||||
exit;
|
||||
}
|
||||
|
||||
$lang = $options['lang'] ?? $options['l'] ?? 'en';
|
||||
$include = $options['include'] ?? $options['i'] ?? '';
|
||||
$exclude = $options['exclude'] ?? $options['e'] ?? '';
|
||||
$preferred_variant = $options['preferred-variant'] ?? null;
|
||||
$filter = $options['filter'] ?? $options['f'] ?? null;
|
||||
$format = $options['format'] ?? $options['o'] ?? 'csv';
|
||||
$debug = isset($options['debug']) || isset($options['d']);
|
||||
$headers_flag = isset($options['headers']) || isset($options['r']);
|
||||
$list_categories = isset($options['list-categories']) || isset($options['c']);
|
||||
$num_words = (int)($options['num_words'] ?? 0);
|
||||
$output_file = $options['output_file'] ?? 'output.csv';
|
||||
|
||||
$client = new WiktionaryClient($lang, $debug, $is_cli);
|
||||
|
||||
if ($list_categories) {
|
||||
$client->list_all_categories($filter);
|
||||
} else {
|
||||
$client->generate_wordlist($include, $exclude, $num_words, $output_file, $preferred_variant, $headers_flag, $format);
|
||||
}
|
||||
} else {
|
||||
// Webserver-Kontext: Parameter aus $_GET
|
||||
$options = $_GET;
|
||||
$lang = $options['lang'] ?? 'en';
|
||||
$include = $options['include'] ?? '';
|
||||
$exclude = $options['exclude'] ?? '';
|
||||
$preferred_variant = $options['preferred-variant'] ?? null;
|
||||
$filter = $options['filter'] ?? null;
|
||||
$format = $options['format'] ?? 'csv';
|
||||
$debug = isset($options['debug']);
|
||||
$headers_flag = isset($options['headers']);
|
||||
$list_categories = isset($options['list-categories']);
|
||||
$num_words = (int)($options['num_words'] ?? 0);
|
||||
$output_file = $options['output_file'] ?? 'output.csv';
|
||||
|
||||
if (isset($options['help'])) {
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
print_help();
|
||||
exit;
|
||||
}
|
||||
|
||||
$client = new WiktionaryClient($lang, $debug, $is_cli);
|
||||
|
||||
if ($list_categories) {
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
$client->list_all_categories($filter);
|
||||
} else {
|
||||
$client->generate_wordlist($include, $exclude, $num_words, $output_file, $preferred_variant, $headers_flag, $format);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,325 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
import random
|
||||
import time
|
||||
import re
|
||||
import argparse
|
||||
import sys
|
||||
from functools import reduce
|
||||
|
||||
def debug_print(msg, debug=False):
|
||||
if debug:
|
||||
print(f"DEBUG: {msg}")
|
||||
|
||||
def print_help():
|
||||
help_text = """
|
||||
Wiktionary Wortliste Generator
|
||||
|
||||
Dieses Skript generiert eine Wortliste mit Begriffen und deren ersten Bedeutungen aus einer Wiktionary-Instanz
|
||||
oder listet verfügbare Kategorien auf.
|
||||
|
||||
Verfügbare Argumente:
|
||||
--lang, -l <Sprachcode> Sprachcode für die Wiktionary-Instanz (z.B. 'de' für de.wiktionary.org).
|
||||
Default: 'en'. Erforderlich.
|
||||
--include, -i <Logik> Logik für einzubeziehende Kategorien (z.B. 'A+B,C' für (A UND B) ODER C).
|
||||
Erforderlich für Wortlisten (keine Includes führt zu Fehler), ignoriert bei --list-categories.
|
||||
--exclude, -e <Kategorien> Komma-separierte Liste auszuschließender Kategorien (OR-Logik).
|
||||
Optional, ignoriert bei --list-categories.
|
||||
--preferred-variant <Name> Bevorzugte Schreibweise (z.B. 'Schweizerische' – extrahiert aus Excerpt und als erstes ausgegeben).
|
||||
Optional.
|
||||
--filter, -f <Text> Textfilter für Kategorien (Substring-Match, case-insensitive).
|
||||
Optional, nur bei --list-categories relevant.
|
||||
--debug, -d Aktiviert ausführliche Debug-Ausgaben für API-Aufrufe und Prozesse.
|
||||
Optional.
|
||||
--headers Aktiviert die Ausgabe der Spaltenüberschrift in der CSV-Datei (default: deaktiviert).
|
||||
Optional, nur für Wortlisten relevant.
|
||||
--list-categories, -c Listet alle Kategorien der Wiktionary-Instanz (optional mit --filter).
|
||||
Wenn gesetzt, werden andere Parameter (außer --lang, --filter, --debug) ignoriert.
|
||||
num_words Anzahl der Wörter für die Wortliste (Integer).
|
||||
Erforderlich für Wortlisten, ignoriert bei --list-categories.
|
||||
output_file Ausgabedatei für die Wortliste (CSV-Format).
|
||||
Erforderlich für Wortlisten, ignoriert bei --list-categories. Default: 'output.csv'.
|
||||
|
||||
Argumentkombinationen:
|
||||
1. Wortliste generieren:
|
||||
- Erforderlich: --lang, --include, num_words, output_file
|
||||
- Optional: --exclude, --preferred-variant, --debug, --headers
|
||||
- Beispiel: python script.py --lang de --include "Substantiv (Deutsch)+Deutsch,Verb (Deutsch)" --exclude "Veraltete Substantive (Deutsch)" --preferred-variant "Schweizerische" 10000 output.csv --debug --headers
|
||||
2. Kategorien auflisten:
|
||||
- Erforderlich: --lang, --list-categories
|
||||
- Optional: --filter, --debug
|
||||
- Beispiel: python script.py --lang de --list-categories --filter "Deutsch" --debug
|
||||
|
||||
Hinweise:
|
||||
- Include-Logik: + für UND, , für ODER. Keine Includes führt zu Fehler (da 'alle' nicht unterstützt).
|
||||
- Duplikate in Kategorien werden automatisch entfernt und gecached, um redundante Abfragen zu vermeiden.
|
||||
- Die API-Aufrufe enthalten Pausen (0.5s), um Rate-Limits zu respektieren.
|
||||
- Große Kategorien oder Wortlisten (>10.000) können lange dauern.
|
||||
- Stelle sicher, dass die Kategorien korrekt benannt sind (z.B. 'Substantiv (Deutsch)' für de.wiktionary.org).
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
def get_category_members(api_url, category, debug=False, category_cache={}):
|
||||
if category in category_cache:
|
||||
debug_print(f"Cache-Hit für Kategorie '{category}': {len(category_cache[category])} Member", debug)
|
||||
return category_cache[category]
|
||||
|
||||
members = set()
|
||||
params = {
|
||||
'action': 'query',
|
||||
'list': 'categorymembers',
|
||||
'cmtitle': f'Category:{category}',
|
||||
'cmtype': 'page',
|
||||
'cmlimit': '500',
|
||||
'format': 'json'
|
||||
}
|
||||
headers = {'User-Agent': 'GrokScript/1.0'}
|
||||
debug_print(f"Starte Abruf für Kategorie '{category}' mit URL: {api_url} und Params: {params}", debug)
|
||||
page = 1
|
||||
total_members = 0
|
||||
while True:
|
||||
try:
|
||||
response = requests.get(api_url, params=params, headers=headers)
|
||||
debug_print(f"Response Status: {response.status_code}, Content-Length: {len(response.text)}", debug)
|
||||
if response.status_code != 200:
|
||||
print(f"Fehler beim Abrufen von {category}: {response.status_code}")
|
||||
debug_print(f"Response Text: {response.text[:500]}...", debug)
|
||||
break
|
||||
data = response.json()
|
||||
debug_print(f"Data Keys: {list(data.keys())}", debug)
|
||||
new_members = [member['title'] for member in data.get('query', {}).get('categorymembers', [])]
|
||||
members.update(new_members)
|
||||
total_members += len(new_members)
|
||||
print(f"\rKategorie '{category}' Paging: Seite {page} abgerufen ({len(new_members)} neue, total {total_members})", end='')
|
||||
if 'continue' in data:
|
||||
params['cmcontinue'] = data['continue']['cmcontinue']
|
||||
debug_print(f"Paginierung fortsetzen mit cmcontinue: {params['cmcontinue']}", debug)
|
||||
page += 1
|
||||
else:
|
||||
print() # Neue Zeile nach Abschluss
|
||||
break
|
||||
time.sleep(0.5) # Pause zur Rate-Limiting-Vermeidung
|
||||
except Exception as e:
|
||||
print(f"Fehler beim API-Aufruf für {category}: {str(e)}")
|
||||
debug_print(f"Exception Details: {type(e).__name__} - {str(e)}", debug)
|
||||
break
|
||||
category_cache[category] = members
|
||||
debug_print(f"Abgerufene Member für '{category}' (cached): {len(members)}", debug)
|
||||
return members
|
||||
|
||||
def parse_include_logic(include_str):
|
||||
if not include_str:
|
||||
return []
|
||||
groups = include_str.split(',')
|
||||
parsed_groups = []
|
||||
for group in groups:
|
||||
cats = set(group.split('+')) # Set für unique in Gruppe
|
||||
parsed_groups.append(list(cats))
|
||||
return parsed_groups
|
||||
|
||||
def get_words_from_logic(api_url, include_groups, debug=False):
|
||||
all_words = set()
|
||||
for group in include_groups:
|
||||
if not group:
|
||||
continue
|
||||
group_members = [get_category_members(api_url, cat, debug) for cat in group]
|
||||
if group_members:
|
||||
intersection = reduce(set.intersection, group_members)
|
||||
all_words.update(intersection)
|
||||
return all_words
|
||||
|
||||
def extract_definition_and_variant(extract, preferred_variant=None):
|
||||
lines = extract.split('\n')
|
||||
definition = 'Keine Definition gefunden'
|
||||
variant = ''
|
||||
in_bedeutungen = False
|
||||
for line in lines:
|
||||
if 'Bedeutungen:' in line:
|
||||
in_bedeutungen = True
|
||||
continue
|
||||
if in_bedeutungen:
|
||||
if line.startswith('[1] '):
|
||||
definition = line[4:].strip()
|
||||
break
|
||||
if line.startswith('[1]'):
|
||||
definition = line.split('[1]', 1)[1].strip()
|
||||
break
|
||||
# Alternative Schreibweise suchen
|
||||
for line in lines:
|
||||
if 'Alternative Schreibweisen:' in line or 'Nebenformen:' in line:
|
||||
# Extrahiere Wörter nach Labels
|
||||
parts = re.split(r'\w+ische Schreibweise:', line) # Split nach Labels wie "Schweizerische Schreibweise:"
|
||||
for part in parts[1:]: # Ignoriere vor dem ersten Label
|
||||
words = re.findall(r'[\w\-]+', part) # Extrahiere Wörter
|
||||
if words:
|
||||
if preferred_variant and preferred_variant.lower() in line.lower():
|
||||
variant = words[0]
|
||||
break
|
||||
elif not preferred_variant:
|
||||
variant = words[0]
|
||||
break
|
||||
if variant:
|
||||
break
|
||||
return definition, variant
|
||||
|
||||
def get_definitions_batch(api_url, words, debug=False):
|
||||
definitions = {}
|
||||
variants = {}
|
||||
headers = {'User-Agent': 'GrokScript/1.0'}
|
||||
for idx, word in enumerate(words):
|
||||
params = {
|
||||
'action': 'query',
|
||||
'prop': 'extracts',
|
||||
'titles': word,
|
||||
'explaintext': '1',
|
||||
'exsectionformat': 'plain',
|
||||
'format': 'json'
|
||||
}
|
||||
debug_print(f"Starte Definitionsabruf für Wort {idx+1}/{len(words)}: '{word}' mit URL: {api_url} und Params: {params}", debug)
|
||||
try:
|
||||
response = requests.get(api_url, params=params, headers=headers)
|
||||
debug_print(f"Response Status: {response.status_code}, Content-Length: {len(response.text)}", debug)
|
||||
if response.status_code != 200:
|
||||
print(f"Fehler beim Abrufen von Definition für '{word}': {response.status_code}")
|
||||
debug_print(f"Response Text: {response.text[:500]}...", debug)
|
||||
definitions[word] = 'Keine Definition gefunden'
|
||||
variants[word] = ''
|
||||
continue
|
||||
data = response.json()
|
||||
debug_print(f"Data Keys: {list(data.keys())}", debug)
|
||||
page = next(iter(data.get('query', {}).get('pages', {}).values()), {})
|
||||
extract = page.get('extract', '')
|
||||
defn, var = extract_definition_and_variant(extract, args.preferred_variant)
|
||||
definitions[word] = defn
|
||||
variants[word] = var
|
||||
print(f"\rDefinitionen abrufen: {idx+1}/{len(words)} abgeschlossen ({word})", end='')
|
||||
time.sleep(0.5) # Pause pro Request zur Rate-Limiting-Vermeidung
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Definitionsabruf für '{word}': {str(e)}")
|
||||
debug_print(f"Exception Details: {type(e).__name__} - {str(e)}", debug)
|
||||
definitions[word] = 'Keine Definition gefunden'
|
||||
variants[word] = ''
|
||||
print() # Neue Zeile nach Abschluss
|
||||
debug_print(f"Abgerufene Definitionen: {len(definitions)}", debug)
|
||||
return definitions, variants
|
||||
|
||||
def list_all_categories(api_url, filter_str=None, debug=False):
|
||||
categories = []
|
||||
params = {
|
||||
'action': 'query',
|
||||
'list': 'allcategories',
|
||||
'aclimit': '500',
|
||||
'format': 'json'
|
||||
}
|
||||
headers = {'User-Agent': 'GrokScript/1.0'}
|
||||
debug_print(f"Starte Abruf aller Kategorien mit URL: {api_url} und Params: {params}", debug)
|
||||
page = 1
|
||||
total_categories = 0
|
||||
while True:
|
||||
try:
|
||||
response = requests.get(api_url, params=params, headers=headers)
|
||||
debug_print(f"Response Status: {response.status_code}, Content-Length: {len(response.text)}", debug)
|
||||
if response.status_code != 200:
|
||||
print("Fehler beim Abrufen der Kategorien")
|
||||
debug_print(f"Response Text: {response.text[:500]}...", debug)
|
||||
break
|
||||
data = response.json()
|
||||
debug_print(f"Data Keys: {list(data.keys())}", debug)
|
||||
new_categories = [cat['*'] for cat in data.get('query', {}).get('allcategories', [])]
|
||||
categories.extend(new_categories)
|
||||
total_categories += len(new_categories)
|
||||
print(f"\rKategorien Paging: Seite {page} abgerufen ({len(new_categories)} neue, total {total_categories})", end='')
|
||||
if 'continue' in data:
|
||||
params['accontinue'] = data['continue']['accontinue']
|
||||
debug_print(f"Paginierung fortsetzen mit accontinue: {params['accontinue']}", debug)
|
||||
page += 1
|
||||
else:
|
||||
print() # Neue Zeile nach Abschluss
|
||||
break
|
||||
time.sleep(0.5) # Pause
|
||||
except Exception as e:
|
||||
print(f"Fehler beim API-Aufruf für Kategorien: {str(e)}")
|
||||
debug_print(f"Exception Details: {type(e).__name__} - {str(e)}", debug)
|
||||
break
|
||||
if filter_str:
|
||||
filter_lower = filter_str.lower()
|
||||
categories = [cat for cat in categories if filter_lower in cat.lower()]
|
||||
debug_print(f"Gefiltert nach '{filter_str}': {len(categories)} Kategorien gefunden", debug)
|
||||
|
||||
print("Vorhandene Kategorien" + (f" (gefiltert nach '{filter_str}')" if filter_str else "") + ":")
|
||||
for cat in sorted(categories):
|
||||
print(cat)
|
||||
debug_print(f"Gesamte Kategorien: {len(categories)}", debug)
|
||||
return categories
|
||||
|
||||
def main(args):
|
||||
category_cache = {} # Globaler Cache für Kategorien
|
||||
if args.help:
|
||||
print_help()
|
||||
return
|
||||
|
||||
api_url = f'https://{args.lang}.wiktionary.org/w/api.php'
|
||||
debug_print(f"API-URL: {api_url}", args.debug)
|
||||
|
||||
if args.list_categories:
|
||||
list_all_categories(api_url, args.filter, args.debug)
|
||||
return
|
||||
|
||||
include_groups = parse_include_logic(args.include)
|
||||
if not include_groups:
|
||||
print("Fehler: --include muss angegeben werden, da 'alle' nicht unterstützt wird.")
|
||||
return
|
||||
|
||||
all_words = get_words_from_logic(api_url, include_groups, args.debug)
|
||||
debug_print(f"Include-Wörter nach Logik: {len(all_words)}", args.debug)
|
||||
|
||||
exclude_cats = list(set([cat.strip() for cat in args.exclude.split(',') if cat.strip()])) if args.exclude else [] # Unique exclude
|
||||
exclude_words = set()
|
||||
for cat in exclude_cats:
|
||||
print(f"Abrufen von Kategorie (exclude): {cat}")
|
||||
members = get_category_members(api_url, cat, args.debug, category_cache)
|
||||
exclude_words.update(members)
|
||||
debug_print(f"Exclude-Wörter: {len(exclude_words)}", args.debug)
|
||||
|
||||
all_words -= exclude_words
|
||||
debug_print(f"Finale Wörter nach Exclude: {len(all_words)}", args.debug)
|
||||
|
||||
if not all_words:
|
||||
print("Keine Wörter gefunden. Überprüfe die Kategorien.")
|
||||
return
|
||||
|
||||
words_list = list(all_words)
|
||||
random.shuffle(words_list)
|
||||
selected_words = words_list[:args.num_words]
|
||||
debug_print(f"Ausgewählte Wörter: {len(selected_words)}", args.debug)
|
||||
|
||||
print(f"Abrufen von Definitionen für {len(selected_words)} Wörter...")
|
||||
definitions, variants = get_definitions_batch(api_url, selected_words, args.debug)
|
||||
|
||||
# Erstelle Tabelle (als CSV-Datei)
|
||||
with open(args.output_file, 'w', encoding='utf-8') as f:
|
||||
if args.headers:
|
||||
f.write("Begriff\tErste Bedeutung\n")
|
||||
for word in selected_words:
|
||||
var = variants.get(word, '')
|
||||
begriff = f"{var}, {word}" if var else word
|
||||
defn = definitions.get(word, 'Keine Definition gefunden')
|
||||
f.write(f"{begriff}\t{defn}\n")
|
||||
print(f"Tabelle gespeichert in {args.output_file}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Wiktionary Wortliste Generator", add_help=False)
|
||||
parser.add_argument('--lang', '-l', default='en', help='Sprachcode für Wiktionary (z.B. de für de.wiktionary.org)')
|
||||
parser.add_argument('--include', '-i', default='', help='Logik für einzubeziehende Kategorien (z.B. A+B,C)')
|
||||
parser.add_argument('--exclude', '-e', default='', help='Komma-separierte Liste auszuschließender Kategorien')
|
||||
parser.add_argument('--preferred-variant', default=None, help='Bevorzugte Schreibweise (z.B. Schweizerische)')
|
||||
parser.add_argument('num_words', type=int, nargs='?', default=0, help='Anzahl der Wörter')
|
||||
parser.add_argument('output_file', nargs='?', default='output.csv', help='Ausgabedatei (CSV)')
|
||||
parser.add_argument('--list-categories', '-c', action='store_true', help='Liste alle Kategorien der Wiktionary-Instanz')
|
||||
parser.add_argument('--filter', '-f', default=None, help='Textfilter für Kategorien (Substring-Match, case-insensitive)')
|
||||
parser.add_argument('--debug', '-d', action='store_true', help='Aktiviere Debug-Ausgaben')
|
||||
parser.add_argument('--headers', action='store_true', help='Aktiviere Spaltenüberschrift in der Ausgabe')
|
||||
parser.add_argument('--help', '-h', action='store_true', help='Zeige diese Hilfe an')
|
||||
args = parser.parse_args()
|
||||
main(args)
|
||||
@@ -0,0 +1,410 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Wiktionary Random Word Generator 4.2 (Korrigierte Version)
|
||||
|
||||
Erstellt zufällige Wortlisten mit Definitionen aus Wiktionary.
|
||||
- Echte Zufälligkeit durch MediaWiki Random API
|
||||
- Integrierte Definition-Extraktion
|
||||
- Kategorie-Filter und Zahlen-Ausschluss
|
||||
- Debug-Modus für Fehlerdiagnose
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import time
|
||||
import csv
|
||||
import re
|
||||
import requests
|
||||
import random
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
class WiktionaryRandomGenerator:
|
||||
def __init__(self, language: str, debug: bool = False):
|
||||
self.language = language
|
||||
self.debug = debug
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({'User-Agent': 'Wiktionary Generator 4.2'})
|
||||
|
||||
# API URLs
|
||||
self.api_url = {
|
||||
'de': 'https://de.wiktionary.org/w/api.php',
|
||||
'en': 'https://en.wiktionary.org/w/api.php'
|
||||
}[language]
|
||||
|
||||
# Wortarten-Kategorien (für Filterung)
|
||||
self.pos_categories = {
|
||||
'de': {
|
||||
'substantiv': 'Kategorie:Substantiv (Deutsch)',
|
||||
'verb': 'Kategorie:Verb (Deutsch)',
|
||||
'adjektiv': 'Kategorie:Adjektiv (Deutsch)',
|
||||
'adverb': 'Kategorie:Adverb (Deutsch)',
|
||||
'artikel': 'Kategorie:Artikel (Deutsch)',
|
||||
'pronomen': 'Kategorie:Pronomen (Deutsch)',
|
||||
'präposition': 'Kategorie:Präposition (Deutsch)',
|
||||
'konjunktion': 'Kategorie:Konjunktion (Deutsch)',
|
||||
'interjektion': 'Kategorie:Interjektion (Deutsch)'
|
||||
},
|
||||
'en': {
|
||||
'noun': 'Category:English nouns',
|
||||
'verb': 'Category:English verbs',
|
||||
'adjective': 'Category:English adjectives',
|
||||
'adverb': 'Category:English adverbs',
|
||||
'pronoun': 'Category:English pronouns',
|
||||
'preposition': 'Category:English prepositions',
|
||||
'conjunction': 'Category:English conjunctions',
|
||||
'interjection': 'Category:English interjections'
|
||||
}
|
||||
}[language]
|
||||
|
||||
# Ausschluss-Kategorien (Namen, Geografie)
|
||||
self.excluded_categories = {
|
||||
'de': {
|
||||
'Kategorie:Eigenname (Deutsch)',
|
||||
'Kategorie:Vorname (Deutsch)',
|
||||
'Kategorie:Nachname (Deutsch)',
|
||||
'Kategorie:Toponym (Deutsch)',
|
||||
'Kategorie:Stadt (Deutsch)',
|
||||
'Kategorie:Land (Deutsch)'
|
||||
},
|
||||
'en': {
|
||||
'Category:English proper nouns',
|
||||
'Category:English given names',
|
||||
'Category:English surnames'
|
||||
}
|
||||
}[language]
|
||||
|
||||
# Standard-Wortarten
|
||||
self.default_pos = ['substantiv', 'verb', 'adjektiv'] if language == 'de' else ['noun', 'verb', 'adjective']
|
||||
|
||||
self.debug_print(f"Initialisiert für Sprache: {language}")
|
||||
self.debug_print(f"API URL: {self.api_url}")
|
||||
self.debug_print(f"Standard-Wortarten: {self.default_pos}")
|
||||
|
||||
def debug_print(self, msg: str):
|
||||
if self.debug:
|
||||
print(f"[DEBUG] {msg}")
|
||||
|
||||
def has_numbers(self, word: str) -> bool:
|
||||
"""Prüft ob ein Wort Ziffern enthält"""
|
||||
result = bool(re.search(r'\d', word))
|
||||
if result and self.debug:
|
||||
self.debug_print(f"Wort '{word}' enthält Zahlen -> ausgeschlossen")
|
||||
return result
|
||||
|
||||
def clean_definition(self, text: str) -> str:
|
||||
"""Bereinigt Definition von Wiki-Markup und Formatierung"""
|
||||
if not text:
|
||||
return ""
|
||||
|
||||
original = text
|
||||
# Entferne führende Nummerierungen: [1], 2), etc.
|
||||
text = re.sub(r'^\s*\[?\d+\]?\s*[\]\)]*\s*', '', text)
|
||||
# Entferne führende Sonderzeichen: :, •, -, etc.
|
||||
text = re.sub(r'^\s*[:\-\*\+>•]+\s*', '', text)
|
||||
# Entferne führende Klammern/Anführungszeichen
|
||||
text = re.sub(r'^\s*[\(\[\"\'`]+\s*', '', text)
|
||||
# Normalisiere Leerzeichen
|
||||
text = re.sub(r'\s+', ' ', text).strip()
|
||||
|
||||
# Kleinschreibung am Anfang (außer bei Eigennamen)
|
||||
if text and len(text) > 1 and text[0].isupper() and text[1].islower():
|
||||
text = text[0].lower() + text[1:]
|
||||
|
||||
if self.debug and original != text:
|
||||
self.debug_print(f"Definition bereinigt: '{original[:30]}...' -> '{text[:30]}...'")
|
||||
|
||||
return text
|
||||
|
||||
def fetch_random_pages(self, count: int) -> List[Dict]:
|
||||
"""
|
||||
Holt zufällige Seiten mit Definitionen und Kategorien.
|
||||
Verwendet zwei API-Calls: random + details
|
||||
"""
|
||||
self.debug_print(f"Hole {count} zufällige Seiten")
|
||||
|
||||
# Schritt 1: Hole zufällige Page-IDs
|
||||
try:
|
||||
params = {
|
||||
'action': 'query',
|
||||
'format': 'json',
|
||||
'list': 'random',
|
||||
'rnlimit': min(count, 500),
|
||||
'rnnamespace': 0 # Nur Hauptnamensraum
|
||||
}
|
||||
|
||||
self.debug_print(f"Random API Call: {params}")
|
||||
response = self.session.get(self.api_url, params=params, timeout=15)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if 'query' not in data or 'random' not in data['query']:
|
||||
self.debug_print(f"Fehler in Random API Response: {data}")
|
||||
return []
|
||||
|
||||
random_pages = data['query']['random']
|
||||
page_ids = [str(page['id']) for page in random_pages]
|
||||
titles = [page['title'] for page in random_pages]
|
||||
|
||||
self.debug_print(f"Gefunden: {len(page_ids)} zufällige Seiten")
|
||||
|
||||
if not page_ids:
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
self.debug_print(f"Fehler beim Random API Call: {e}")
|
||||
return []
|
||||
|
||||
# Schritt 2: Hole Details (Extract + Categories) für diese IDs
|
||||
try:
|
||||
params = {
|
||||
'action': 'query',
|
||||
'format': 'json',
|
||||
'pageids': '|'.join(page_ids),
|
||||
'prop': 'extracts|categories',
|
||||
'exintro': True,
|
||||
'explaintext': True,
|
||||
'cllimit': 500
|
||||
}
|
||||
|
||||
self.debug_print(f"Details API Call: {params}")
|
||||
response = self.session.get(self.api_url, params=params, timeout=15)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if 'query' not in data or 'pages' not in data['query']:
|
||||
self.debug_print(f"Fehler in Details API Response: {data}")
|
||||
return []
|
||||
|
||||
pages = data['query']['pages']
|
||||
results = []
|
||||
|
||||
for page_id, page_data in pages.items():
|
||||
if page_id == '-1': # Ungültige Seite
|
||||
continue
|
||||
|
||||
title = page_data.get('title', '')
|
||||
extract = page_data.get('extract', '')
|
||||
categories = set()
|
||||
|
||||
# Kategorien sammeln
|
||||
if 'categories' in page_data:
|
||||
categories = {cat['title'] for cat in page_data['categories']}
|
||||
|
||||
results.append({
|
||||
'title': title,
|
||||
'extract': extract,
|
||||
'categories': categories
|
||||
})
|
||||
|
||||
self.debug_print(f"Seite '{title}': Extract={len(extract)} chars, Categories={len(categories)}")
|
||||
|
||||
self.debug_print(f"Details für {len(results)} Seiten geholt")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
self.debug_print(f"Fehler beim Details API Call: {e}")
|
||||
return []
|
||||
|
||||
def check_word_categories(self, categories: set, include_pos: List[str]) -> bool:
|
||||
"""
|
||||
Prüft ob ein Wort in den gewünschten Wortart-Kategorien ist.
|
||||
Wenn include_pos leer ist, werden alle Wörter akzeptiert.
|
||||
"""
|
||||
if not include_pos:
|
||||
return True # Keine POS-Filter
|
||||
|
||||
# Prüfe ob eine der gewünschten Kategorien vorhanden ist
|
||||
desired_categories = {self.pos_categories[pos] for pos in include_pos
|
||||
if pos in self.pos_categories}
|
||||
|
||||
has_desired_category = bool(categories & desired_categories)
|
||||
|
||||
if self.debug:
|
||||
if has_desired_category:
|
||||
matching = categories & desired_categories
|
||||
self.debug_print(f"Wort in gewünschter Kategorie: {matching}")
|
||||
else:
|
||||
self.debug_print(f"Wort nicht in gewünschten Kategorien: {desired_categories}")
|
||||
|
||||
return has_desired_category
|
||||
|
||||
def is_excluded_word(self, categories: set) -> bool:
|
||||
"""Prüft ob ein Wort in Ausschluss-Kategorien ist"""
|
||||
excluded = bool(categories & self.excluded_categories)
|
||||
if excluded and self.debug:
|
||||
matching = categories & self.excluded_categories
|
||||
self.debug_print(f"Wort ausgeschlossen wegen: {matching}")
|
||||
return excluded
|
||||
|
||||
def generate(self, count: int, include_pos: List[str], include_names: bool,
|
||||
exclude_numbers: bool, outfile: str):
|
||||
"""Generiert die Wortliste"""
|
||||
|
||||
pos = include_pos or []
|
||||
self.debug_print(f"Parameter: count={count}, pos={pos}, names={include_names}, numbers={exclude_numbers}")
|
||||
|
||||
print(f"Generiere {count} zufällige Wörter aus {self.language.upper()}-Wiktionary")
|
||||
if pos:
|
||||
print(f"Wortarten-Filter: {', '.join(pos)}")
|
||||
else:
|
||||
print("Keine Wortarten-Filter (alle Wörter)")
|
||||
print(f"Namen einschließen: {'Ja' if include_names else 'Nein'}")
|
||||
print(f"Zahlen ausschließen: {'Ja' if exclude_numbers else 'Nein'}")
|
||||
print()
|
||||
|
||||
results = []
|
||||
attempts = 0
|
||||
max_attempts = count * 5 # Maximal 5x so viele Versuche
|
||||
|
||||
print("Sammle zufällige Kandidaten...")
|
||||
|
||||
while len(results) < count and attempts < max_attempts:
|
||||
batch_size = min(50, (count - len(results)) * 2) # Hole mehr als nötig
|
||||
candidates = self.fetch_random_pages(batch_size)
|
||||
|
||||
if not candidates:
|
||||
self.debug_print("Keine Kandidaten erhalten")
|
||||
break
|
||||
|
||||
self.debug_print(f"Verarbeite {len(candidates)} Kandidaten...")
|
||||
|
||||
for candidate in candidates:
|
||||
if len(results) >= count:
|
||||
break
|
||||
|
||||
title = candidate['title']
|
||||
extract = candidate['extract']
|
||||
categories = candidate['categories']
|
||||
|
||||
self.debug_print(f"Prüfe Kandidat: '{title}'")
|
||||
|
||||
# Filter 1: Namen ausschließen (falls gewünscht)
|
||||
if not include_names and self.is_excluded_word(categories):
|
||||
self.debug_print(f"'{title}' wegen Namen-Filter übersprungen")
|
||||
continue
|
||||
|
||||
# Filter 2: Zahlen ausschließen (falls gewünscht)
|
||||
if exclude_numbers and self.has_numbers(title):
|
||||
continue
|
||||
|
||||
# Filter 3: Wortarten-Filter (falls angegeben)
|
||||
if pos and not self.check_word_categories(categories, pos):
|
||||
self.debug_print(f"'{title}' wegen POS-Filter übersprungen")
|
||||
continue
|
||||
|
||||
# Filter 4: Definition muss vorhanden sein
|
||||
if not extract or len(extract) < 10:
|
||||
self.debug_print(f"'{title}' hat keine ausreichende Definition")
|
||||
continue
|
||||
|
||||
# Definition bereinigen
|
||||
cleaned_definition = self.clean_definition(extract)
|
||||
if len(cleaned_definition) < 10:
|
||||
self.debug_print(f"'{title}' Definition zu kurz nach Bereinigung")
|
||||
continue
|
||||
|
||||
# Wort akzeptieren
|
||||
results.append((title, cleaned_definition))
|
||||
print(f" {len(results):2d}/{count}: {title}")
|
||||
|
||||
self.debug_print(f"'{title}' erfolgreich hinzugefügt")
|
||||
|
||||
attempts += batch_size
|
||||
|
||||
# Kleine Pause zwischen Batches
|
||||
if len(results) < count:
|
||||
time.sleep(0.3)
|
||||
|
||||
if not results:
|
||||
print("FEHLER: Keine Wörter generiert! Versuchen Sie:")
|
||||
print("- --include-names für mehr Kandidaten")
|
||||
print("- Andere/keine --include-pos Filter")
|
||||
print("- --debug für detaillierte Ausgabe")
|
||||
sys.exit(1)
|
||||
|
||||
# Finale Mischung und Begrenzung
|
||||
random.shuffle(results)
|
||||
results = results[:count]
|
||||
|
||||
print(f"\n✅ Erfolgreich {len(results)} Wörter generiert!")
|
||||
print(f"💾 Speichere in '{outfile}'...")
|
||||
|
||||
# CSV-Datei schreiben
|
||||
try:
|
||||
with open(outfile, 'w', encoding='utf-8', newline='') as f:
|
||||
writer = csv.writer(f, delimiter='\t')
|
||||
writer.writerow(['Wort', 'Definition', 'Sprache', 'Zeitstempel'])
|
||||
|
||||
for word, definition in results:
|
||||
writer.writerow([
|
||||
word,
|
||||
definition,
|
||||
self.language,
|
||||
datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
])
|
||||
|
||||
print(f"✅ Datei '{outfile}' erfolgreich erstellt!")
|
||||
|
||||
# Beispiele anzeigen
|
||||
print("\n📖 Beispiele aus der generierten Liste:")
|
||||
print("-" * 65)
|
||||
for i, (word, definition) in enumerate(results[:5], 1):
|
||||
short_def = definition[:50] + "..." if len(definition) > 50 else definition
|
||||
print(f"{i:2d}. {word:15s} → {short_def}")
|
||||
|
||||
if len(results) > 5:
|
||||
print(f" ... und {len(results) - 5} weitere Wörter")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ FEHLER beim Schreiben der Datei: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Wiktionary Random Word Generator 4.2',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Beispiele:
|
||||
python script.py -c 30 -l de
|
||||
python script.py -c 50 --include-pos substantiv verb
|
||||
python script.py -c 25 -l en --exclude-numbers
|
||||
python script.py -c 40 --include-names --debug
|
||||
|
||||
Verfügbare Wortarten:
|
||||
Deutsch: substantiv, verb, adjektiv, adverb, artikel, pronomen, präposition, konjunktion, interjektion
|
||||
Englisch: noun, verb, adjective, adverb, pronoun, preposition, conjunction, interjection
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('-c', '--count', type=int, default=50,
|
||||
help='Anzahl der zu generierenden Wörter (Standard: 50)')
|
||||
parser.add_argument('-l', '--language', choices=['de', 'en'], default='de',
|
||||
help='Wiktionary-Sprache (Standard: de)')
|
||||
parser.add_argument('-o', '--output', default='wiktionary_words.csv',
|
||||
help='Output-Datei (Standard: wiktionary_words.csv)')
|
||||
parser.add_argument('--include-pos', nargs='+',
|
||||
help='Nur diese Wortarten einschließen')
|
||||
parser.add_argument('--include-names', action='store_true',
|
||||
help='Namen und geografische Bezeichnungen einschließen')
|
||||
parser.add_argument('--exclude-numbers', action='store_true',
|
||||
help='Wörter mit Ziffern ausschließen')
|
||||
parser.add_argument('--debug', action='store_true',
|
||||
help='Debug-Modus für detaillierte Ausgabe')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validierung
|
||||
if args.count <= 0:
|
||||
print("❌ Anzahl muss größer als 0 sein!")
|
||||
sys.exit(1)
|
||||
|
||||
# Generator erstellen und ausführen
|
||||
gen = WiktionaryRandomGenerator(args.language, debug=args.debug)
|
||||
gen.generate(
|
||||
count=args.count,
|
||||
include_pos=args.include_pos,
|
||||
include_names=args.include_names,
|
||||
exclude_numbers=args.exclude_numbers,
|
||||
outfile=args.output
|
||||
)
|
||||
Reference in new issue
Block a user