Multilingual Content-Strategy: Drupal 11 mit DeepL für 30+ Sprachen
Multilingual Content-Strategy: Drupal 11 mit DeepL für 30+ Sprachen
Die Verwaltung mehrsprachiger Websites mit 30+ Sprachen stellt besondere Herausforderungen an Content-Management, Translation-Workflows und Performance. In diesem Enterprise-Guide zeige ich, wie Sie mit Drupal 11 und der DeepL API eine skalierbare, kosteneffiziente Multilingual-Lösung aufbauen.
Warum Drupal 11 + DeepL?
Vorteile der Kombination
Drupal 11 Multilingual:
- Native i18n Support seit Core
- Content Translation Module (Core)
- Configuration Translation
- Interface Translation
- Language Detection & Selection
DeepL API:
- 31 Sprachen verfügbar (Stand 2025)
- Formality API (Du/Sie, Tu/Vous)
- Glossary Support für Fachterminologie
- Context-aware Übersetzungen
- EU-Server, DSGVO-konform
Kostenvorteil:
- DeepL: ~€5-20 pro 1 Million Zeichen
- Professionelle Übersetzer: €150-300 pro 1000 Wörter
- Einsparung: 95-98% bei akzeptabler Qualität
Architektur-Übersicht
┌─────────────────────────────────────────┐
│ Content Creation (Source Language) │
└──────────────────┬──────────────────────┘
│
┌─────────▼─────────┐
│ Translation Queue │
└─────────┬─────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│DeepL │ │Human │ │Hybrid │
│ API │ │Review │ │Workflow│
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
└──────────────┼──────────────┘
│
┌─────────▼─────────┐
│ Published Langs │
└───────────────────┘
Setup: Drupal 11 Multilingual
Schritt 1: Core Module aktivieren
# Language Module
drush en language -y
# Content Translation
drush en content_translation -y
# Configuration Translation
drush en config_translation -y
# Interface Translation
drush en locale -y
# Language Icons (optional)
composer require drupal/languageicons
drush en languageicons -y
Schritt 2: Sprachen hinzufügen
Via Drush (empfohlen für bulk):
# Einzelne Sprache
drush language:add de
# Multiple Sprachen
LANGUAGES=(de fr es it nl pl pt ru ja zh-hans)
for lang in "${LANGUAGES[@]}"; do
drush language:add $lang
done
Via UI:
/admin/config/regional/language/add
Schritt 3: Content Types konfigurieren
// In einem Custom Module: mymodule.install
/**
* Enable translation for node types.
*/
function mymodule_update_9001() {
$entity_types = ['node', 'taxonomy_term', 'block_content'];
$bundles = [
'node' => ['article', 'page', 'product'],
'taxonomy_term' => ['tags', 'categories'],
'block_content' => ['basic'],
];
foreach ($entity_types as $entity_type) {
foreach ($bundles[$entity_type] as $bundle) {
\Drupal::service('content_translation.manager')
->setEnabled($entity_type, $bundle, TRUE);
}
}
}
Via UI:
/admin/config/regional/content-language
DeepL Glossary Integration
Glossare ermöglichen konsistente Übersetzung von Fachbegriffen.
Glossar erstellen
<?php
namespace Drupal\mymodule\Service;
use DeepL\Translator;
use DeepL\GlossaryEntries;
/**
* DeepL Glossary Manager.
*/
class DeepLGlossaryManager {
/**
* The DeepL Translator.
*
* @var \DeepL\Translator
*/
protected $translator;
/**
* Constructor.
*/
public function __construct() {
$api_key = \Drupal::config('deepl_translator.settings')->get('api_key');
$this->translator = new Translator($api_key);
}
/**
* Creates a glossary.
*
* @param string $name
* Glossary name.
* @param string $source_lang
* Source language.
* @param string $target_lang
* Target language.
* @param array $entries
* Key-value pairs of terms.
*
* @return string
* Glossary ID.
*/
public function createGlossary(
string $name,
string $source_lang,
string $target_lang,
array $entries
): string {
$glossary_entries = GlossaryEntries::fromEntries($entries);
$glossary = $this->translator->createGlossary(
$name,
$source_lang,
$target_lang,
$glossary_entries
);
// Speichern für später
\Drupal::state()->set("deepl_glossary_{$source_lang}_{$target_lang}", [
'id' => $glossary->glossaryId,
'name' => $name,
'source_lang' => $source_lang,
'target_lang' => $target_lang,
'entry_count' => $glossary->entryCount,
]);
return $glossary->glossaryId;
}
/**
* Gets glossary for language pair.
*/
public function getGlossaryId(string $source_lang, string $target_lang): ?string {
$glossary = \Drupal::state()->get("deepl_glossary_{$source_lang}_{$target_lang}");
return $glossary['id'] ?? null;
}
/**
* Lists all glossaries.
*/
public function listGlossaries(): array {
$glossaries = $this->translator->listGlossaries();
$result = [];
foreach ($glossaries as $glossary) {
$result[] = [
'id' => $glossary->glossaryId,
'name' => $glossary->name,
'source' => $glossary->sourceLang,
'target' => $glossary->targetLang,
'entries' => $glossary->entryCount,
'created' => $glossary->creationTime,
];
}
return $result;
}
/**
* Deletes a glossary.
*/
public function deleteGlossary(string $glossary_id): void {
$this->translator->deleteGlossary($glossary_id);
}
}
Glossar verwenden
// Beispiel: Technisches Glossar
$technical_terms = [
'API' => 'API',
'Backend' => 'Backend',
'Frontend' => 'Frontend',
'Cache' => 'Cache',
'Database' => 'Datenbank',
'Server' => 'Server',
'User' => 'Benutzer',
'Admin' => 'Administrator',
'Login' => 'Anmeldung',
'Logout' => 'Abmeldung',
];
$glossary_manager = \Drupal::service('mymodule.deepl_glossary');
$glossary_id = $glossary_manager->createGlossary(
'Technical Terms EN-DE',
'EN',
'DE',
$technical_terms
);
// Übersetzen mit Glossar
$translator = \Drupal::service('deepl_translator.client');
$translation = $translator->translateText(
'Please login to access the admin backend.',
'DE',
'EN',
['glossary_id' => $glossary_id]
);
// Ergebnis: "Bitte melden Sie sich an, um auf das Administrator-Backend zuzugreifen."
Translation Workflow
1. Automated Translation Workflow
<?php
namespace Drupal\mymodule\Service;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Queue\QueueFactory;
/**
* Translation Workflow Manager.
*/
class TranslationWorkflow {
/**
* Queue a node for translation.
*/
public function queueTranslation(
ContentEntityInterface $entity,
array $target_languages,
string $workflow = 'auto'
): void {
$queue = \Drupal::queue('deepl_translation_queue');
foreach ($target_languages as $lang) {
$item = [
'entity_type' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
'target_language' => $lang,
'workflow' => $workflow,
'timestamp' => time(),
];
$queue->createItem($item);
}
\Drupal::logger('translation_workflow')->info('Queued @entity for translation to @count languages', [
'@entity' => $entity->label(),
'@count' => count($target_languages),
]);
}
/**
* Process translation queue.
*/
public function processQueue(): void {
$queue = \Drupal::queue('deepl_translation_queue');
$queue_worker = \Drupal::service('plugin.manager.queue_worker')
->createInstance('deepl_translation_queue');
while ($item = $queue->claimItem()) {
try {
$queue_worker->processItem($item->data);
$queue->deleteItem($item);
}
catch (\Exception $e) {
\Drupal::logger('translation_workflow')->error('Failed to process queue item: @error', [
'@error' => $e->getMessage(),
]);
// Release item for retry
$queue->releaseItem($item);
}
}
}
}
2. Hybrid Workflow (Auto + Human Review)
/**
* Translation with review workflow.
*/
class HybridTranslationWorkflow {
/**
* Translate with pending review state.
*/
public function translateWithReview(
ContentEntityInterface $entity,
string $target_language
): void {
// 1. Auto-translate with DeepL
$translator = \Drupal::service('deepl_translator.translator');
$translation = $translator->translateEntity($entity, $target_language);
// 2. Set moderation state to "needs_review"
if ($translation->hasField('moderation_state')) {
$translation->set('moderation_state', 'needs_review');
}
// 3. Add metadata for reviewer
$translation->set('field_translation_info', [
'value' => json_encode([
'auto_translated' => true,
'service' => 'DeepL',
'translated_at' => date('c'),
'source_language' => $entity->language()->getId(),
'requires_review' => true,
]),
]);
// 4. Notify reviewer
$this->notifyReviewer($translation, $target_language);
$translation->save();
}
/**
* Notify translator for review.
*/
protected function notifyReviewer($entity, $lang): void {
$reviewer = $this->getReviewerForLanguage($lang);
if ($reviewer) {
$params = [
'entity' => $entity,
'language' => $lang,
'edit_url' => $entity->toUrl('edit-form')->setAbsolute()->toString(),
];
\Drupal::service('plugin.manager.mail')->mail(
'mymodule',
'translation_review',
$reviewer->getEmail(),
$lang,
$params
);
}
}
}
Content Strategy
Übersetzungs-Prioritäten
/**
* Content Translation Priority Manager.
*/
class TranslationPriority {
/**
* Priority levels.
*/
const PRIORITY_CRITICAL = 1; // Homepage, Landing Pages
const PRIORITY_HIGH = 2; // Products, Services
const PRIORITY_MEDIUM = 3; // Blog Posts, News
const PRIORITY_LOW = 4; // Archive, Documentation
/**
* Determine translation priority.
*/
public function getPriority(ContentEntityInterface $entity): int {
// Critical: Promoted content
if ($entity->hasField('promote') && $entity->get('promote')->value) {
return self::PRIORITY_CRITICAL;
}
// High: Product content types
if ($entity->bundle() === 'product') {
return self::PRIORITY_HIGH;
}
// Medium: Recent content
if ($entity->hasField('created')) {
$created = $entity->get('created')->value;
if ($created > (time() - 2592000)) { // 30 days
return self::PRIORITY_MEDIUM;
}
}
return self::PRIORITY_LOW;
}
/**
* Get languages based on priority.
*/
public function getTargetLanguages(int $priority): array {
$all_languages = \Drupal::languageManager()->getLanguages();
switch ($priority) {
case self::PRIORITY_CRITICAL:
// All languages
return array_keys($all_languages);
case self::PRIORITY_HIGH:
// Major European languages
return ['de', 'fr', 'es', 'it', 'nl', 'pl'];
case self::PRIORITY_MEDIUM:
// Top 3 languages
return ['de', 'fr', 'es'];
case self::PRIORITY_LOW:
// On-demand only
return [];
}
return [];
}
}
Formality Strategy
/**
* Formality Manager for different content types.
*/
class FormalityStrategy {
/**
* Determine formality level.
*/
public function getFormality(ContentEntityInterface $entity): string {
// B2B Content: Formal
if ($entity->hasField('field_audience') &&
$entity->get('field_audience')->value === 'b2b') {
return 'more'; // Sie/Vous/Usted
}
// B2C Content: Informal
if ($entity->bundle() === 'blog_post') {
return 'less'; // Du/Tu/Tú
}
// Legal/Compliance: Formal
if ($entity->bundle() === 'legal_document') {
return 'more';
}
// Default: Context-aware
return 'default';
}
}
Performance Optimization
1. Batch Processing
/**
* Batch translation for large content sets.
*/
class BatchTranslation {
/**
* Create batch for mass translation.
*/
public static function createBatch(array $entity_ids, array $target_languages): array {
$operations = [];
// Split into chunks of 10
$chunks = array_chunk($entity_ids, 10);
foreach ($chunks as $chunk) {
foreach ($target_languages as $lang) {
$operations[] = [
[self::class, 'processBatch'],
[$chunk, $lang],
];
}
}
return [
'title' => t('Translating content...'),
'operations' => $operations,
'finished' => [self::class, 'finishBatch'],
'progressive' => true,
];
}
/**
* Process batch operation.
*/
public static function processBatch($entity_ids, $target_lang, &$context) {
$translator = \Drupal::service('deepl_translator.translator');
$storage = \Drupal::entityTypeManager()->getStorage('node');
foreach ($entity_ids as $id) {
$entity = $storage->load($id);
if ($entity && !$entity->hasTranslation($target_lang)) {
try {
$translator->translateEntity($entity, $target_lang);
$context['results']['success'][] = $id;
}
catch (\Exception $e) {
$context['results']['failed'][] = $id;
}
}
}
$context['message'] = t('Translated @count entities', [
'@count' => count($context['results']['success']),
]);
}
}
2. Caching Strategy
/**
* Translation cache manager.
*/
class TranslationCache {
/**
* Cache translation result.
*/
public function cacheTranslation(
string $text,
string $source,
string $target,
string $result
): void {
$cache_key = $this->generateCacheKey($text, $source, $target);
\Drupal::cache('translation')->set(
$cache_key,
$result,
time() + 2592000, // 30 days
['translation', "translation:$source:$target"]
);
}
/**
* Get cached translation.
*/
public function getCachedTranslation(
string $text,
string $source,
string $target
): ?string {
$cache_key = $this->generateCacheKey($text, $source, $target);
$cached = \Drupal::cache('translation')->get($cache_key);
return $cached ? $cached->data : null;
}
/**
* Generate cache key.
*/
protected function generateCacheKey(
string $text,
string $source,
string $target
): string {
return 'translation:' . md5($text . $source . $target);
}
}
SEO für Multilingual Sites
1. Hreflang Tags
<?php
/**
* Implements hook_page_attachments().
*/
function mymodule_page_attachments(array &$attachments) {
$route_match = \Drupal::routeMatch();
if ($route_match->getRouteName() === 'entity.node.canonical') {
$node = $route_match->getParameter('node');
if ($node instanceof \Drupal\node\NodeInterface) {
$languages = \Drupal::languageManager()->getLanguages();
foreach ($languages as $language) {
$lang_code = $language->getId();
if ($node->hasTranslation($lang_code)) {
$translation = $node->getTranslation($lang_code);
$url = $translation->toUrl()->setAbsolute()->toString();
$attachments['#attached']['html_head'][] = [
[
'#tag' => 'link',
'#attributes' => [
'rel' => 'alternate',
'hreflang' => $lang_code,
'href' => $url,
],
],
'hreflang_' . $lang_code,
];
}
}
}
}
}
2. Structured Data per Language
/**
* JSON-LD Structured Data for each language.
*/
function mymodule_preprocess_node(&$variables) {
$node = $variables['node'];
$current_lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
$structured_data = [
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => $node->getTranslation($current_lang)->getTitle(),
'inLanguage' => $current_lang,
'url' => $node->toUrl()->setAbsolute()->toString(),
];
$variables['#attached']['html_head'][] = [
[
'#tag' => 'script',
'#attributes' => ['type' => 'application/ld+json'],
'#value' => json_encode($structured_data),
],
'structured_data_article',
];
}
Cost Optimization
Übersetzungs-Budget Manager
/**
* Translation Budget Manager.
*/
class BudgetManager {
/**
* Calculate translation cost.
*/
public function calculateCost(
ContentEntityInterface $entity,
array $target_languages
): array {
$total_chars = 0;
$fields = $this->getTranslatableFields($entity);
foreach ($fields as $field_name) {
$value = $entity->get($field_name)->value;
$total_chars += mb_strlen(strip_tags($value));
}
$chars_per_lang = $total_chars * count($target_languages);
// DeepL Pricing: ~€0.00002 per character
$cost_eur = $chars_per_lang * 0.00002;
return [
'characters' => $total_chars,
'total_characters' => $chars_per_lang,
'languages' => count($target_languages),
'cost_eur' => round($cost_eur, 2),
'cost_per_language' => round($cost_eur / count($target_languages), 2),
];
}
/**
* Check if within budget.
*/
public function isWithinBudget(float $cost): bool {
$monthly_budget = \Drupal::config('mymodule.settings')->get('monthly_budget');
$current_spend = \Drupal::state()->get('translation_spend_' . date('Y-m'), 0);
return ($current_spend + $cost) <= $monthly_budget;
}
}
Quality Assurance
Translation Quality Checker
/**
* Translation Quality Checker.
*/
class QualityChecker {
/**
* Check translation quality.
*/
public function checkQuality(
string $source,
string $translation,
string $source_lang,
string $target_lang
): array {
$issues = [];
// 1. Length check (translation shouldn't be 3x longer/shorter)
$ratio = mb_strlen($translation) / mb_strlen($source);
if ($ratio < 0.3 || $ratio > 3) {
$issues[] = 'Length ratio unusual: ' . round($ratio, 2);
}
// 2. HTML tag consistency
if ($this->hasHtmlTags($source)) {
if (!$this->tagsMatch($source, $translation)) {
$issues[] = 'HTML tags mismatch';
}
}
// 3. Placeholder consistency
$source_placeholders = $this->extractPlaceholders($source);
$trans_placeholders = $this->extractPlaceholders($translation);
if ($source_placeholders !== $trans_placeholders) {
$issues[] = 'Placeholder mismatch';
}
// 4. Character encoding issues
if (mb_detect_encoding($translation, 'UTF-8', true) === false) {
$issues[] = 'Encoding issue detected';
}
return [
'has_issues' => !empty($issues),
'issues' => $issues,
'quality_score' => $this->calculateScore($issues),
];
}
/**
* Calculate quality score (0-100).
*/
protected function calculateScore(array $issues): int {
return max(0, 100 - (count($issues) * 20));
}
}
Monitoring & Analytics
Translation Analytics Dashboard
/**
* Translation Analytics Service.
*/
class TranslationAnalytics {
/**
* Get translation statistics.
*/
public function getStatistics(string $period = 'month'): array {
$start = strtotime("-1 $period");
$query = \Drupal::database()->select('translation_log', 't');
$query->fields('t', ['language', 'characters', 'cost']);
$query->condition('timestamp', $start, '>=');
$results = $query->execute()->fetchAll();
$stats = [];
foreach ($results as $row) {
$lang = $row->language;
if (!isset($stats[$lang])) {
$stats[$lang] = [
'translations' => 0,
'characters' => 0,
'cost' => 0,
];
}
$stats[$lang]['translations']++;
$stats[$lang]['characters'] += $row->characters;
$stats[$lang]['cost'] += $row->cost;
}
return $stats;
}
/**
* Get most translated content types.
*/
public function getTopContentTypes(): array {
$query = \Drupal::database()->select('translation_log', 't');
$query->fields('t', ['entity_type', 'bundle']);
$query->addExpression('COUNT(*)', 'count');
$query->groupBy('entity_type');
$query->groupBy('bundle');
$query->orderBy('count', 'DESC');
$query->range(0, 10);
return $query->execute()->fetchAll();
}
}
Fazit
Die Kombination von Drupal 11 und DeepL ermöglicht Enterprise-Multilingual-Websites mit 30+ Sprachen bei überschaubarem Budget und Wartungsaufwand. Durch intelligente Workflows, Glossare und Quality-Assurance-Prozesse erreichen Sie professionelle Übersetzungsqualität bei Kostenersparnis von 95%+ gegenüber manueller Übersetzung.
Key Takeaways:
- Glossare für konsistente Fachterminologie
- Hybrid-Workflow (Auto + Human Review) für kritischen Content
- Content-Priorisierung nach Business-Value
- Performance-Optimierung durch Caching und Batch-Processing
- Kontinuierliches Monitoring und Quality-Assurance
Planen Sie eine mehrsprachige Drupal 11 Website? Mit über 20 Jahren Erfahrung in komplexen Drupal-Projekten unterstütze ich Sie bei Architektur, Implementation und Optimierung. Kontakt: mail@stevenschulz.net oder 04037420859
Häufig gestellte Fragen (FAQ)
Was sind DeepL Glossare und wie verbessern sie Übersetzungen?
Wie viel spare ich mit automatischer Übersetzung gegenüber professionellen Übersetzern?
Was ist ein Hybrid Translation Workflow?
Wie priorisiere ich Content für Übersetzungen bei 30+ Sprachen?
Wie optimiere ich Hreflang Tags für mehrsprachige Drupal-Sites?
Das könnte Sie auch interessieren
DeepL API in Drupal 11 integrieren: Professionelle Übersetzungen automatisieren
Step-by-Step Guide zur Integration der DeepL API in Drupal 11. Automatisieren Sie Content-Übersetzungen mit Custom Modul...
AI Content-Generierung in Drupal: Automatische Texterstellung mit ChatGPT
Praktischer Guide zur automatischen Content-Generierung in Drupal mit dem AI Module und OpenAI: Blog-Posts, Produktbesch...
Klaviyo API Integration mit Drupal 11 & Drupal Commerce
Schritt-für-Schritt Anleitung: So integrieren Sie Klaviyo E-Mail-Marketing in Drupal 11. E-Commerce Tracking, Warenkorba...