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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

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
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions 'apply translations' but doesn't clarify what this entails—whether it modifies the .xcstrings file directly, requires write permissions, has side effects, or handles errors. For a tool with potential file mutations, this lack of detail is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with a clear purpose statement followed by parameter and return value sections. Each sentence adds value, such as clarifying parameter formats and the optional nature of 'app_description'. It could be slightly more front-loaded by emphasizing the 'apply' action earlier, but overall it's concise and well-organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (4 parameters, potential file mutations) and lack of annotations, the description is moderately complete. It covers parameter semantics adequately and includes an output schema (returns a string), so it doesn't need to explain return values. However, it falls short on behavioral transparency and usage guidelines, which are crucial for a tool that may modify files.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds meaningful context beyond the input schema, which has 0% description coverage. It explains that 'target_languages' is a comma-separated list of codes (e.g., 'es,fr,de') and that 'app_description' provides optional context for translations. This compensates well for the schema's lack of descriptions, though it doesn't detail the format or constraints for 'file_path' and 'key'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'translate a specific key to multiple target languages and apply translations.' It specifies the verb (translate and apply), resource (key in .xcstrings file), and scope (multiple languages). However, it doesn't explicitly differentiate from sibling tools like 'translate_tool' or 'apply_tool', which likely have overlapping functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With sibling tools like 'translate_tool', 'apply_tool', and 'apply_missing_tool' available, there's no indication of the specific scenarios or prerequisites for choosing this tool over others, leaving usage context ambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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