CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-rosetta

A Django application that facilitates the translation process of your Django projects

Pending
Overview
Eval results
Files

file-operations.mddocs/

File Operations and Utilities

Utilities for discovering, processing, and managing .po/.mo translation files across Django projects. These functions handle the core file system operations needed for translation management, including file discovery, timestamp handling, and pagination utilities.

Capabilities

Translation File Discovery

Core function for locating .po translation files across different types of Django applications.

def find_pos(lang: str, project_apps: bool = True, django_apps: bool = False, third_party_apps: bool = False) -> list:
    """
    Find .po files for specified language across application types.
    
    Searches for translation files in different categories of Django applications
    based on the provided parameters. Returns list of file paths with metadata.
    
    Parameters:
    - lang: Language code to search for (e.g., 'en', 'fr', 'de')
    - project_apps: Include project-specific applications (default: True)
    - django_apps: Include Django framework applications (default: False)  
    - third_party_apps: Include third-party applications (default: False)
    
    Returns:
    List of dictionaries containing:
    - 'po_path': Full path to .po file
    - 'mo_path': Full path to corresponding .mo file
    - 'app_name': Application name
    - 'domain': Translation domain (e.g., 'django', 'djangojs')
    - 'is_writable': Boolean indicating write permissions
    - 'stats': Statistics dict with 'translated', 'untranslated', 'fuzzy' counts
    
    Respects:
    - ROSETTA_EXCLUDED_APPLICATIONS setting
    - ROSETTA_EXCLUDED_PATHS setting  
    - ROSETTA_POFILENAMES setting for allowed filenames
    """

Timestamp Utilities

Function for generating properly formatted timestamps with timezone information.

def timestamp_with_timezone(dt=None) -> str:
    """
    Generate timestamp with timezone information.
    
    Creates timestamp string suitable for .po file headers and logging,
    following GNU gettext conventions for date formatting.
    
    Parameters:
    - dt: datetime object to format (optional, defaults to current time)
    
    Returns:
    Formatted timestamp string with timezone (e.g., "2025-01-15 10:30 +0000")
    
    Format follows .po file header requirements:
    - YYYY-MM-DD HH:MM+ZZZZ format
    - Uses system timezone if available
    - Falls back to UTC if timezone cannot be determined
    """

Pagination Utilities

Function for generating pagination ranges for the web interface.

def pagination_range(first: int, last: int, current: int) -> list:
    """
    Generate pagination ranges for UI display.
    
    Creates smart pagination ranges that show relevant page numbers
    around the current page, with ellipsis for gaps.
    
    Parameters:
    - first: First page number (typically 1)
    - last: Last page number  
    - current: Current page number
    
    Returns:
    List of page numbers and/or ellipsis strings for display
    
    Examples:
    - pagination_range(1, 10, 1) -> [1, 2, 3, '...', 10]
    - pagination_range(1, 10, 5) -> [1, '...', 4, 5, 6, '...', 10]
    - pagination_range(1, 5, 3) -> [1, 2, 3, 4, 5]
    """

Cache Instance

Cache instance used for file operation caching and optimization.

cache
"""
Django cache instance for Rosetta file operations.

Used for:
- Caching file discovery results
- Storing .po file statistics
- Optimizing repeated file system access
- Temporary storage of file metadata

Cache key patterns:
- 'rosetta_file_list_{lang}_{hash}': File discovery results
- 'rosetta_po_stats_{path_hash}': .po file statistics
- 'rosetta_app_list': Application discovery results
"""

Usage Examples

Basic File Discovery

from rosetta.poutil import find_pos

def discover_translation_files():
    """Discover all available translation files."""
    
    # Find French translation files in project applications only
    french_files = find_pos('fr', project_apps=True, django_apps=False, third_party_apps=False)
    
    for file_info in french_files:
        print(f"App: {file_info['app_name']}")
        print(f"Domain: {file_info['domain']}")
        print(f"PO file: {file_info['po_path']}")
        print(f"Writable: {file_info['is_writable']}")
        print(f"Stats: {file_info['stats']}")
        print("---")
    
    # Find all translation files including Django and third-party apps
    all_files = find_pos('es', project_apps=True, django_apps=True, third_party_apps=True)
    
    return all_files

File Statistics Analysis

from rosetta.poutil import find_pos

def analyze_translation_progress(language):
    """Analyze translation progress for a language."""
    
    files = find_pos(language)
    
    total_stats = {
        'translated': 0,
        'untranslated': 0, 
        'fuzzy': 0
    }
    
    app_stats = {}
    
    for file_info in files:
        app_name = file_info['app_name']
        stats = file_info['stats']
        
        # Aggregate totals
        for key in total_stats:
            total_stats[key] += stats.get(key, 0)
        
        # Per-app statistics
        if app_name not in app_stats:
            app_stats[app_name] = {'translated': 0, 'untranslated': 0, 'fuzzy': 0}
        
        for key in app_stats[app_name]:
            app_stats[app_name][key] += stats.get(key, 0)
    
    # Calculate percentages
    total_entries = sum(total_stats.values())
    if total_entries > 0:
        completion_percentage = (total_stats['translated'] / total_entries) * 100
    else:
        completion_percentage = 0
    
    return {
        'language': language,
        'total_stats': total_stats,
        'app_stats': app_stats,
        'completion_percentage': completion_percentage
    }

