Skip to main content
Glama
iamnotagentleman

Localizable XStrings MCP Server

translate_key_tool

Translate a specific localization key to multiple target languages in Xcode String Catalog files for iOS/macOS projects.

Instructions

MCP tool to translate a specific key to multiple target languages and apply translations.

Args:
    file_path (str): Path to the .xcstrings file
    key (str): The specific key to translate
    target_languages (str): Comma-separated list of target language codes (e.g., "es,fr,de")
    app_description (str): Optional description of the app for better translation context

Returns:
    str: Translation results or error message

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYes
keyYes
target_languagesYes
app_descriptionNo

Implementation Reference

  • The primary handler for 'translate_key_tool'. Validates inputs, parses languages, invokes translate_single_key helper, handles errors, and returns formatted results. Registered via @mcp.tool() decorator.
    @mcp.tool()
    def translate_key_tool(file_path: str, key: str, target_languages: str, app_description: str = "") -> str:
        """
        MCP tool to translate a specific key to multiple target languages and apply translations.
    
        Args:
            file_path (str): Path to the .xcstrings file
            key (str): The specific key to translate
            target_languages (str): Comma-separated list of target language codes (e.g., "es,fr,de")
            app_description (str): Optional description of the app for better translation context
    
        Returns:
            str: Translation results or error message
        """
        try:
            if not validate_xcstrings_file(file_path):
                return f"Error: Invalid file path or not an .xcstrings file: {file_path}"
    
            # Parse target languages
            languages = [lang.strip() for lang in target_languages.split(',') if lang.strip()]
            if not languages:
                return "Error: No target languages provided"
    
            # Validate all language codes
            for lang in languages:
                if not validate_language_code(lang):
                    return f"Error: Invalid language code: {lang}"
    
            # Translate the key
            app_desc = app_description if app_description else None
            translations, backup_path, errors = translate_single_key(
                file_path, key, languages, app_description=app_desc
            )
    
            if not translations and errors:
                return f"Error: All translations failed:\n" + "\n".join(
                    f"{lang}: {error}" for lang, error in errors.items()
                )
    
            result = [
                f"Translated key '{key}' to {len(translations)} language(s)",
                f"Backup created: {backup_path}",
            ]
    
            if translations:
                result.append("\nSuccessful translations:")
                for lang, trans_dict in translations.items():
                    for k, value in trans_dict.items():
                        result.append(f"  {lang}: {value}")
    
            if errors:
                result.append("\nFailed translations:")
                for lang, error in errors.items():
                    result.append(f"  {lang}: {error}")
    
            return "\n".join(result)
    
        except KeyError as e:
            return f"Error: {str(e)}"
        except Exception as e:
            return format_error_message(e, "Failed to translate key")
  • Key helper function implementing the core logic: backup creation, file parsing/modification, translation application per language using translate_strings, and error handling for the single key across multiple languages.
    def translate_single_key(
        file_path: str,
        key: str,
        target_languages: List[str],
        source_language: str = "en",
        app_description: Optional[str] = None
    ) -> Tuple[Dict[str, Dict[str, str]], str, Dict[str, str]]:
        """
        Translate a single key to multiple target languages and apply translations to a Localizable.xcstrings file.
        
        Args:
            file_path (str): Path to the .xcstrings file
            key (str): The specific key to translate
            target_languages (List[str]): List of target language codes
            source_language (str): Source language code (default: 'en')
            app_description (Optional[str]): Optional description of the app for better translation context
            
        Returns:
            Tuple[Dict[str, Dict[str, str]], str, Dict[str, str]]: Tuple of (translations by language, backup file path, errors by language)
            
        Raises:
            FileNotFoundError: If the file doesn't exist
            KeyError: If the key doesn't exist in the file
            json.JSONDecodeError: If the file is not valid JSON
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"File not found: {file_path}")
        
        # Get base language strings (now returns list of keys)
        base_keys = get_base_language_strings(file_path)
        if key not in base_keys:
            raise KeyError(f"Key '{key}' not found in {file_path}")
        
        # For translation, we'll use the key as both key and value
        single_key_dict = {key: key}
        
        # Create backup before modifying the file
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_path = f"{file_path}.bak.{timestamp}"
        try:
            shutil.copy2(file_path, backup_path)
            print(f"Created backup: {backup_path}")
        except Exception as e:
            raise Exception(f"Failed to create backup: {str(e)}")
        
        # Load the xcstrings file
        with open(file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        if 'strings' not in data:
            data['strings'] = {}
        
        # Translate to each target language
        translations_by_language = {}
        errors_by_language = {}
        
        for target_lang in target_languages:
            try:
                # Translate the single key
                translated, skipped = translate_strings(single_key_dict, target_lang, source_language, app_description)
                
                if key in translated:
                    # Apply the translation
                    if key not in data['strings']:
                        data['strings'][key] = {"localizations": {}}
                    
                    if 'localizations' not in data['strings'][key]:
                        data['strings'][key]['localizations'] = {}
                    
                    data['strings'][key]['localizations'][target_lang] = {
                        "stringUnit": {
                            "state": "translated",
                            "value": translated[key]
                        }
                    }
                    
                    translations_by_language[target_lang] = {key: translated[key]}
                else:
                    error_msg = skipped.get(key, "Translation failed") if skipped else "Translation failed"
                    errors_by_language[target_lang] = error_msg
                    
            except Exception as e:
                errors_by_language[target_lang] = str(e)
        
        # Write back to file if we have any successful translations
        if translations_by_language:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(data, f, indent=2, ensure_ascii=False)
            except Exception as e:
                raise Exception(f"Failed to write file: {str(e)}")
        
        return translations_by_language, backup_path, errors_by_language

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iamnotagentleman/localizable-xcstrings-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server