CoolFace
Apppublic

eoboldina/localization-manager

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
full_app.py775 linesDownload Raw Back to root
1#!/usr/bin/env python32"""3Полнофункциональный менеджер локализации для HuggingFace Spaces4Включает все возможности оригинального приложения, но без проблемных зависимостей5"""6 7import gradio as gr8import json9import tempfile10import shutil11import os12import zipfile13import io14from datetime import datetime15from typing import Dict, Set, Tuple, List16from file_manager import FileManager17from neural_translator import get_neural_translator, initialize_neural_translator18 19def safe_get_path(file_input):20    """Безопасное получение пути к файлу"""21    if file_input is None:22        return None23    if isinstance(file_input, str):24        return file_input25    if hasattr(file_input, 'name'):26        return file_input.name27    return str(file_input)28 29def auto_translate_template(template_file, target_language):30    """31    Автоматический перевод шаблона с помощью нейросетей32    """33    if not template_file:34        return "❌ Загрузите шаблон для перевода.", None35    36    # Получаем нейросетевой переводчик37    translator = get_neural_translator()38    if not translator or not translator.is_available():39        return "❌ Нейросетевой переводчик недоступен. Попробуйте позже.", None40    41    try:42        template_path = safe_get_path(template_file)43        if not template_path:44            return "❌ Не удалось получить путь к файлу", None45        46        # Создаем временную директорию47        temp_dir = tempfile.mkdtemp()48        49        try:50            # Определяем тип файла и создаем file manager51            file_manager = FileManager()52            file_type = file_manager._detect_file_type(template_path)53            54            # Загружаем данные из шаблона55            template_data = file_manager.load_file(template_path)56            if not template_data:57                return "❌ Не удалось загрузить данные из шаблона", None58            59            # Определяем целевой язык код60            lang_mapping = {61                "английский (en)": "en",62                "узбекский (uz)": "uz",63                "французский (fr)": "fr",64                "немецкий (de)": "de",65                "испанский (es)": "es",66                "итальянский (it)": "it"67            }68            69            target_lang_code = lang_mapping.get(target_language, "en")70            71            # Переводим все ключи72            translated_data = {}73            total_keys = len(template_data)74            75            for i, (key, russian_text) in enumerate(template_data.items()):76                if russian_text and russian_text.strip():77                    # Определяем домен и контекст78                    domain = translator.get_domain_from_key(key)79                    context_type = translator.get_context_type_from_key(key)80                    81                    # Переводим текст82                    if target_lang_code in ["en", "uz"]:83                        # Используем нейросетевые модели84                        translated_text = translator.translate_text(85                            russian_text, target_lang_code, domain, context_type86                        )87                    else:88                        # Для других языков оставляем исходный текст89                        translated_text = f"[TO_TRANSLATE_{target_lang_code.upper()}] {russian_text}"90                    91                    translated_data[key] = translated_text92                else:93                    translated_data[key] = russian_text94            95            # Сохраняем переведенный файл96            output_filename = f"auto_translated_{target_lang_code}_{os.path.basename(template_path)}"97            output_path = os.path.join(temp_dir, output_filename)98            99            file_manager.save_file(output_path, translated_data, file_type)100            101            # Формируем отчет102            neural_translations = sum(1 for v in translated_data.values() 103                                    if v and not v.startswith("[TO_TRANSLATE_"))104            105            report = f"""✅ Автоматический перевод завершен!106 107📊 **Статистика:**108- Всего ключей: {total_keys}109- Переведено нейросетью: {neural_translations}110- Целевой язык: {target_language}111 112🤖 **Использованные модели:**113- RU→EN: mBART (для английского)114- RU→UZ: NLLB (для узбекского)115 116📝 **Рекомендации:**117- Проверьте качество переводов118- Отредактируйте при необходимости119- Для других языков ({target_lang_code}) требуется ручной перевод120 121🎯 **Готово к использованию!**"""122            123            return report, output_path124            125        finally:126            # Очищаем временные файлы (кроме результата)127            pass128            129    except Exception as e:130        return f"❌ Ошибка при автоматическом переводе: {str(e)}", None131 132 133 134def process_localizations(old_file, new_file, target_files):135    """Полная обработка локализаций"""136    137    # Проверяем входные данные138    if not old_file:139        return "❌ Загрузите старую русскую локаль.", None140    if not new_file:141        return "❌ Загрузите новую русскую локаль.", None142    if not target_files:143        return "❌ Загрузите целевые файлы для обновления.", None144    145    try:146        # Получаем пути к файлам147        old_path = safe_get_path(old_file)148        new_path = safe_get_path(new_file)149        150        if not old_path or not new_path:151            return "❌ Ошибка получения путей к основным файлам", None152        153        # Обрабатываем целевые файлы154        target_paths = []155        if isinstance(target_files, list):156            for tf in target_files:157                tp = safe_get_path(tf)158                if tp and os.path.exists(tp):159                    target_paths.append(tp)160        else:161            tp = safe_get_path(target_files)162            if tp and os.path.exists(tp):163                target_paths.append(tp)164        165        if not target_paths:166            return "❌ Целевые файлы не найдены или недоступны", None167        168        # Проверяем существование основных файлов169        if not os.path.exists(old_path):170            return f"❌ Старая локаль не найдена: {os.path.basename(old_path)}", None171        if not os.path.exists(new_path):172            return f"❌ Новая локаль не найдена: {os.path.basename(new_path)}", None173        174        # Создаем файловый менеджер и определяем тип файлов175        file_manager = FileManager()176        177        try:178            old_type = file_manager.detect_file_type(old_path)179            new_type = file_manager.detect_file_type(new_path)180            181            if old_type != new_type:182                return f"❌ Типы файлов не совпадают: {old_type} vs {new_type}", None183            184            # Проверяем типы целевых файлов185            for target_path in target_paths:186                target_type = file_manager.detect_file_type(target_path)187                if target_type != old_type:188                    return f"❌ Тип файла {os.path.basename(target_path)} ({target_type}) не совпадает с основным ({old_type})", None189            190            # Загружаем файлы191            old_data, old_order = file_manager.load_file(old_path)192            new_data, new_order = file_manager.load_file(new_path)193            194        except Exception as e:195            return f"❌ {str(e)}", None196        197        # Сравниваем локализации198        changes, added_keys, updated_keys = file_manager.compare_files(old_path, new_path)199        200        if not changes:201            result_msg = "Между старой и новой локалью изменений не обнаружено.\n\n"202            result_msg += f"общая информация\n"203            result_msg += f"тип файлов: {old_type.upper()}\n"204            result_msg += f"обработано файлов: {len(target_paths)}\n"205            result_msg += f"изменений: 0\n\n"206            result_msg += f"Все локали уже актуальны."207            return result_msg, None208        209        # Создаем временную директорию210        temp_dir = tempfile.mkdtemp()211        212        try:213            processed_files = []214            total_changes = 0215            errors = []216            217            # Обрабатываем каждый целевой файл218            for i, target_path in enumerate(target_paths):219                try:220                    # Применяем изменения с использованием файлового менеджера221                    updated_data, file_changes = file_manager.apply_changes_to_target(222                        target_path, changes, added_keys, updated_keys, new_data, new_order223                    )224                    225                    if file_changes > 0:226                        # Сохраняем обновленный файл227                        updated_filename = f"updated_{os.path.basename(target_path)}"228                        updated_path = os.path.join(temp_dir, updated_filename)229                        file_manager.save_file(updated_path, updated_data, new_order)230                        processed_files.append(updated_path)231                        total_changes += file_changes232                    233                except Exception as e:234                    errors.append(f"Ошибка обработки {os.path.basename(target_path)}: {str(e)}")235                    continue236            237            if not processed_files:238                error_msg = "❌ Не удалось обработать ни одного файла"239                if errors:240                    error_msg += f"\nОшибки:\n" + "\n".join(errors)241                return error_msg, None242            243            # Создаем шаблон для переводчиков244            template_created = False245            if added_keys:246                # Формируем шаблон в порядке ключей новой локали247                template = {}248                for key in new_order:249                    if key in added_keys:250                        template[key] = new_data[key]251                252                # Определяем расширение файла для шаблона253                template_extension = ".properties" if old_type == "properties" else ".json"254                template_filename = f"translation_template{template_extension}"255                template_path = os.path.join(temp_dir, template_filename)256                257                try:258                    file_manager.save_file(template_path, template, new_order)259                    processed_files.append(template_path)260                    template_created = True261                except:262                    pass263            264            # Создаем ZIP архив265            zip_path = os.path.join(temp_dir, "localization_results.zip")266            with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:267                for file_path in processed_files:268                    zip_file.write(file_path, os.path.basename(file_path))269            270            # Формируем детальное сообщение результата со статистикой271            result_msg = "Обработка завершена.\n\n"272            273            # Общая информация274            result_msg += f"общая информация\n"275            result_msg += f"тип файлов: {old_type.upper()}\n"276            result_msg += f"обработано файлов: {len(target_paths)}\n"277            result_msg += f"обновлено: {len(processed_files) - (1 if template_created else 0)}\n"278            if errors:279                result_msg += f"ошибок при обработке: {len(errors)}\n"280            result_msg += f"шаблон создан: {'да' if template_created else 'нет'}\n\n"281            282            # Детальная статистика изменений283            result_msg += f"статистика изменений\n"284            result_msg += f"всего изменений: {len(added_keys) + len(updated_keys)}\n"285            result_msg += f"новых ключей: {len(added_keys)}\n"286            result_msg += f"изменённых ключей: {len(updated_keys)}\n"287            result_msg += f"удалённых ключей: 0\n\n"288            289            # Детализация новых ключей290            if added_keys:291                result_msg += f"новые ключи ({len(added_keys)}):\n"292                for key in sorted(list(added_keys))[:10]:  # Показываем первые 10293                    result_msg += f"{key}\n"294                if len(added_keys) > 10:295                    result_msg += f"... и ещё {len(added_keys) - 10}\n"296                result_msg += "\n"297            298            # Детализация измененных ключей299            if updated_keys:300                result_msg += f"изменённые ключи ({len(updated_keys)}):\n"301                for key in sorted(list(updated_keys))[:10]:  # Показываем первые 10302                    result_msg += f"{key}\n"303                if len(updated_keys) > 10:304                    result_msg += f"... и ещё {len(updated_keys) - 10}\n"305                result_msg += "\n"306            307            # Содержимое архива308            result_msg += f"содержимое архива:\n"309            for file_path in processed_files:310                filename = os.path.basename(file_path)311                if filename == "translation_template.json":312                    result_msg += f"{filename} (шаблон)\n"313                else:314                    result_msg += f"{filename} (обновлённая локаль)\n"315            316            if errors:317                result_msg += f"\nпредупреждения:\n"318                for error in errors:319                    result_msg += f"{error}\n"320            321            return result_msg, zip_path322            323        finally:324            # Временная папка будет очищена автоматически при завершении сессии325            pass326            327    except Exception as e:328        return f"❌ Критическая ошибка: {str(e)}", None329 330def apply_translations(template_file, target_file):331    """Применяет переводы из шаблона к целевому файлу"""332    333    if not template_file or not target_file:334        return "❌ Загрузите оба файла: шаблон переводов и целевой файл.", None335    336    try:337        template_path = safe_get_path(template_file)338        target_path = safe_get_path(target_file)339        340        if not template_path or not target_path:341            return "❌ Ошибка получения путей к файлам", None342        343        if not os.path.exists(template_path):344            return f"❌ Файл шаблона не найден: {os.path.basename(template_path)}", None345        if not os.path.exists(target_path):346            return f"❌ Целевой файл не найден: {os.path.basename(target_path)}", None347        348        # Создаем файловый менеджер и загружаем данные349        file_manager = FileManager()350        351        try:352            template_type = file_manager.detect_file_type(template_path)353            target_type = file_manager.detect_file_type(target_path)354            355            if template_type != target_type:356                return f"❌ Типы файлов не совпадают: шаблон ({template_type}) vs целевой ({target_type})", None357            358            template_data, _ = file_manager.load_file(template_path)359            target_data, target_order = file_manager.load_file(target_path)360            361        except Exception as e:362            return f"❌ {str(e)}", None363        364        # Применяем переводы365        updated_data = target_data.copy()366        applied_count = 0367        368        for key, value in template_data.items():369            if key in updated_data:370                updated_data[key] = value371                applied_count += 1372        373        if applied_count == 0:374            return "ℹ️ Не найдено ключей для применения из шаблона", None375        376        # Сохраняем результат377        temp_dir = tempfile.mkdtemp()378        result_filename = f"translated_{os.path.basename(target_path)}"379        result_path = os.path.join(temp_dir, result_filename)380        381        try:382            file_manager.save_file(result_path, updated_data, target_order)383        except Exception as e:384            return f"❌ {str(e)}", None385        386        result_msg = f"Переводы применены.\n\n"387        result_msg += f"Тип файла: {target_type.upper()}\n"388        result_msg += f"Применено переводов: {applied_count}\n"389        result_msg += f"Результат сохранен как: {result_filename}"390        391        return result_msg, result_path392        393    except Exception as e:394        return f"❌ Критическая ошибка: {str(e)}", None395 396def create_full_interface():397    """Создает полнофункциональный интерфейс"""398    399    with gr.Blocks(400        title="Localization Manager Pro", 401        theme=gr.themes.Soft(),402        css="""403        .gradio-container {404            max-width: 1400px !important;405        }406        407        /* Кастомные стили для файловых компонентов */408        .custom-file-upload {409            border: 2px dashed #7c3aed !important;410            border-radius: 12px !important;411            background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;412            padding: 20px !important;413            text-align: center !important;414            transition: all 0.3s ease !important;415            min-height: 100px !important;416        }417        418        .custom-file-upload:hover {419            border-color: #5b21b6 !important;420            background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%) !important;421            transform: translateY(-2px) !important;422            box-shadow: 0 4px 12px rgba(124, 58, 237, 0.15) !important;423        }424        425        .custom-file-upload .file-preview {426            font-size: 14px !important;427            color: #374151 !important;428            margin-top: 8px !important;429        }430        431        .custom-file-upload .file-preview::before {432            content: "📄 ";433            color: #7c3aed;434            font-size: 16px;435        }436        437        /* Стили для лейблов */438        .custom-label {439            font-size: 16px !important;440            font-weight: 600 !important;441            color: #1f2937 !important;442            margin-bottom: 8px !important;443            display: block !important;444        }445        446        .custom-label .highlight {447            color: #7c3aed !important;448        }449        450        .custom-label .subtitle {451            font-size: 12px !important;452            color: #6b7280 !important;453            font-weight: 400 !important;454            margin-top: 4px !important;455        }456        457        /* Компактные результаты */458        .compact-result {459            min-height: 60px !important;460            max-height: 80px !important;461            border: 1px solid #e5e7eb !important;462            border-radius: 8px !important;463        }464        465        /* Улучшенные кнопки */466        .primary-button {467            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;468            border: none !important;469            border-radius: 12px !important;470            color: white !important;471            font-weight: 600 !important;472            padding: 16px 32px !important;473            font-size: 16px !important;474            transition: all 0.3s ease !important;475            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important;476        }477        478        .primary-button:hover {479            background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%) !important;480            transform: translateY(-2px) !important;481            box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3) !important;482        }483        484        /* Улучшенные текстовые поля */485        .custom-textbox {486            border: 1px solid #e5e7eb !important;487            border-radius: 8px !important;488            background: #fafafa !important;489        }490        491        /* Общие улучшения */492        .tab-nav {493            margin-bottom: 30px !important;494        }495        496        .main-title {497            font-size: 32px !important;498            font-weight: 700 !important;499            color: #1f2937 !important;500            margin-bottom: 8px !important;501        }502        503        .subtitle {504            font-size: 16px !important;505            color: #6b7280 !important;506            margin-bottom: 15px !important;507        }508        """509    ) as interface:510        511        gr.Markdown("# Localization Manager", elem_classes=["main-title"])512        gr.Markdown("инструмент для управления переводами и локализацией", elem_classes=["subtitle"])513        514        with gr.Tab("автоматическая обработка"):515            with gr.Row():516                with gr.Column():517                    gr.Markdown("### загрузка файлов", elem_classes=["custom-label"])518                    519                    old_ru_file = gr.File(520                        label="старая русская локаль",521                        file_types=[".json", ".properties"],522                        type="filepath",523                        elem_classes=["custom-file-upload"],524                        height=100525                    )526                    new_ru_file = gr.File(527                        label="новая русская локаль", 528                        file_types=[".json", ".properties"],529                        type="filepath",530                        elem_classes=["custom-file-upload"],531                        height=100532                    )533                    target_files = gr.File(534                        label="целевые локали для обновления",535                        file_count="multiple",536                        file_types=[".json", ".properties"],537                        type="filepath",538                        elem_classes=["custom-file-upload"],539                        height=100540                    )541                    542                    process_btn = gr.Button(543                        "обработать локали", 544                        variant="primary",545                        size="lg",546                        elem_classes=["primary-button"]547                    )548                549                with gr.Column():550                    gr.Markdown("### результаты", elem_classes=["custom-label"])551                    552                    process_result = gr.Textbox(553                        label="статистика",554                        lines=20,555                        interactive=False,556                        show_copy_button=True,557                        elem_classes=["custom-textbox"]558                    )559                    process_output = gr.File(560                        label="скачать архив",561                        interactive=False,562                        elem_classes=["compact-result"],563                        height=70564                    )565            566            process_btn.click(567                fn=process_localizations,568                inputs=[old_ru_file, new_ru_file, target_files],569                outputs=[process_result, process_output]570            )571        572        with gr.Tab("применение переводов"):573            with gr.Row():574                with gr.Column():575                    gr.Markdown("### загрузка файлов", elem_classes=["custom-label"])576                    577                    template_file = gr.File(578                        label="шаблон с переводами (JSON или Properties)",579                        file_types=[".json", ".properties"],580                        type="filepath",581                        elem_classes=["custom-file-upload"],582                        height=100583                    )584                    apply_target_file = gr.File(585                        label="целевая локаль (JSON или Properties)",586                        file_types=[".json", ".properties"],587                        type="filepath",588                        elem_classes=["custom-file-upload"],589                        height=100590                    )591                    592                    apply_btn = gr.Button(593                        "применить переводы", 594                        variant="primary",595                        size="lg",596                        elem_classes=["primary-button"]597                    )598                599                with gr.Column():600                    gr.Markdown("### результаты", elem_classes=["custom-label"])601                    602                    apply_result = gr.Textbox(603                        label="статистика",604                        lines=10,605                        interactive=False,606                        show_copy_button=True,607                        elem_classes=["custom-textbox"]608                    )609                    apply_output = gr.File(610                        label="скачать обновлённый файл",611                        interactive=False,612                        elem_classes=["compact-result"],613                        height=70614                    )615            616            apply_btn.click(617                fn=apply_translations,618                inputs=[template_file, apply_target_file],619                outputs=[apply_result, apply_output]620            )621        622        with gr.Tab("нейросетевой перевод"):623            gr.Markdown("""624            ## автоматический перевод с помощью нейросетей625            626            Автоматический перевод шаблонов с помощью обученных нейросетевых моделей.627            628            **Поддерживаемые языки:**629            - **Английский** - с помощью mBART модели630            - **Узбекский** - с помощью NLLB модели631            - **Другие языки** - будут помечены для ручного перевода632            """)633            634            with gr.Row():635                with gr.Column():636                    gr.Markdown("### загрузка шаблона", elem_classes=["custom-label"])637                    638                    neural_template_file = gr.File(639                        label="шаблон для перевода (JSON/Properties)",640                        file_types=[".json", ".properties"],641                        type="filepath",642                        elem_classes=["custom-file-upload"],643                        height=100644                    )645                    646                    neural_target_language = gr.Dropdown(647                        label="целевой язык",648                        choices=[649                            "английский (en)",650                            "узбекский (uz)", 651                            "французский (fr)",652                            "немецкий (de)",653                            "испанский (es)",654                            "итальянский (it)"655                        ],656                        value="английский (en)",657                        elem_classes=["custom-textbox"]658                    )659                    660                    neural_translate_btn = gr.Button(661                        "перевести автоматически", 662                        variant="primary",663                        elem_classes=["primary-button"]664                    )665                666                with gr.Column():667                    gr.Markdown("### результат", elem_classes=["custom-label"])668                    669                    neural_result = gr.Textbox(670                        label="отчет о переводе",671                        lines=15,672                        interactive=False,673                        show_copy_button=True,674                        elem_classes=["custom-textbox"]675                    )676                    neural_output = gr.File(677                        label="скачать переведенный файл",678                        interactive=False,679                        elem_classes=["compact-result"],680                        height=70681                    )682            683            # Подключаем обработчик684            neural_translate_btn.click(685                fn=auto_translate_template,686                inputs=[neural_template_file, neural_target_language],687                outputs=[neural_result, neural_output]688            )689 690        with gr.Tab("справка"):691            gr.Markdown("""692            ## руководство пользователя693            694            ### автоматическая обработка695            696            **шаг 1:** загрузите файлы697            - **старая русская локаль** - предыдущая версия ru.json или messages.properties698            - **новая русская локаль** - новая версия с изменениями (тот же формат)699            - **целевые локали** - файлы других языков (en.json, fr.json, messages-en.properties, и т.д.)700            701            **шаг 2:** нажмите "обработать локали"702            703            **результат:** ZIP архив с:704            - обновлёнными локалями705            - шаблоном для переводчиков (только новые ключи)706            707            ### применение переводов708            709            **шаг 1:** отправьте `translation_template.json` или `.properties` переводчикам710            711            **шаг 2:** после получения переведенного шаблона:712            - загрузите заполненный шаблон713            - загрузите целевую локаль714            - нажмите "применить переводы"715            716            **результат:** обновлённый файл с новыми переводами717            718            ### 🤖 нейросетевой перевод719            720            Автоматический перевод с помощью обученных нейросетевых моделей:721            722            **шаг 1:** загрузите шаблон для перевода (JSON или Properties)723            724            **шаг 2:** выберите целевой язык:725            - **Английский** - автоматический перевод с помощью mBART726            - **Узбекский** - автоматический перевод с помощью NLLB727            - **Другие языки** - разметка для ручного перевода728            729            **шаг 3:** нажмите "перевести автоматически"730            731            **результат:** полностью переведенный файл готовый к использованию!732            733            **преимущества:**734            - Мгновенный перевод всех ключей735            - Контекстуальный перевод для HR-доменов736            - Автоматическое определение типа контекста737            - Подробная статистика переводов738            739            ### поддерживаемые форматы740            741            #### JSON (фронтенд)742            - **формат файлов:** .json743            - **кодировка:** UTF-8744            - **структура:** плоский объект ключ-значение745            746            ```json747            {748              "key1": "Значение 1",749              "key2": "Значение 2"750            }751            ```752            753            #### Properties (бэкенд)754            - **формат файлов:** .properties755            - **кодировка:** UTF-8756            - **структура:** ключ=значение757            758            ```properties759            key1=Значение 1760            key2=Значение 2761            ```762            763            **Важно:** Все файлы в одной операции должны быть одного формата.764            """)765    766    return interface767 768if __name__ == "__main__":769    # Инициализируем нейросетевой переводчик при запуске770    print("Инициализируем нейросетевые модели...")771    initialize_neural_translator()772    773    interface = create_full_interface()774    interface.launch() 775