# Skill: WPML Integration

## Identity
- **Skill ID**: `wpml-integration`
- **Domain**: WPML Plugin Integration & Compatibility
- **Technologies**: WPML Hooks API, Translation Management, Language Codes
- **Source Agent**: `api-integration-expert.md`, `frontend-php-expert.md`

## When to Load This Skill
- Task involves WPML compatibility
- Registering translation services with WPML
- Processing WPML translation jobs
- Working with WPML language codes
- Files matching: `includes/**/class-*-service-registrar.php`, `includes/**/Wpml*.php`

## Core Patterns

### WPML Service Registration (Secure)
```php
function presszone_multilingual_register_wpml_service() {
    // Verify WPML is active
    if (!defined('WPML_VERSION')) {
        return;
    }
    
    // Register translation service
    add_filter('wpml_tm_translation_service_container', function($services) {
        $services['presszone_multilingual'] = [
            'name' => __('Multilingual Press Zone', 'multilingual-press-zone'),
            'class' => 'PresszoneMultilingual_WPML_Service',
            'description' => __('Enterprise multilingual solution', 'multilingual-press-zone')
        ];
        return $services;
    });
}
add_action('plugins_loaded', 'presszone_multilingual_register_wpml_service');
```

### WPML Translation Job Processing (Secure)
```php
add_action('wpml_tm_translation_job_data', function($job_data) {
    // Validate job data structure
    if (!isset($job_data['job_id'], $job_data['source_language'], $job_data['target_language'])) {
        return;
    }
    
    // Sanitize job data
    $job_id = absint($job_data['job_id']);
    $source_lang = sanitize_key($job_data['source_language']);
    $target_lang = sanitize_key($job_data['target_language']);
    
    // Validate languages against whitelist
    $allowed_languages = presszone_multilingual_get_supported_languages();
    if (!array_key_exists($source_lang, $allowed_languages) || 
        !array_key_exists($target_lang, $allowed_languages)) {
        return;
    }
    
    // Process job securely
    presszone_multilingual_process_wpml_job($job_id, $source_lang, $target_lang, $job_data);
});
```

### WPML String Registration (Secure)
```php
function presszone_multilingual_register_strings() {
    if (!function_exists('icl_register_string')) {
        return;
    }
    
    $strings = [
        'welcome_message' => get_option('presszone_multilingual_welcome', ''),
        'footer_text' => get_option('presszone_multilingual_footer', ''),
    ];
    
    foreach ($strings as $name => $value) {
        // Sanitize before registration
        $name = sanitize_key($name);
        $value = sanitize_text_field($value);
        
        icl_register_string(
            'multilingual-press-zone',  // Context
            $name,                       // Name
            $value                       // Value
        );
    }
}
add_action('init', 'presszone_multilingual_register_strings');
```

### Language Code Validation
```php
function presszone_multilingual_validate_language_code($code) {
    // Sanitize first
    $code = sanitize_key($code);
    
    // Whitelist of supported languages
    $allowed = presszone_multilingual_get_supported_languages();
    
    if (!array_key_exists($code, $allowed)) {
        return new WP_Error('invalid_language', __('Unsupported language code', 'multilingual-press-zone'));
    }
    
    return $code;
}

function presszone_multilingual_get_supported_languages() {
    return [
        'en' => 'English',
        'es' => 'Español',
        'fr' => 'Français',
        'de' => 'Deutsch',
        'it' => 'Italiano',
        'pt' => 'Português',
        'ru' => 'Русский',
        'zh' => '中文',
        'ja' => '日本語',
        'ko' => '한국어',
        'ar' => 'العربية',
        'nl' => 'Nederlands',
        'pl' => 'Polski',
        'tr' => 'Türkçe',
        'vi' => 'Tiếng Việt'
    ];
}
```

### WPML Language Switcher Integration
```php
function presszone_multilingual_wpml_language_switcher() {
    if (!function_exists('icl_get_languages')) {
        return;
    }
    
    $languages = icl_get_languages('skip_missing=0&orderby=code');
    
    if (empty($languages)) {
        return;
    }
    
    echo '<div class="presszone-multilingual-switcher">';
    foreach ($languages as $lang) {
        // Escape all output
        $url = esc_url($lang['url']);
        $code = esc_attr($lang['language_code']);
        $name = esc_html($lang['native_name']);
        $active = $lang['active'] ? ' presszone-multilingual-switcher__item--active' : '';
        
        echo sprintf(
            '<a href="%s" class="presszone-multilingual-switcher__item%s" hreflang="%s">%s</a>',
            $url,
            $active,
            $code,
            $name
        );
    }
    echo '</div>';
}
```

