NefMoto

ECU Files => ECU Definition Files => Topic started by: Misterdray on October 31, 2025, 08:40:36 PM



Title: WinOLS Translator (Python Script)
Post by: Misterdray on October 31, 2025, 08:40:36 PM
Just wanted to share a Python Script that can be used to translate a WinOLS .json export from German to English which can then be imported back into WinOLS as English. When using this script, you must be connected to the internet as it uses Google Translation  and it does take a little bit of time to do. I have also included a script that can be used to split of the .json file into multiple files so the translation can go quicker. These multiple files can still be imported back into WinOLS individually.

TRANSLATOR SCRIPT
____________________________________

import json
import asyncio
from googletrans import Translator
import chardet
import re
import time
import tkinter as tk
from tkinter import filedialog

async def translate_json(input_file, output_file, retries=3, delay=1.5):
    translator = Translator()

    # --- Detect encoding ---
    with open(input_file, "rb") as f:
        raw_data = f.read()
        encoding = chardet.detect(raw_data)["encoding"] or "utf-8"
        print(f"Detected encoding: {encoding}")
        text = raw_data.decode(encoding, errors="replace")

    # --- Clean invalid chars ---
    cleaned_text = re.sub(r"[\x00-\x08\x0B-\x0C\x0E-\x1F]", "", text)
    data = json.loads(cleaned_text)

    async def safe_translate(text):
        if not text.strip():
            return text
        for attempt in range(retries):
            try:
                result = await translator.translate(text, src="de", dest="en")
                return result.text
            except Exception as e:
                print(f"Error translating '{text}': {e} (attempt {attempt+1}/{retries})")
                await asyncio.sleep(delay)
        print(f"Keeping original text for '{text}' (translation failed).")
        return text

    async def translate_field(obj):
        if isinstance(obj, dict):
            for key, value in obj.items():
                if key in ["IDName", "Name", "FolderName"] and isinstance(value, str):
                    obj[key] = await safe_translate(value)
                else:
                    await translate_field(value)
        elif isinstance(obj, list):
            for item in obj:
                await translate_field(item)

    await translate_field(data)

    with open(output_file, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=4)

    print(f"Translation complete. Saved as {output_file}")


if __name__ == "__main__":
    # --- File selection ---
    root = tk.Tk()
    root.withdraw()  # Hide main window

    print("Select the input JSON file...")
    input_path = filedialog.askopenfilename(
        title="Select JSON file to translate",
        filetypes=[("JSON Files", "*.json"), ("All Files", "*.*")]
    )
    if not input_path:
        print("No input file selected. Exiting.")
        exit()

    print("Choose where to save the translated JSON file...")
    output_path = filedialog.asksaveasfilename(
        title="Save translated JSON file as",
        defaultextension=".json",
        filetypes=[("JSON Files", "*.json")],
        initialfile="translated.json"
    )
    if not output_path:
        print("No output path. Exiting.")
        exit()

    asyncio.run(translate_json(input_path, output_path))





Title: Re: WinOLS Translator (Python Script)
Post by: Misterdray on October 31, 2025, 08:45:33 PM

____________________________________


Split .json SCRIPT
____________________________________

import json
import os
import math
import tkinter as tk
from tkinter import filedialog

def split_list(lst, n):
    """Return exactly n lists distributing elements as evenly as possible."""
    total = len(lst)
    base = total // n
    rem = total % n
    sizes = [(base + (1 if i < rem else 0)) for i in range(n)]
    out = []
    idx = 0
    for s in sizes:
        out.append(lst[idx: idx + s])
        idx += s
    return out

def split_items(items, n):
    """Split a list of (key,value) pairs into n chunks (lists of pairs)."""
    total = len(items)
    base = total // n
    rem = total % n
    sizes = [(base + (1 if i < rem else 0)) for i in range(n)]
    out = []
    idx = 0
    for s in sizes:
        out.append(items[idx: idx + s])
        idx += s
    return out

