apply_missing_tool
Apply translations to missing keys in Xcode String Catalogs for a target language, filling gaps without overwriting existing translations.
Instructions
MCP tool to apply only missing translations for a target language in xcstrings file.
Only translates keys that don't already have translations in the target language.
Args:
file_path (str): Path to the .xcstrings file
target_language (str): Target language code
app_description (str): Optional description of the app for better translation context
Returns:
str: Application result with newly translated keys or error message
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| app_description | No | ||
| file_path | Yes | ||
| target_language | Yes |
Implementation Reference
- The main handler function for the 'apply_missing_tool' MCP tool. It is decorated with @mcp.tool() for registration and executes the tool logic by validating inputs and calling the core apply_missing_translations helper.@mcp.tool() def apply_missing_tool(file_path: str, target_language: str, app_description: str = "") -> str: """ MCP tool to apply only missing translations for a target language in xcstrings file. Only translates keys that don't already have translations in the target language. Args: file_path (str): Path to the .xcstrings file target_language (str): Target language code app_description (str): Optional description of the app for better translation context Returns: str: Application result with newly translated keys or error message """ try: if not validate_xcstrings_file(file_path): return f"Error: Invalid file path or not an .xcstrings file: {file_path}" if not validate_language_code(target_language): return f"Error: Invalid language code: {target_language}" # Apply only missing translations app_desc = app_description if app_description else None applied_translations, backup_path, summary, skipped_keys = apply_missing_translations(file_path, target_language, app_description=app_desc) result = [ f"Summary: {summary}", ] if backup_path: result.append(f"Backup created: {backup_path}") if applied_translations: result.append(f"\nNew translations added ({len(applied_translations)}):") for key, value in applied_translations.items(): result.append(f"{key}: {value}") if skipped_keys: result.append(f"\nSkipped strings ({len(skipped_keys)}):") for key, reason in skipped_keys.items(): result.append(f"{key}: {reason}") return "\n".join(result) except Exception as e: return format_error_message(e, "Failed to apply missing translations")
- Core helper function that implements the logic to identify missing translations in the target language, translate only those using translate_strings, create a backup, modify the xcstrings JSON file by adding the new translations, and return results including summary and skipped keys.def apply_missing_translations( file_path: str, target_language: str, source_language: str = "en", app_description: Optional[str] = None ) -> Tuple[Dict[str, str], str, str, Dict[str, str]]: """ Translate and apply only missing translations for a target language in a Localizable.xcstrings file. Only keys that don't have translations in the target language will be processed. Args: file_path (str): Path to the .xcstrings file target_language (str): Target language code 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, str], str, str, Dict[str, str]]: Tuple of (translated key-value pairs, backup file path, summary message, skipped keys with reasons) Raises: FileNotFoundError: If the file doesn't exist json.JSONDecodeError: If the file is not valid JSON Exception: If translation or file writing fails """ if not os.path.exists(file_path): raise FileNotFoundError(f"File not found: {file_path}") # Load the xcstrings file to check existing translations with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) if 'strings' not in data: data['strings'] = {} # Get base language strings (now returns list of keys) base_keys = get_base_language_strings(file_path) if not base_keys: return {}, "", f"No base language strings found in {file_path}", {} # Filter out keys that already have translations in the target language missing_keys = [] existing_translations = {} for key in base_keys: # Check if this key already has a translation in the target language if key in data['strings']: localizations = data['strings'][key].get('localizations', {}) if target_language in localizations: # Key already has translation in target language existing_translations[key] = localizations[target_language].get('stringUnit', {}).get('value', '') else: # Key missing translation in target language missing_keys.append(key) else: # Key doesn't exist at all, needs translation missing_keys.append(key) total_strings = len(base_keys) missing_count = len(missing_keys) existing_count = len(existing_translations) if not missing_keys: return {}, "", f"All {total_strings} strings already have {target_language} translations in {file_path}", {} print(f"Found {existing_count} existing {target_language} translations, {missing_count} missing translations") # Translate only the missing keys translations, skipped_keys = translate_strings(missing_keys, target_language, source_language, app_description) if not translations and not skipped_keys: return {}, "", f"Translation failed for {target_language}", {} # 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)}") # Apply translations and track successful translations applied_translations = {} for key, value in translations.items(): if key not in data['strings']: # Create new string entry if it doesn't exist data['strings'][key] = { "localizations": {} } if 'localizations' not in data['strings'][key]: data['strings'][key]['localizations'] = {} # Add the translation for the target language (only if it was missing) data['strings'][key]['localizations'][target_language] = { "stringUnit": { "state": "translated", "value": value } } applied_translations[key] = value # Write back to file try: with open(file_path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) # Create summary message new_translations_count = len(applied_translations) total_translations_now = existing_count + new_translations_count if new_translations_count == 0: summary = f"No new {target_language} translations added to {file_path} (all {missing_count} missing translations failed)" elif missing_count == new_translations_count: summary = f"{target_language} missing translations added to {file_path} ({new_translations_count} new, {total_translations_now}/{total_strings} total)" else: summary = f"{target_language} missing translations added to {file_path} ({new_translations_count}/{missing_count} new translations completed, {total_translations_now}/{total_strings} total)" return applied_translations, backup_path, summary, skipped_keys except Exception as e: raise Exception(f"Failed to write file: {str(e)}")
- src/localizable_xstrings_mcp/server.py:177-177 (registration)The @mcp.tool() decorator registers the apply_missing_tool function as an MCP tool.@mcp.tool()