оцени мой код
import tkinter as tk
from tkinter import messagebox, ttk
import math
import logging
import json
import os
from typing import Union, List, Dict
from datetime import datetime

# Настройка логирования
logging.basicConfig(
    filename='calculator.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

HISTORY_FILE = 'history.json'
LOCALE_FILES = {
    'ru': 'locales/ru.json',
    'en': 'locales/en.json'
}
DEFAULT_LOCALE = 'en'


class CalculatorApp:
    def __init__(self, root):
        self.root = root
        self.current_locale = 'ru'
        self.locale = self.load_locale(self.current_locale)
        self.history: List[Dict] = []
        self.create_widgets()
        self.load_history()
        self.bind_shortcuts()

    def load_locale(self, locale_code: str) -> dict:
        locale_file = LOCALE_FILES.get(locale_code, LOCALE_FILES[DEFAULT_LOCALE])
        try:
            with open(locale_file, 'r', encoding='utf-8-sig') as f:
                locale_data = json.load(f)
                logging.info(f"Загружена локализация: {locale_code}")
                return locale_data
        except FileNotFoundError:
            logging.error(f"Файл локализации не найден: {locale_file}. Используется {DEFAULT_LOCALE}.")
            if locale_code != DEFAULT_LOCALE:
                return self.load_locale(DEFAULT_LOCALE)
            return {}
        except json.JSONDecodeError as e:
            logging.error(f"Ошибка парсинга файла локализации: {locale_file} - {e}. Используется {DEFAULT_LOCALE}.")
            if locale_code != DEFAULT_LOCALE:
                return self.load_locale(DEFAULT_LOCALE)
            return {}

    def translate(self, key: str) -> str:
        return self.locale.get(key, key)

    # Арифметические операции
    def add(self, x: float, y: float) -> float:
        return x + y

    def subtract(self, x: float, y: float) -> float:
        return x - y

    def multiply(self, x: float, y: float) -> float:
        return x * y

    def divide(self, x: float, y: float) -> float:
        if y == 0:
            raise ValueError(self.translate("На ноль делить нельзя"))
        return x / y

    def power(self, x: float, y: float) -> float:
        return x ** y

    def sqrt(self, x: float) -> float:
        if x < 0:
            raise ValueError(self.translate("Нельзя вычислить квадратный корень из отрицательного числа"))
        return math.sqrt(x)

    def is_valid_number(self, entry: str) -> bool:
        try:
            float(entry)
            return True
        except ValueError:
            return False

    def save_history(self) -> None:
        try:
            with open(HISTORY_FILE, 'w', encoding='utf-8') as f:
                json.dump(self.history, f, ensure_ascii=False, indent=4)
            logging.info(self.translate("История операций сохранена"))
        except Exception as e:
            logging.error(f"Ошибка при сохранении истории: {e}")

    def load_history(self) -> None:
        if os.path.exists(HISTORY_FILE):
            try:
                with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
                    self.history = json.load(f)
                self.update_history_display()
                logging.info(self.translate("История операций загружена"))
            except Exception as e:
                logging.error(f"Ошибка при загрузке истории: {e}")
                self.history = []
        else:
            self.history = []

    def calculate(self, num1: float, num2: Union[float, None], operation: str) -> float:
        operations_map = {
            "add": self.add,
            "subtract": self.subtract,
            "multiply": self.multiply,
            "divide": self.divide,
            "power": self.power,
            "sqrt": self.sqrt
        }
        operation_func = operations_map.get(operation)
        if not operation_func:
            raise ValueError(self.translate("Неверная операция"))
        if operation == "sqrt":
            return operation_func(num1)
        return operation_func(num1, num2)

    def perform_operation(self, event: tk.Event = None) -> None:
        try:
            num1_str = self.entry1.get().strip()
            if not self.is_valid_number(num1_str):
                raise ValueError(self.translate("Первое число должно быть числом"))
            num1 = float(num1_str)

            operation_display = self.selected_operation.get()
            operation = self.display_operation_map.get(operation_display, None)
            if operation is None:
                raise ValueError(self.translate("Неверная операция"))

            if operation == "sqrt":
                result = self.calculate(num1, None, operation)
                num2 = None
                self.entry2.delete(0, tk.END)
                expression = f"√{num1}"
            else:
                num2_str = self.entry2.get().strip()
                if not self.is_valid_number(num2_str):
                    raise ValueError(self.translate("Второе число должно быть числом"))
                num2 = float(num2_str)
                result = self.calculate(num1, num2, operation)
                symbol = self.operation_symbol(operation)
                expression = f"{num1} {symbol} {num2}"

            self.result_label.config(text=f"{self.translate('Результат')}: {result}")

            # Форматирование записи для истории
            time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            history_entry = {
                "time": time_now,
                "operation": self.get_operation_name(operation),
                "expression": expression,
                "result": result
            }
            self.history.append(history_entry)
            display_entry = f"{history_entry['time']} - {history_entry['operation']}: {history_entry['expression']} = {history_entry['result']}"
            self.history_text.config(state=tk.NORMAL)
            self.history_text.insert(tk.END, display_entry + '\n')
            self.history_text.see(tk.END)
            self.history_text.config(state=tk.DISABLED)
            self.save_history()
            logging.info(f"{self.translate('Выполнена операция')}: {history_entry['operation']} {self.translate('с числами')} {num1} и {num2 if num2 is not None else ''}. {self.translate('Результат')}: {result}")
        except ValueError as e:
            messagebox.showerror(self.translate("Ошибка"), str(e))
            logging.error(f"{self.translate('Ошибка ввода данных')}: {str(e)}")
        except Exception as e:
            messagebox.showerror(self.translate("Ошибка"), self.translate("Неверный ввод данных"))
            logging.error(f"{self.translate('Непредвиденная ошибка')}: {str(e)}")

    def operation_symbol(self, operation: str) -> str:
        symbols = {
            "add": '+',
            "subtract": '-',
            "multiply": '*',
            "divide": '/',
            "power": '^',
        }
        return symbols.get(operation, '')

    def get_operation_name(self, operation: str) -> str:
        operation_names = {
            "add": self.translate("Сложение"),
            "subtract": self.translate("Вычитание"),
            "multiply": self.translate("Умножение"),
            "divide": self.translate("Деление"),
            "power": self.translate("Возведение в степень"),
            "sqrt": self.translate("Квадратный корень")
        }
        return operation_names.get(operation, operation)

    def clear_entries(self, event: tk.Event = None) -> None:
        self.entry1.delete(0, tk.END)
        self.entry2.delete(0, tk.END)
        self.result_label.config(text=f"{self.translate('Результат')}: ")

    def on_operation_change(self, event: tk.Event = None) -> None:
        if self.display_operation_map.get(self.selected_operation.get(), "") == "sqrt":
            self.entry2.config(state=tk.DISABLED)
        else:
            self.entry2.config(state=tk.NORMAL)

    def show_info(self, event: tk.Event = None) -> None:
        messagebox.showinfo(self.translate("О калькуляторе"), self.translate("Этот калькулятор выполнен на Python с использованием библиотеки Tkinter."))

    def show_shortcuts(self, event: tk.Event = None) -> None:
        shortcuts_text = (
            f"{self.translate('Ctrl+C')}: {self.translate('Скопировать результат')}\n"
            f"{self.translate('Ctrl+I')}: {self.translate('О приложении')}\n"
            f"{self.translate('Ctrl+Q')}: {self.translate('Выход')}\n"
            f"{self.translate('Ctrl+K')}: {self.translate('Клавиатурные шорткаты')}"
        )
        messagebox.showinfo(
            self.translate("Клавиатурные шорткаты"),
            shortcuts_text
        )

    def exit_app(self, event: tk.Event = None) -> None:
        self.save_history()
        self.root.destroy()

    def copy_result(self, event: tk.Event = None) -> None:
        result_text = self.result_label.cget("text")
        if result_text != f"{self.translate('Результат')}: ":
            self.root.clipboard_clear()
            self.root.clipboard_append(result_text.split(": ", 1)[1])
            messagebox.showinfo(self.translate("Скопировано"), self.translate("Результат скопирован в буфер обмена."))
        else:
            messagebox.showwarning(self.translate("Предупреждение"), self.translate("Нет результата для копирования."))

    def switch_language(self) -> None:
        self.current_locale = 'en' if self.current_locale == 'ru' else 'ru'
        self.locale = self.load_locale(self.current_locale)
        self.update_ui_language()

    def update_ui_language(self) -> None:
        self.root.title(self.translate("Калькулятор"))
        self.update_labels()
        self.update_buttons()
        self.update_operation_menu()
        self.theme_label.config(text=self.translate("Тема") + ": ")

    def update_labels(self) -> None:
        labels = {
            "Первое число": self.label1,
            "Второе число": self.label2,
            "Операция": self.label3,
            "Результат": self.result_label,
            "История операций": self.label_history
        }

        for key, widget in labels.items():
            if widget == self.result_label:
                current_text = widget.cget("text").split(": ")[0]
                widget.config(text=f"{self.translate(key)}: ")
            else:
                widget.config(text=self.translate(key))

    def update_buttons(self) -> None:
        buttons = {
            "Вычислить": self.calculate_button,
            "Очистить": self.clear_button,
            "Скопировать результат": self.copy_button,
            "О приложении": self.info_button,
            "Клавиатурные шорткаты": self.shortcuts_button,
            "Выход": self.exit_button,
            "Переключить язык": self.language_button
        }

        for key, widget in buttons.items():
            widget.config(text=self.translate(key))

    def update_operation_display_map(self):
        self.operation_display_map = {
            "add": self.translate("Сложение"),
            "subtract": self.translate("Вычитание"),
            "multiply": self.translate("Умножение"),
            "divide": self.translate("Деление"),
            "power": self.translate("Возведение в степень"),
            "sqrt": self.translate("Квадратный корень")
        }
        self.display_operation_map = {v: k for k, v in self.operation_display_map.items()}

    def update_operation_menu(self) -> None:
        self.update_operation_display_map()
        self.operation_menu['values'] = list(self.operation_display_map.values())
        # Установить текущую операцию, если она доступна в новом списке
        current_display = self.get_operation_name_map_reverse()
        if current_display in self.display_operation_map:
            self.selected_operation.set(current_display)
        else:
            self.selected_operation.set(self.operation_display_map["add"])
            self.operation_menu.current(0)

    def get_operation_name_map_reverse(self):
        # Возвращает текущую операцию в виде отображаемого названия
        return self.operation_menu.get()

    def switch_theme(self, theme: str) -> None:
        self.style.theme_use(theme)

    def bind_shortcuts(self) -> None:
        self.root.bind('<Control-c>', self.copy_result)
        self.root.bind('<Control-C>', self.copy_result)
        self.root.bind('<Control-i>', self.show_info)
        self.root.bind('<Control-I>', self.show_info)
        self.root.bind('<Control-q>', self.exit_app)
        self.root.bind('<Control-Q>', self.exit_app)
        self.root.bind('<Control-k>', self.show_shortcuts)
        self.root.bind('<Control-K>', self.show_shortcuts)
        self.root.bind('<Return>', self.perform_operation)
        self.root.bind('<Escape>', self.clear_entries)

    def create_widgets(self):
        self.style = ttk.Style()
        self.style.theme_use('clam')

        self.selected_operation = tk.StringVar()
        # Initialize operation_display_map
        self.update_operation_display_map()

        # Labels and Entries
        self.label1 = ttk.Label(self.root, text=self.translate("Первое число"), font=("Arial", 14), background="#2e3f4f", foreground="white")
        self.label1.grid(row=0, column=0, padx=10, pady=10, sticky='w')
        self.entry1 = ttk.Entry(self.root, font=("Arial", 14))
        self.entry1.grid(row=0, column=1, padx=10, pady=10, sticky='w')

        self.label2 = ttk.Label(self.root, text=self.translate("Второе число"), font=("Arial", 14), background="#2e3f4f", foreground="white")
        self.label2.grid(row=1, column=0, padx=10, pady=10, sticky='w')
        self.entry2 = ttk.Entry(self.root, font=("Arial", 14))
        self.entry2.grid(row=1, column=1, padx=10, pady=10, sticky='w')

        self.label3 = ttk.Label(self.root, text=self.translate("Операция"), font=("Arial", 14), background="#2e3f4f", foreground="white")
        self.label3.grid(row=2, column=0, padx=10, pady=10, sticky='w')

        # Operation Menu as Combobox
        self.operation_menu = ttk.Combobox(
            self.root,
            textvariable=self.selected_operation,
            values=list(self.operation_display_map.values()),
            state="readonly",
            font=("Arial", 12)
        )
        self.operation_menu.grid(row=2, column=1, padx=10, pady=10, sticky='w')
        self.operation_menu.current(0)
        self.operation_menu.bind("<<ComboboxSelected>>", self.on_operation_change)

        # Buttons
        self.calculate_button = ttk.Button(self.root, text=self.translate("Вычислить"), command=self.perform_operation)
        self.calculate_button.grid(row=3, column=0, columnspan=2, pady=10, sticky='w')

        self.clear_button = ttk.Button(self.root, text=self.translate("Очистить"), command=self.clear_entries)
        self.clear_button.grid(row=4, column=0, columnspan=2, pady=10, sticky='w')

        self.result_label = ttk.Label(self.root, text=f"{self.translate('Результат')}: ", font=("Arial", 14), background="#2e3f4f", foreground="white")
        self.result_label.grid(row=5, column=0, columnspan=2, pady=10, sticky='w')

        self.copy_button = ttk.Button(self.root, text=self.translate("Скопировать результат"), command=self.copy_result)
        self.copy_button.grid(row=6, column=0, columnspan=2, pady=10, sticky='w')

        self.info_button = ttk.Button(self.root, text=self.translate("О приложении"), command=self.show_info)
        self.info_button.grid(row=7, column=0, columnspan=2, pady=10, sticky='w')

        self.shortcuts_button = ttk.Button(self.root, text=self.translate("Клавиатурные шорткаты"), command=self.show_shortcuts)
        self.shortcuts_button.grid(row=8, column=0, columnspan=2, pady=10, sticky='w')

        self.exit_button = ttk.Button(self.root, text=self.translate("Выход"), command=self.exit_app)
        self.exit_button.grid(row=9, column=0, columnspan=2, pady=10, sticky='w')

        self.language_button = ttk.Button(self.root, text=self.translate("Переключить язык"), command=self.switch_language)
        self.language_button.grid(row=10, column=0, columnspan=2, pady=10, sticky='w')

        # History Section
        self.label_history = ttk.Label(self.root, text=self.translate("История операций"), font=("Arial", 14), background="#2e3f4f", foreground="white")
        self.label_history.grid(row=0, column=2, padx=10, pady=10, sticky='nw')

        self.history_frame = ttk.Frame(self.root)
        self.history_frame.grid(row=1, column=2, rowspan=10, padx=10, pady=10, sticky='nw')
        self.history_text = tk.Text(self.history_frame, height=18, width=30, font=("Arial", 12), state=tk.DISABLED, bg="#f0f0f0")
        self.history_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.history_scroll = ttk.Scrollbar(self.history_frame, command=self.history_text.yview)
        self.history_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.history_text.config(yscrollcommand=self.history_scroll.set)

        # Theme Selection
        self.theme_label = ttk.Label(self.root, text=self.translate("Тема") + ":", font=("Arial", 14), background="#2e3f4f", foreground="white")
        self.theme_label.grid(row=11, column=0, padx=10, pady=10, sticky='w')
        self.theme_menu = ttk.Combobox(self.root, values=self.style.theme_names(), state="readonly")
        current_theme = self.style.theme_use()
        self.theme_menu.set(current_theme)
        self.theme_menu.grid(row=11, column=1, padx=10, pady=10, sticky='w')
        self.theme_menu.bind("<<ComboboxSelected>>", lambda event: self.switch_theme(self.theme_menu.get()))

    def update_history_display(self):
        self.history_text.config(state=tk.NORMAL)
        self.history_text.delete('1.0', tk.END)
        for entry in self.history:
            display_entry = f"{entry['time']} - {entry['operation']}: {entry['expression']} = {entry['result']}"
            self.history_text.insert(tk.END, display_entry + '\n')
        self.history_text.see(tk.END)
        self.history_text.config(state=tk.DISABLED)

    def update_operation_display_map(self):
        self.operation_display_map = {
            "add": self.translate("Сложение"),
            "subtract": self.translate("Вычитание"),
            "multiply": self.translate("Умножение"),
            "divide": self.translate("Деление"),
            "power": self.translate("Возведение в степень"),
            "sqrt": self.translate("Квадратный корень")
        }
        self.display_operation_map = {v: k for k, v in self.operation_display_map.items()}

    def update_operation_menu(self) -> None:
        self.update_operation_display_map()
        self.operation_menu['values'] = list(self.operation_display_map.values())
        # Установить текущую операцию, если она доступна в новом списке
        current_display = self.get_operation_name_map_reverse()
        if current_display in self.display_operation_map:
            self.selected_operation.set(current_display)
        else:
            self.selected_operation.set(self.operation_display_map["add"])
            self.operation_menu.current(0)

    def get_operation_name_map_reverse(self):
        # Возвращает текущую операцию в виде отображаемого названия
        return self.operation_menu.get()

    def switch_theme(self, theme: str) -> None:
        self.style.theme_use(theme)

    def bind_shortcuts(self) -> None:
        self.root.bind('<Control-c>', self.copy_result)
        self.root.bind('<Control-C>', self.copy_result)
        self.root.bind('<Control-i>', self.show_info)
        self.root.bind('<Control-I>', self.show_info)
        self.root.bind('<Control-q>', self.exit_app)
        self.root.bind('<Control-Q>', self.exit_app)
        self.root.bind('<Control-k>', self.show_shortcuts)
        self.root.bind('<Control-K>', self.show_shortcuts)
        self.root.bind('<Return>', self.perform_operation)
        self.root.bind('<Escape>', self.clear_entries)


if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("900x600")
    root.configure(bg="#2e3f4f")
    app = CalculatorApp(root)
    root.mainloop()

предложи улучшения для него но пока не делай их