Spaces:
Sleeping
Sleeping
Trung Vu commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -21,11 +21,18 @@ LANG_TARGET = 'vi'
|
|
| 21 |
OUTPUT_SUFFIX = "_Dich_CN"
|
| 22 |
BATCH_SIZE = 100
|
| 23 |
SLEEP_BETWEEN_BATCHES = 0.1
|
| 24 |
-
GEMINI_MODEL = "gemini-1.5-flash-latest"
|
| 25 |
MAX_RETRIES = 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
# -----------------
|
| 27 |
|
| 28 |
-
# --- HELPER FUNCTIONS (
|
| 29 |
|
| 30 |
def safe_sheet_title(base: str) -> str:
|
| 31 |
cleaned = re.sub(r'[:\\/?*\[\]]', '_', base)
|
|
@@ -40,7 +47,6 @@ def excel_quote_sheet(name: str) -> str:
|
|
| 40 |
def _gemini_translate_call(model, texts: List[str], *, src: str, tgt: str, max_retries: int = MAX_RETRIES) -> List[str]:
|
| 41 |
if not texts:
|
| 42 |
return []
|
| 43 |
-
|
| 44 |
system_prompt = (
|
| 45 |
"You are a professional translator. "
|
| 46 |
f"Translate each input string from {src} to {tgt}. "
|
|
@@ -55,7 +61,6 @@ def _gemini_translate_call(model, texts: List[str], *, src: str, tgt: str, max_r
|
|
| 55 |
+ json.dumps(payload, ensure_ascii=False)
|
| 56 |
+ "\n\nOUTPUT: JSON array of strings only."
|
| 57 |
)
|
| 58 |
-
|
| 59 |
for attempt in range(1, max_retries + 1):
|
| 60 |
try:
|
| 61 |
resp = model.generate_content(user_msg)
|
|
@@ -85,27 +90,32 @@ def update_formula_references(sheet, sheet_map):
|
|
| 85 |
formula = pattern.sub(f"{new_q}!", formula)
|
| 86 |
cell.value = formula
|
| 87 |
|
| 88 |
-
# ---
|
| 89 |
-
|
|
|
|
|
|
|
| 90 |
"""
|
| 91 |
Main function to be called by the Gradio interface.
|
| 92 |
-
It takes the API key and uploaded file, performs translation, and returns the output file.
|
| 93 |
"""
|
| 94 |
if not api_key or not api_key.strip():
|
| 95 |
raise gr.Error("Gemini API Key is missing. Please paste your key.")
|
| 96 |
if input_file is None:
|
| 97 |
raise gr.Error("No Excel file uploaded. Please upload a .xlsx file.")
|
|
|
|
|
|
|
| 98 |
|
| 99 |
logs = []
|
| 100 |
|
| 101 |
try:
|
| 102 |
-
# 1. Configure Gemini with the user-provided key
|
| 103 |
genai.configure(api_key=api_key.strip())
|
| 104 |
-
|
| 105 |
-
|
|
|
|
| 106 |
yield "\n".join(logs), None # Update logs, no file yet
|
| 107 |
|
| 108 |
-
#
|
| 109 |
wb = load_workbook(input_file.name)
|
| 110 |
original_sheet_names = [name for name in wb.sheetnames if OUTPUT_SUFFIX not in name]
|
| 111 |
logs.append(f"✅ Loaded Excel file. Found {len(original_sheet_names)} sheet(s) to translate.")
|
|
@@ -116,10 +126,8 @@ def process_translation(api_key: str, input_file, progress=gr.Progress(track_tqd
|
|
| 116 |
# 3. Step 1: Translate and create new sheets
|
| 117 |
for sheet_name in progress.tqdm(original_sheet_names, desc="Translating Sheets"):
|
| 118 |
source_sheet = wb[sheet_name]
|
| 119 |
-
# FIX: Was using the undefined 'source_sheet_name' here
|
| 120 |
new_sheet_name_raw = f"{sheet_name}{OUTPUT_SUFFIX}"
|
| 121 |
new_sheet_name = safe_sheet_title(new_sheet_name_raw)
|
| 122 |
-
# FIX: Was using 'source_sheet_name' as the key
|
| 123 |
sheet_name_map[sheet_name] = new_sheet_name
|
| 124 |
|
| 125 |
if new_sheet_name in wb.sheetnames:
|
|
@@ -141,7 +149,6 @@ def process_translation(api_key: str, input_file, progress=gr.Progress(track_tqd
|
|
| 141 |
cells_to_translate.append({'text': val, 'row': cell.row, 'col': cell.column})
|
| 142 |
|
| 143 |
total_cells = len(cells_to_translate)
|
| 144 |
-
# FIX: Was using 'source_sheet_name' in the log message
|
| 145 |
logs.append(f"--- Translating Sheet: {sheet_name} ({total_cells} cells) -> {new_sheet_name} ---")
|
| 146 |
yield "\n".join(logs), None
|
| 147 |
|
|
@@ -176,14 +183,12 @@ def process_translation(api_key: str, input_file, progress=gr.Progress(track_tqd
|
|
| 176 |
target_cell.value = translated
|
| 177 |
|
| 178 |
processed_count = min(idx + len(chunk), total_cells)
|
| 179 |
-
# FIX: Was using 'source_sheet_name' in the log update
|
| 180 |
logs[-1] = f"--- Translating Sheet: {sheet_name} ({processed_count}/{total_cells} cells) -> {new_sheet_name} ---"
|
| 181 |
yield "\n".join(logs), None
|
| 182 |
|
| 183 |
idx += BATCH_SIZE
|
| 184 |
time.sleep(SLEEP_BETWEEN_BATCHES)
|
| 185 |
|
| 186 |
-
# FIX: Was using 'source_sheet_name' here
|
| 187 |
logs.append(f"✅ Finished sheet: {sheet_name}")
|
| 188 |
yield "\n".join(logs), None
|
| 189 |
|
|
@@ -207,7 +212,6 @@ def process_translation(api_key: str, input_file, progress=gr.Progress(track_tqd
|
|
| 207 |
yield "\n".join(logs), output_file_path
|
| 208 |
|
| 209 |
except Exception as e:
|
| 210 |
-
# Use gr.Error to show a popup to the user
|
| 211 |
raise gr.Error(f"An error occurred: {e}")
|
| 212 |
|
| 213 |
# --- GRADIO INTERFACE ---
|
|
@@ -216,16 +220,17 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 216 |
gr.Markdown(
|
| 217 |
"""
|
| 218 |
# 批量Excel翻译工具 (ZH-VI) | Excel Translator (ZH-VI)
|
| 219 |
-
使用Google Gemini
|
| 220 |
|
| 221 |
**使用说明 (Instructions):**
|
| 222 |
1. 在下方输入您的 Google Gemini API Key。(API Key is kept private and not stored).
|
| 223 |
-
2.
|
| 224 |
-
3.
|
| 225 |
-
4.
|
| 226 |
-
5.
|
|
|
|
| 227 |
"""
|
| 228 |
-
)
|
| 229 |
with gr.Row():
|
| 230 |
with gr.Column(scale=1):
|
| 231 |
api_key_input = gr.Textbox(
|
|
@@ -233,6 +238,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 233 |
type="password",
|
| 234 |
placeholder="Enter your API key here..."
|
| 235 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
file_input = gr.File(
|
| 237 |
label="Upload Excel File (.xlsx)",
|
| 238 |
file_types=[".xlsx"]
|
|
@@ -248,9 +259,10 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 248 |
)
|
| 249 |
file_output = gr.File(label="下载翻译文件 (Download Translated File)")
|
| 250 |
|
|
|
|
| 251 |
translate_button.click(
|
| 252 |
fn=process_translation,
|
| 253 |
-
inputs=[api_key_input, file_input],
|
| 254 |
outputs=[log_output, file_output]
|
| 255 |
)
|
| 256 |
|
|
|
|
| 21 |
OUTPUT_SUFFIX = "_Dich_CN"
|
| 22 |
BATCH_SIZE = 100
|
| 23 |
SLEEP_BETWEEN_BATCHES = 0.1
|
|
|
|
| 24 |
MAX_RETRIES = 5
|
| 25 |
+
|
| 26 |
+
# List of available Gemini models for the user to choose from
|
| 27 |
+
GEMINI_MODELS = [
|
| 28 |
+
"gemini-flash-latest", # Fast, cost-effective, good for most tasks
|
| 29 |
+
"gemini-2.5-pro", # Highest quality, larger context window
|
| 30 |
+
"gemini-flash-lite-latest", # Previous generation pro model
|
| 31 |
+
] #
|
| 32 |
+
|
| 33 |
# -----------------
|
| 34 |
|
| 35 |
+
# --- HELPER FUNCTIONS (Unchanged) ---
|
| 36 |
|
| 37 |
def safe_sheet_title(base: str) -> str:
|
| 38 |
cleaned = re.sub(r'[:\\/?*\[\]]', '_', base)
|
|
|
|
| 47 |
def _gemini_translate_call(model, texts: List[str], *, src: str, tgt: str, max_retries: int = MAX_RETRIES) -> List[str]:
|
| 48 |
if not texts:
|
| 49 |
return []
|
|
|
|
| 50 |
system_prompt = (
|
| 51 |
"You are a professional translator. "
|
| 52 |
f"Translate each input string from {src} to {tgt}. "
|
|
|
|
| 61 |
+ json.dumps(payload, ensure_ascii=False)
|
| 62 |
+ "\n\nOUTPUT: JSON array of strings only."
|
| 63 |
)
|
|
|
|
| 64 |
for attempt in range(1, max_retries + 1):
|
| 65 |
try:
|
| 66 |
resp = model.generate_content(user_msg)
|
|
|
|
| 90 |
formula = pattern.sub(f"{new_q}!", formula)
|
| 91 |
cell.value = formula
|
| 92 |
|
| 93 |
+
# --- MAIN PROCESSING FUNCTION FOR GRADIO ---
|
| 94 |
+
|
| 95 |
+
# <-- MODIFIED: Added 'model_name' as an argument
|
| 96 |
+
def process_translation(api_key: str, model_name: str, input_file, progress=gr.Progress(track_tqdm=True)):
|
| 97 |
"""
|
| 98 |
Main function to be called by the Gradio interface.
|
| 99 |
+
It takes the API key, selected model, and uploaded file, performs translation, and returns the output file.
|
| 100 |
"""
|
| 101 |
if not api_key or not api_key.strip():
|
| 102 |
raise gr.Error("Gemini API Key is missing. Please paste your key.")
|
| 103 |
if input_file is None:
|
| 104 |
raise gr.Error("No Excel file uploaded. Please upload a .xlsx file.")
|
| 105 |
+
if not model_name:
|
| 106 |
+
raise gr.Error("No Gemini model selected. Please choose a model from the dropdown.")
|
| 107 |
|
| 108 |
logs = []
|
| 109 |
|
| 110 |
try:
|
| 111 |
+
# 1. Configure Gemini with the user-provided key and selected model
|
| 112 |
genai.configure(api_key=api_key.strip())
|
| 113 |
+
# <-- MODIFIED: Use the 'model_name' argument instead of a fixed constant
|
| 114 |
+
model = genai.GenerativeModel(model_name)
|
| 115 |
+
logs.append(f"✅ Initialized Gemini model: {model_name}")
|
| 116 |
yield "\n".join(logs), None # Update logs, no file yet
|
| 117 |
|
| 118 |
+
# The rest of the function remains the same as the corrected version from before
|
| 119 |
wb = load_workbook(input_file.name)
|
| 120 |
original_sheet_names = [name for name in wb.sheetnames if OUTPUT_SUFFIX not in name]
|
| 121 |
logs.append(f"✅ Loaded Excel file. Found {len(original_sheet_names)} sheet(s) to translate.")
|
|
|
|
| 126 |
# 3. Step 1: Translate and create new sheets
|
| 127 |
for sheet_name in progress.tqdm(original_sheet_names, desc="Translating Sheets"):
|
| 128 |
source_sheet = wb[sheet_name]
|
|
|
|
| 129 |
new_sheet_name_raw = f"{sheet_name}{OUTPUT_SUFFIX}"
|
| 130 |
new_sheet_name = safe_sheet_title(new_sheet_name_raw)
|
|
|
|
| 131 |
sheet_name_map[sheet_name] = new_sheet_name
|
| 132 |
|
| 133 |
if new_sheet_name in wb.sheetnames:
|
|
|
|
| 149 |
cells_to_translate.append({'text': val, 'row': cell.row, 'col': cell.column})
|
| 150 |
|
| 151 |
total_cells = len(cells_to_translate)
|
|
|
|
| 152 |
logs.append(f"--- Translating Sheet: {sheet_name} ({total_cells} cells) -> {new_sheet_name} ---")
|
| 153 |
yield "\n".join(logs), None
|
| 154 |
|
|
|
|
| 183 |
target_cell.value = translated
|
| 184 |
|
| 185 |
processed_count = min(idx + len(chunk), total_cells)
|
|
|
|
| 186 |
logs[-1] = f"--- Translating Sheet: {sheet_name} ({processed_count}/{total_cells} cells) -> {new_sheet_name} ---"
|
| 187 |
yield "\n".join(logs), None
|
| 188 |
|
| 189 |
idx += BATCH_SIZE
|
| 190 |
time.sleep(SLEEP_BETWEEN_BATCHES)
|
| 191 |
|
|
|
|
| 192 |
logs.append(f"✅ Finished sheet: {sheet_name}")
|
| 193 |
yield "\n".join(logs), None
|
| 194 |
|
|
|
|
| 212 |
yield "\n".join(logs), output_file_path
|
| 213 |
|
| 214 |
except Exception as e:
|
|
|
|
| 215 |
raise gr.Error(f"An error occurred: {e}")
|
| 216 |
|
| 217 |
# --- GRADIO INTERFACE ---
|
|
|
|
| 220 |
gr.Markdown(
|
| 221 |
"""
|
| 222 |
# 批量Excel翻译工具 (ZH-VI) | Excel Translator (ZH-VI)
|
| 223 |
+
使用Google Gemini进行中越翻译。
|
| 224 |
|
| 225 |
**使用说明 (Instructions):**
|
| 226 |
1. 在下方输入您的 Google Gemini API Key。(API Key is kept private and not stored).
|
| 227 |
+
2. 选择您想使用的 Gemini 模型。(`gemini-1.5-flash` is recommended for speed and cost).
|
| 228 |
+
3. 上传您的 `.xlsx` 格式的Excel文件。
|
| 229 |
+
4. 点击 "开始翻译 (Translate)" 按钮。
|
| 230 |
+
5. 等待处理完成,处理日志会实时显示。
|
| 231 |
+
6. 完成后,在右侧下载翻译好的文件。
|
| 232 |
"""
|
| 233 |
+
) # <-- MODIFIED: Added instruction for model selector
|
| 234 |
with gr.Row():
|
| 235 |
with gr.Column(scale=1):
|
| 236 |
api_key_input = gr.Textbox(
|
|
|
|
| 238 |
type="password",
|
| 239 |
placeholder="Enter your API key here..."
|
| 240 |
)
|
| 241 |
+
# <-- ADDED: Dropdown for model selection
|
| 242 |
+
model_selector = gr.Dropdown(
|
| 243 |
+
label="选择Gemini模型 (Select Gemini Model)",
|
| 244 |
+
choices=GEMINI_MODELS,
|
| 245 |
+
value=GEMINI_MODELS[0] # Set default to flash
|
| 246 |
+
)
|
| 247 |
file_input = gr.File(
|
| 248 |
label="Upload Excel File (.xlsx)",
|
| 249 |
file_types=[".xlsx"]
|
|
|
|
| 259 |
)
|
| 260 |
file_output = gr.File(label="下载翻译文件 (Download Translated File)")
|
| 261 |
|
| 262 |
+
# <-- MODIFIED: Add 'model_selector' to the inputs list
|
| 263 |
translate_button.click(
|
| 264 |
fn=process_translation,
|
| 265 |
+
inputs=[api_key_input, model_selector, file_input],
|
| 266 |
outputs=[log_output, file_output]
|
| 267 |
)
|
| 268 |
|