def split_json_file(skip_empty=False):
    root = tk.Tk()
    root.withdraw()
    input_path = filedialog.askopenfilename(
        title="Select a JSON file",
        filetypes=[("JSON Files", "*.json")]
    )
    if not input_path:
        print("No file selected. Exiting.")
        return

    # number of splits
    while True:
        try:
            num_splits = int(input("How many files would you like to split it into? ").strip())
            if num_splits < 1:
                print("Enter a positive integer.")
                continue
            break
        except ValueError:
            print("Enter a valid integer.")

    # read using windows-1252 (as you said)
    try:
        with open(input_path, "r", encoding="windows-1252") as f:
            data = json.load(f)
    except Exception as e:
        print("Failed to read JSON:", e)
        return

    # prepare output base and folder
    output_dir = filedialog.askdirectory(title="Select folder to save split files")
    if not output_dir:
        print("No folder selected. Exiting.")
        return

    default_base = os.path.splitext(os.path.basename(input_path))[0] + "_part"
    base_name = input(f"Enter base name for split files (default: '{default_base}'): ").strip() or default_base

    # Helper to write a JSON object to disk
    def write_json(obj, idx, total_width):
        filename = f"{base_name}_{str(idx).zfill(total_width)}.json"
        path = os.path.join(output_dir, filename)
        with open(path, "w", encoding="utf-8") as out:
            json.dump(obj, out, indent=2, ensure_ascii=False)
        return path

    # Decide splitting strategy
    wrote_any = False
    total_width = len(str(num_splits))

    if isinstance(data, list):
        # straightforward: split list elements
        parts = split_list(data, num_splits)
        for i, part in enumerate(parts, start=1):
            if skip_empty and len(part) == 0:
                print(f"Skipping empty part {i}")
                continue
            path = write_json(part, i, total_width)
            print(f"Saved {path} ({len(part)} items)")
            wrote_any = True

    elif isinstance(data, dict):
        keys = list(data.keys())
        if len(keys) > 1:
            # split top-level key/value pairs across files
            items = [(k, data[k]) for k in keys]
            chunks = split_items(items, num_splits)
            for i, chunk in enumerate(chunks, start=1):
                if skip_empty and len(chunk) == 0:
                    print(f"Skipping empty part {i}")
                    continue
                out_dict = {k: v for k, v in chunk}
                path = write_json(out_dict, i, total_width)
                print(f"Saved {path} ({len(chunk)} keys)")
                wrote_any = True

        elif len(keys) == 1:
            # single key at top-level — inspect its value
            top_key = keys[0]
            inner = data[top_key]
            if isinstance(inner, list):
                # split inner list and wrap back with same top-level key
                parts = split_list(inner, num_splits)
                for i, part in enumerate(parts, start=1):
                    if skip_empty and len(part) == 0:
                        print(f"Skipping empty part {i}")
                        continue
                    out_obj = {top_key: part}
                    path = write_json(out_obj, i, total_width)
                    print(f"Saved {path} ({len(part)} items under '{top_key}')")
                    wrote_any = True
            elif isinstance(inner, dict):
                # split inner dict items across files and wrap back
                inner_items = list(inner.items())
                chunks = split_items(inner_items, num_splits)
                for i, chunk in enumerate(chunks, start=1):
                    if skip_empty and len(chunk) == 0:
                        print(f"Skipping empty part {i}")
                        continue
                    out_inner = {k: v for k, v in chunk}
                    out_obj = {top_key: out_inner}
                    path = write_json(out_obj, i, total_width)
                    print(f"Saved {path} ({len(chunk)} keys under '{top_key}')")
                    wrote_any = True
            else:
                # scalar or unknown single-object — can't meaningfully split inner structure
                print("Top-level is a single key with a scalar/non-splittable value.")
                # We'll create first file with the full object and optionally empty others (or skip)
                if not skip_empty:
                    for i in range(1, num_splits + 1):
                        obj = data if i == 1 else ({} if isinstance(data, dict) else [])
                        path = write_json(obj, i, total_width)
                        print(f"Saved {path} ({'full object' if i==1 else 'empty'})")
                        wrote_any = True
                else:
                    path = write_json(data, 1, total_width)
                    print(f"Saved {path} (full object)")
                    wrote_any = True
        else:
            # empty dict
            print("Top-level JSON object is an empty object {}.")
            for i in range(1, num_splits + 1):
                if skip_empty:
                    print(f"Skipping empty part {i}")
                    continue
                path = write_json({}, i, total_width)
                print(f"Saved {path} (empty object)")
                wrote_any = True

    else:
        # scalar (string/number/bool/null)
        print("Top-level JSON is a scalar (not a list or dict).")
        if not skip_empty:
            for i in range(1, num_splits + 1):
                obj = data if i == 1 else None
                path = write_json(obj, i, total_width)
                print(f"Saved {path} ({'value' if i==1 else 'null'})")
                wrote_any = True
        else:
            path = write_json(data, 1, total_width)
            print(f"Saved {path} (scalar)")
            wrote_any = True

    if not wrote_any:
        print("No files were written (maybe all parts were empty and skip_empty=True).")
    else:
        print("Splitting complete!")

if __name__ == "__main__":
    # If you prefer to skip writing empty parts, call with skip_empty=True
    split_json_file(skip_empty=False)