Dien-Hoa
refactor translate 1 and multi languages
fa7018d
from fasthtml.common import *
from fasthtml.xtend import CheckboxX
from translator import languages as VALID_LANGUAGES, translate_to_languages
import tempfile
from starlette.requests import Request
from typing import List
from starlette.responses import FileResponse, PlainTextResponse
import os
import mimetypes
import zipfile
import io
css = Style(':root {--pico-font-size:90%,--pico-font-family: Pacifico, cursive;}')
app = FastHTML(hdrs=(picolink, css))
TEMP_FILES = {}
# Extract form generation to a separate function
def generate_language_form():
return Form(
Input(type='file', name='sbv_file', accept='.sbv', required=True),
H3('Select languages for translation'),
Fieldset(
Legend("Languages"),
Div(
*[CheckboxX(
label=lang,
value=lang,
id=f'lang_{lang}',
name='languages'
) for lang in VALID_LANGUAGES],
class_='grid'
)
),
Button('Translate', class_="primary"),
action="/upload", method='post', enctype='multipart/form-data'
)
@app.get('/')
def home():
return Container(
Article(
H1("SBV File Translator"),
P("Upload an SBV File and select languages for translation:"),
generate_language_form(),
)
)
# Extract file processing logic to a separate function
def translate_and_process_sbv(content: bytes, filename: str, languages: List[str]) -> dict:
"""
Translate and process an SBV file for the given languages.
Args:
content (bytes): The content of the uploaded SBV file.
filename (str): The name of the uploaded file.
languages (List[str]): List of target languages for translation.
Returns:
dict: A dictionary containing translation results, file paths, and zip filename.
"""
try:
temp_dir = tempfile.mkdtemp()
input_file = os.path.join(temp_dir, filename)
# Ensure the directory exists
os.makedirs(os.path.dirname(input_file), exist_ok=True)
with open(input_file, 'wb') as f:
f.write(content)
selected_languages = [lang for lang in languages if lang in VALID_LANGUAGES]
output_dir = os.path.join(temp_dir, "translations")
os.makedirs(output_dir, exist_ok=True)
translate_to_languages(input_file, output_dir, selected_languages)
return create_translation_files(temp_dir, output_dir, filename, selected_languages)
except Exception as e:
print(f"Error in translate_and_process_sbv: {str(e)}")
raise # Re-raise the exception after logging
# Extract translation file creation logic to a separate function
def create_translation_files(temp_dir: str, output_dir: str, filename: str, languages: List[str]) -> dict:
"""
Create translation files and organize them for download.
This function processes the translated SBV files, organizes them in a temporary directory,
creates a zip file containing all translations, and prepares the data for the response.
Args:
temp_dir (str): Path to the temporary directory for storing files.
output_dir (str): Path to the directory containing the translated SBV files.
filename (str): Name of the original uploaded file.
languages (List[str]): List of languages for which translations were created.
Returns:
dict: A dictionary containing:
- 'translations': Dict of language codes to translated content.
- 'file_paths': Dict of language codes to paths of individual translation files.
- 'zip_filename': Name of the zip file containing all translations.
"""
translations = {}
file_paths = {}
base_filename = os.path.splitext(filename)[0]
for lang in languages:
translated_file = os.path.join(output_dir, f"{base_filename}_{lang}.sbv")
with open(translated_file, 'r', encoding='utf-8') as f:
translations[lang] = f.read()
new_filename = f"{base_filename}_{lang}.sbv"
new_path = os.path.join(temp_dir, new_filename)
os.rename(translated_file, new_path)
file_paths[lang] = new_path
TEMP_FILES[new_filename] = new_path
zip_filename = f"{base_filename}_{'_'.join(languages)}.zip"
zip_path = os.path.join(temp_dir, zip_filename)
with zipfile.ZipFile(zip_path, 'w') as zip_file:
for lang, file_path in file_paths.items():
zip_file.write(file_path, os.path.basename(file_path))
TEMP_FILES[zip_filename] = zip_path
return {
'translations': translations,
'file_paths': file_paths,
'zip_filename': zip_filename
}
@app.post('/upload')
async def upload_file(request: Request, sbv_file: UploadFile):
content = await sbv_file.read()
# Get form data
form = await request.form()
languages = form.getlist('languages')
# Ensure languages is always a list
if not languages:
return PlainTextResponse("Please select at least one language for translation.", status_code=400)
result = translate_and_process_sbv(content, sbv_file.filename, languages)
return Container(
Article(
H1('Translations'),
A("Download All Translations",
href=f"/download/{result['zip_filename']}",
download=True,
class_="primary",
role="button"
),
*[Card(
H3(f'{lang.capitalize()} Translation'),
Pre(
'\n'.join(result['translations'][lang].split('\n')[:9]) + '\n...',
),
Footer(
A(f"Download {lang.capitalize()} Translation",
href=f"/download/{os.path.basename(result['file_paths'][lang])}",
download=True,
class_="secondary outline",
role="button"
)
)
) for lang in result['translations']],
class_="container-fluid" # Add this class for full-width content
)
)
@app.get("/download/{filename}")
def get(filename: str):
file_path = TEMP_FILES.get(filename)
if file_path and os.path.exists(file_path):
mime_type, _ = mimetypes.guess_type(file_path)
return FileResponse(
file_path,
filename=filename,
media_type=mime_type or "application/octet-stream",
headers={
"Content-Disposition": f"attachment; filename={filename}",
"X-Content-Type-Options": "nosniff",
}
)
else:
return PlainTextResponse("File not found", status_code=404)
serve()