Custom File Filtering

from rosetta.poutil import find_pos

def find_writable_files(language):
    """Find only writable translation files for editing."""
    
    all_files = find_pos(language, project_apps=True, django_apps=True)
    
    # Filter for writable files only
    writable_files = [
        file_info for file_info in all_files 
        if file_info['is_writable']
    ]
    
    return writable_files

def find_incomplete_files(language, threshold=50):
    """Find files with translation completion below threshold."""
    
    all_files = find_pos(language)
    incomplete_files = []
    
    for file_info in all_files:
        stats = file_info['stats']
        total = stats.get('translated', 0) + stats.get('untranslated', 0) + stats.get('fuzzy', 0)
        
        if total > 0:
            completion = (stats.get('translated', 0) / total) * 100
            if completion < threshold:
                file_info['completion_percentage'] = completion
                incomplete_files.append(file_info)
    
    return incomplete_files

Timestamp Generation

from rosetta.poutil import timestamp_with_timezone
from datetime import datetime, timezone

def update_po_file_header():
    """Generate timestamps for .po file headers."""
    
    # Current timestamp
    current_timestamp = timestamp_with_timezone()
    print(f"Current: {current_timestamp}")
    
    # Specific datetime
    specific_time = datetime(2025, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
    specific_timestamp = timestamp_with_timezone(specific_time)
    print(f"Specific: {specific_timestamp}")
    
    # Use in .po file header
    po_header = f'''# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\\n"
"Report-Msgid-Bugs-To: \\n"
"POT-Creation-Date: {current_timestamp}\\n"
"PO-Revision-Date: {current_timestamp}\\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
"Language-Team: LANGUAGE <LL@li.org>\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
'''
    
    return po_header

Pagination Implementation

from rosetta.poutil import pagination_range

def generate_pagination_context(current_page, total_pages):
    """Generate pagination context for templates."""
    
    if total_pages <= 1:
        return {'show_pagination': False}
    
    # Generate page range
    page_range = pagination_range(1, total_pages, current_page)
    
    # Build pagination context
    context = {
        'show_pagination': True,
        'current_page': current_page,
        'total_pages': total_pages,
        'page_range': page_range,
        'has_previous': current_page > 1,
        'has_next': current_page < total_pages,
        'previous_page': current_page - 1 if current_page > 1 else None,
        'next_page': current_page + 1 if current_page < total_pages else None,
    }
    
    return context

# Usage in views
def translation_list_view(request):
    from django.core.paginator import Paginator
    from rosetta.conf import settings as rosetta_settings
    
    # Get translation entries
    entries = get_translation_entries()  # Your function to get entries
    
    # Paginate
    paginator = Paginator(entries, rosetta_settings.MESSAGES_PER_PAGE)
    page_number = request.GET.get('page', 1)
    page_obj = paginator.get_page(page_number)
    
    # Generate pagination context
    pagination_context = generate_pagination_context(
        page_obj.number, 
        paginator.num_pages
    )
    
    return render(request, 'template.html', {
        'page_obj': page_obj,
        'pagination': pagination_context
    })

File System Monitoring

import os
from rosetta.poutil import find_pos, cache

def monitor_translation_files(language):
    """Monitor translation files for changes."""
    
    files = find_pos(language)
    file_info = {}
    
    for file_data in files:
        po_path = file_data['po_path']
        
        if os.path.exists(po_path):
            stat = os.stat(po_path)
            file_info[po_path] = {
                'mtime': stat.st_mtime,
                'size': stat.st_size,
                'is_writable': os.access(po_path, os.W_OK)
            }
    
    # Cache file information for comparison
    cache_key = f'rosetta_file_monitor_{language}'
    previous_info = cache.get(cache_key, {})
    cache.set(cache_key, file_info, 3600)  # Cache for 1 hour
    
    # Detect changes
    changed_files = []
    for path, info in file_info.items():
        if path in previous_info:
            prev_info = previous_info[path]
            if (info['mtime'] != prev_info['mtime'] or 
                info['size'] != prev_info['size']):
                changed_files.append(path)
        else:
            # New file
            changed_files.append(path)
    
    return changed_files

def clear_file_caches():
    """Clear all file-related caches."""
    
    # Clear file discovery caches
    cache_keys_to_clear = []
    
    # You would need to implement cache key pattern matching
    # This is a simplified example
    for key in cache._cache.keys():
        if key.startswith('rosetta_file_'):
            cache_keys_to_clear.append(key)
    
    for key in cache_keys_to_clear:
        cache.delete(key)

Install with Tessl CLI

npx tessl i tessl/pypi-django-rosetta

docs

access-control.md

configuration.md

django-signals.md

file-operations.md

index.md

storage-backends.md

template-integration.md

translation-services.md

web-interface.md

tile.json