### WPML Job Completion Callback
```php
function presszone_multilingual_wpml_job_complete($job_id, $translated_content) {
    // Validate job ID
    $job_id = absint($job_id);
    if (!$job_id) {
        return new WP_Error('invalid_job_id', __('Invalid job ID', 'multilingual-press-zone'));
    }
    
    // Sanitize translated content
    $translated_content = wp_kses_post($translated_content);
    
    if (!function_exists('wpml_tm_save_translation')) {
        return new WP_Error('wpml_not_loaded', __('WPML Translation Management not available', 'multilingual-press-zone'));
    }
    
    // Update WPML job
    wpml_tm_save_translation($job_id, [
        'translation' => $translated_content,
        'complete' => 1
    ]);
    
    do_action('presszone_multilingual_wpml_job_completed', $job_id, $translated_content);
}
```

## Anti-Patterns (Forbidden)

| Mistake | Fix |
|---------|-----|
| Not checking WPML existence | Always check `defined('WPML_VERSION')` |
| Missing language validation | Whitelist validate all language codes |
| Direct function calls | Check `function_exists()` before calling WPML functions |
| Hardcoded language codes | Use WPML's language detection |
| Missing translation context | Always provide context to `icl_register_string()` |
| No error handling | Handle cases when WPML functions fail |

## WordPress.org Compliance

### WPML Compatibility Declaration
```php
// In main plugin file
if (!defined('WPML_VERSION')) {
    // Plugin works standalone without WPML
}

// Optional WPML integration
if (defined('WPML_VERSION') && version_compare(WPML_VERSION, '4.5.0', '>=')) {
    require_once PRESSZONE_MULTILINGUAL_PATH . 'includes/Wpml/ServiceRegistrar.php';
}
```

### Language Code Format
- **WPML Format**: 2-letter ISO 639-1 codes (en, es, fr)
- **WordPress Format**: Locale codes (en_US, es_ES, fr_FR)
- **Always sanitize**: Use `sanitize_key()` for language codes

## Integration with Other Skills
- **Often combined with**: `wordpress-php-integration`, `api-integration`
- **For database storage**: Load `database-operations`
- **For settings**: Load `settings-management`

## Quick Reference

### WPML Filters & Actions

| Hook | Type | Purpose |
|------|------|---------|
| `wpml_tm_translation_service_container` | Filter | Register translation service |
| `wpml_tm_translation_job_data` | Action | Process translation jobs |
| `wpml_register_single_string` | Action | Register strings for translation |
| `icl_current_language` | Filter | Get/modify current language |
| `wpml_active_languages` | Filter | Get active languages |

### WPML Functions (Check Existence)
```php
if (function_exists('icl_register_string')) {
    icl_register_string($context, $name, $value);
}

if (function_exists('icl_get_languages')) {
    $languages = icl_get_languages();
}

if (function_exists('icl_t')) {
    $translated = icl_t($context, $name, $default);
}
```

### WPML Language Detection
```php
function presszone_multilingual_get_current_language() {
    if (defined('ICL_LANGUAGE_CODE')) {
        return sanitize_key(ICL_LANGUAGE_CODE);
    }
    
    if (function_exists('wpml_get_current_language')) {
        return sanitize_key(wpml_get_current_language());
    }
    
    // Fallback to site language
    return substr(get_locale(), 0, 2);
}
```

## Validation Checklist
- [ ] Checked `defined('WPML_VERSION')` before integration
- [ ] All WPML function calls wrapped in `function_exists()`
- [ ] Language codes validated against whitelist
- [ ] All output escaped (`esc_html`, `esc_attr`, `esc_url`)
- [ ] Job data sanitized before processing
- [ ] Error handling for WPML failures
- [ ] Plugin works standalone without WPML
- [ ] WPML hooks registered on `plugins_loaded` or later
