mintlee commited on
Commit
ff93898
·
1 Parent(s): b14389a

add no mongodb

Browse files
db/__pycache__/mongodb.cpython-310.pyc CHANGED
Binary files a/db/__pycache__/mongodb.cpython-310.pyc and b/db/__pycache__/mongodb.cpython-310.pyc differ
 
excel/__pycache__/excel_translate.cpython-310.pyc CHANGED
Binary files a/excel/__pycache__/excel_translate.cpython-310.pyc and b/excel/__pycache__/excel_translate.cpython-310.pyc differ
 
excel/__pycache__/xlsx.cpython-310.pyc CHANGED
Binary files a/excel/__pycache__/xlsx.cpython-310.pyc and b/excel/__pycache__/xlsx.cpython-310.pyc differ
 
excel/excel_translate.py CHANGED
@@ -1,171 +1,48 @@
1
- import openpyxl
2
- from typing import Dict, List
3
  from translate.translator import translate_text_dict
4
  import math
5
  import chardet
6
- import io
7
- import pandas as pd
8
- import pymongo
9
- import gridfs
10
- import tempfile
11
- import os
12
-
13
-
14
-
15
- def translate_xlsx(file_id: str, target_lang: str = ""):
16
- # Kết nối MongoDB
17
- client = pymongo.MongoClient("mongodb+srv://admin:[email protected]/?retryWrites=true&w=majority&appName=Cluster0")
18
- db = client["excel"]
19
- fs_input = gridfs.GridFS(db, collection="root_file")
20
- fs_output = gridfs.GridFS(db, collection="final_file")
21
-
22
- # Tải file từ MongoDB
23
- file_data = fs_input.get(file_id)
24
-
25
-
26
- # Lưu file tạm thời
27
- with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as temp_file:
28
- temp_file.write(file_data.read())
29
- temp_file_path = temp_file.name
30
-
31
- # Đọc file Excel bằng openpyxl
32
- wb = openpyxl.load_workbook(temp_file_path)
33
-
34
-
35
- sheets = wb.worksheets # Chọn tất cả sheets nếu sheet_name không hợp lệ
36
 
37
- for ws in sheets:
38
- max_row = ws.max_row
39
- max_col = ws.max_column
40
-
41
- # Tạo dictionary lưu trữ nội dung cần dịch và mapping từ key đến cell
42
- text_dict: Dict[str, List[str]] = {}
43
- cell_map: Dict[str, any] = {} # lưu mapping key -> cell object
44
-
45
- for row in range(1, max_row + 1):
46
- for col in range(1, max_col + 1):
47
- cell = ws.cell(row=row, column=col)
48
- if isinstance(cell.value, str):
49
- key = f"R{row}C{col}" # key theo dạng R{row}C{col}
50
- text_dict[key] = [cell.value] # Lưu giá trị dưới dạng danh sách với 1 phần tử
51
- cell_map[key] = cell
52
-
53
- # Gọi hàm dịch theo dạng bulk
54
- translated_dict = translate_text_dict(text_dict, target_lang=target_lang)
55
-
56
- # Cập nhật lại các cell với nội dung đã dịch
57
- for key, cell in cell_map.items():
58
- if key in translated_dict:
59
- translated_text_list = translated_dict[key]
60
- if translated_text_list and len(translated_text_list) > 0:
61
- cell.value = translated_text_list[0]
62
-
63
- # Lưu workbook vào file tạm thời
64
- with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as output_file:
65
- wb.save(output_file.name)
66
- output_file.seek(0)
67
- translated_file_id = fs_output.put(output_file.read(), filename=file_data.filename)
68
-
69
- # Đóng workbook và xóa file tạm
70
- wb.close()
71
- os.remove(temp_file_path)
72
-
73
- print(f"✅ Dịch thành công! File đã lưu vào MongoDB với file_id: {translated_file_id}")
74
- return translated_file_id
75
-
76
-
77
- def read_csv_with_auto_encoding(csv_path):
78
- # Đọc file dưới dạng nhị phân
79
- with open(csv_path, "rb") as f:
80
- raw_data = f.read()
81
- # Dò tìm encoding
82
- detect_result = chardet.detect(raw_data)
83
- encoding = detect_result["encoding"]
84
- confidence = detect_result["confidence"]
85
-
86
- print(f"Chardet dự đoán file '{csv_path}' có encoding = {encoding} (độ tin cậy = {confidence})")
87
-
88
- # Nếu chardet không phát hiện được, ta đặt fallback = 'utf-8'
89
- if encoding is None:
90
- encoding = "utf-8"
91
-
92
  decoded_data = raw_data.decode(encoding, errors='replace')
93
-
94
- # Sử dụng io.StringIO để chuyển đổi chuỗi thành đối tượng file-like
95
  csv_data = io.StringIO(decoded_data)
96
- df = pd.read_csv(csv_data)
97
- return df
98
 
99
-
100
- def translate_csv(file_id, source_lang, target_lang="vi", chunk_size=50):
101
- # Kết nối MongoDB
102
- client = pymongo.MongoClient("mongodb+srv://admin:[email protected]/?retryWrites=true&w=majority&appName=Cluster0")
103
- db = client["csv"]
104
- fs_input = gridfs.GridFS(db, collection="root_file")
105
- fs_output = gridfs.GridFS(db, collection="final_file")
106
-
107
- # Tải file từ MongoDB
108
- file_data = fs_input.get(file_id).read()
109
-
110
- # Lưu file tạm thời
111
- with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as temp_file:
112
- temp_file.write(file_data)
113
- temp_file_path = temp_file.name
114
-
115
- df = read_csv_with_auto_encoding(temp_file_path)
116
-
117
- # If text_columns is not specified, we assume we want to translate everything that looks like text.
118
- # Otherwise, only translate the given columns.
119
-
120
  text_columns = df.select_dtypes(include=["object"]).columns.tolist()
121
 
122
  num_rows = len(df)
123
  num_chunks = math.ceil(num_rows / chunk_size)
124
-
125
- translated_df = df.copy() # copy to store the final translations
126
 
127
  for chunk_index in range(num_chunks):
128
  start_idx = chunk_index * chunk_size
129
  end_idx = min((chunk_index + 1) * chunk_size, num_rows)
130
  chunk_df = df.iloc[start_idx:end_idx]
131
 
132
- # Build a dictionary structure. For example, row-based:
133
- # {
134
- # "0": {"colA": "some text", "colB": "some text"},
135
- # "1": {"colA": "some text", "colB": "some text"},
136
- # ...
137
- # }
138
  chunk_dict = {}
139
  for i, row in chunk_df.iterrows():
140
- row_dict = {}
141
- for col in text_columns:
142
- row_dict[col] = str(row[col]) if pd.notnull(row[col]) else ""
143
  chunk_dict[str(i)] = row_dict
144
 
145
- # Now call your LLM translator on this dictionary
146
  translated_chunk = translate_text_dict(
147
  text_dict=chunk_dict,
148
  source_lang=source_lang,
149
  target_lang=target_lang
150
  )
151
 
152
- # 'translated_chunk' should be the same structure, so let's re-inject into the DataFrame
153
  for i_str, row_data in translated_chunk.items():
154
  i = int(i_str)
155
  for col, translated_val in row_data.items():
156
  translated_df.at[i, col] = translated_val
157
 
158
- # Lưu file dịch vào tệp tạm thời
159
- translated_file_path = temp_file_path.replace(".csv", f"_translated_{target_lang}.csv")
160
- translated_df.to_csv(translated_file_path, index=False, encoding='utf-8-sig')
161
-
162
- # Đọc lại file tạm để lưu vào MongoDB
163
- with open(translated_file_path, "rb") as f:
164
- translated_file_id = fs_output.put(f, filename=f"translated_{file_id}.csv")
165
-
166
- # Xóa file tạm
167
- os.remove(temp_file_path)
168
- os.remove(translated_file_path)
169
-
170
- print(f"Translation complete! Saved to MongoDB with file_id: {translated_file_id}")
171
- return translated_file_id
 
1
+ import io
2
+ import pandas as pd
3
  from translate.translator import translate_text_dict
4
  import math
5
  import chardet
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ def read_csv_with_auto_encoding_from_bytes(csv_bytes) -> pd.DataFrame:
8
+ raw_data = csv_bytes.read()
9
+ detect_result = chardet.detect(raw_data)
10
+ encoding = detect_result["encoding"] or "utf-8"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  decoded_data = raw_data.decode(encoding, errors='replace')
 
 
12
  csv_data = io.StringIO(decoded_data)
13
+ return pd.read_csv(csv_data)
 
14
 
15
+ def translate_csv(file_bytes, file_name, source_lang: str, target_lang: str = "vi", chunk_size: int = 50) -> bytes:
16
+ df = read_csv_with_auto_encoding_from_bytes(file_bytes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  text_columns = df.select_dtypes(include=["object"]).columns.tolist()
18
 
19
  num_rows = len(df)
20
  num_chunks = math.ceil(num_rows / chunk_size)
21
+ translated_df = df.copy()
 
22
 
23
  for chunk_index in range(num_chunks):
24
  start_idx = chunk_index * chunk_size
25
  end_idx = min((chunk_index + 1) * chunk_size, num_rows)
26
  chunk_df = df.iloc[start_idx:end_idx]
27
 
 
 
 
 
 
 
28
  chunk_dict = {}
29
  for i, row in chunk_df.iterrows():
30
+ row_dict = {col: str(row[col]) if pd.notnull(row[col]) else "" for col in text_columns}
 
 
31
  chunk_dict[str(i)] = row_dict
32
 
 
33
  translated_chunk = translate_text_dict(
34
  text_dict=chunk_dict,
35
  source_lang=source_lang,
36
  target_lang=target_lang
37
  )
38
 
39
+
40
  for i_str, row_data in translated_chunk.items():
41
  i = int(i_str)
42
  for col, translated_val in row_data.items():
43
  translated_df.at[i, col] = translated_val
44
 
45
+ output_buffer = io.BytesIO()
46
+ translated_df.to_csv(output_buffer, index=False, encoding='utf-8-sig')
47
+ output_buffer.seek(0)
48
+ return output_buffer, file_name
 
 
 
 
 
 
 
 
 
 
excel/xlsx.py CHANGED
@@ -406,9 +406,12 @@ def translate_sheet_names_via_regex(
406
  traceback.print_exc()
407
 
408
 
409
- def zip_folder_to_excel_file(folder_path, file_name):
 
 
 
 
410
  try:
411
- # Nén thư mục thành file .xlsx trong RAM
412
  xlsx_buffer = io.BytesIO()
413
  with zipfile.ZipFile(xlsx_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
414
  for root, _, files in os.walk(folder_path):
@@ -418,19 +421,11 @@ def zip_folder_to_excel_file(folder_path, file_name):
418
  zipf.write(file_path, archive_path)
419
 
420
  xlsx_buffer.seek(0)
421
-
422
- client = MongoClient("mongodb+srv://admin:[email protected]/?retryWrites=true&w=majority&appName=Cluster0")
423
- db = client['excel']
424
- fs = gridfs.GridFS(db, collection='final_file')
425
-
426
- file_id = fs.put(xlsx_buffer.read(), filename=file_name)
427
- print(f"✅ Đã lưu file Excel vào MongoDB với ID: {file_id}")
428
- return file_id
429
 
430
  except Exception as e:
431
- print(f"❌ Lỗi khi nén lưu Excel vào MongoDB: {e}")
432
- return None
433
-
434
 
435
  def get_text_list_from_nodes(modifiable_nodes: Optional[List[Dict[str, Any]]]) -> List[str]:
436
  if modifiable_nodes is None:
@@ -482,16 +477,10 @@ def _translate_batch_helper(segments_to_translate, original_indices_1based, sour
482
  return batch_results
483
 
484
 
485
- def translate_xlsx(file_id, file_name, source_lang='en', target_lang='vi', batch_size_segments=50, max_words_per_segment=100, delay_between_requests=1):
486
-
487
- client = MongoClient("mongodb+srv://admin:[email protected]/?retryWrites=true&w=majority&appName=Cluster0")
488
- db = client['excel']
489
- fs = gridfs.GridFS(db, collection='root_file')
490
 
491
- ppt_file = fs.get(file_id)
492
- excel_file = BytesIO(ppt_file.read())
493
-
494
- xml_folder = unzip_office_file(excel_file)
495
  path_to_workbook_xml = os.path.join(xml_folder, "xl", "workbook.xml")
496
  translate_sheet_names_via_regex(path_to_workbook_xml, source_lang, target_lang)
497
 
@@ -600,9 +589,9 @@ def translate_xlsx(file_id, file_name, source_lang='en', target_lang='vi', batch
600
  print("LỖI NGHIÊM TRỌNG: Không thể lưu thay đổi vào file XML.")
601
  else:
602
  # Only zip if saving XML was successful
603
- final_id = zip_folder_to_excel_file(xml_folder, file_name)
604
- if final_id:
605
  shutil.rmtree(xml_folder) # Mark folder as 'handled' by zipping
606
  else:
607
  print("LỖI NGHIÊM TRỌNG: Không thể tạo file XLSX đã dịch cuối cùng.")
608
- return final_id
 
406
  traceback.print_exc()
407
 
408
 
409
+ def zip_folder_to_excel_bytes(folder_path):
410
+ """
411
+ Nén toàn bộ thư mục thành file Excel (.xlsx) dưới dạng BytesIO (trong RAM).
412
+ Trả lại buffer BytesIO chứa nội dung file.
413
+ """
414
  try:
 
415
  xlsx_buffer = io.BytesIO()
416
  with zipfile.ZipFile(xlsx_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
417
  for root, _, files in os.walk(folder_path):
 
421
  zipf.write(file_path, archive_path)
422
 
423
  xlsx_buffer.seek(0)
424
+ return xlsx_buffer
 
 
 
 
 
 
 
425
 
426
  except Exception as e:
427
+ print(f"❌ Lỗi khi nén thư mục thành file Excel: {e}")
428
+ return None
 
429
 
430
  def get_text_list_from_nodes(modifiable_nodes: Optional[List[Dict[str, Any]]]) -> List[str]:
431
  if modifiable_nodes is None:
 
477
  return batch_results
478
 
479
 
480
+ def translate_xlsx(file_io, file_name, source_lang='en', target_lang='vi', batch_size_segments=50, max_words_per_segment=100, delay_between_requests=1):
 
 
 
 
481
 
482
+ file_io.seek(0)
483
+ xml_folder = unzip_office_file(file_io)
 
 
484
  path_to_workbook_xml = os.path.join(xml_folder, "xl", "workbook.xml")
485
  translate_sheet_names_via_regex(path_to_workbook_xml, source_lang, target_lang)
486
 
 
589
  print("LỖI NGHIÊM TRỌNG: Không thể lưu thay đổi vào file XML.")
590
  else:
591
  # Only zip if saving XML was successful
592
+ translated_buffer = zip_folder_to_excel_bytes(xml_folder)
593
+ if translated_buffer:
594
  shutil.rmtree(xml_folder) # Mark folder as 'handled' by zipping
595
  else:
596
  print("LỖI NGHIÊM TRỌNG: Không thể tạo file XLSX đã dịch cuối cùng.")
597
+ return translated_buffer, file_name
pages/upload.py CHANGED
@@ -9,10 +9,9 @@ import dotenv
9
  import os
10
 
11
  dotenv.load_dotenv(".env")
12
-
13
  genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
14
 
15
- # Cấu hình trang
16
  st.set_page_config(page_title="Translate Your File", page_icon="🌍", layout="centered")
17
 
18
  # CSS custom
@@ -55,7 +54,6 @@ st.markdown("""
55
  color: black !important;
56
  border-radius: 8px;
57
  }
58
- /* Thu hẹp khoảng cách giữa label và selectbox */
59
  .stSelectbox label {
60
  margin-bottom: 0.2rem;
61
  font-weight: bold;
@@ -65,11 +63,12 @@ st.markdown("""
65
  </style>
66
  """, unsafe_allow_html=True)
67
 
68
- # Upload file section
69
  with st.container():
70
  st.markdown("### 📂 Chọn file để dịch")
71
  uploaded_file = st.file_uploader("Kéo thả hoặc chọn file", type=['pptx', 'xlsx', 'csv', 'docx'])
72
 
 
73
  with st.container():
74
  col1, col2 = st.columns(2)
75
 
@@ -81,39 +80,36 @@ with st.container():
81
  st.markdown('<p style="font-size:16px; font-weight:bold; margin-bottom:4px;">🌐 Ngôn ngữ muốn dịch sang</p>', unsafe_allow_html=True)
82
  target_lang = st.selectbox(" ", ["chinese", "english", "vietnamese"], key="target_lang")
83
 
 
84
  def process_file(file, file_type):
85
  progress_bar = st.progress(0)
86
- with st.spinner("🔄 Đang lưu file lên hệ thống..."):
87
- file_id, file_name = save_file_to_mongodb(uploaded_file=file, db_name=file_type.lower(), collection_name="root_file")
88
- progress_bar.progress(20)
89
- st.write(f"📂 **File ID:** `{file_id}`")
90
 
91
  with st.spinner("🔍 Đang xử lý và dịch tài liệu..."):
92
  if file_type == "PPTX":
93
- final_id = translate_pptx(file_id, file_name, source_lang=source_lang, target_lang=target_lang, slides_per_batch=5)
94
- progress_bar.progress(60)
95
  elif file_type == "Excel":
96
- final_id = translate_xlsx(file_id=file_id, file_name=file_name, source_lang=source_lang, target_lang=target_lang)
97
  elif file_type == "CSV":
98
- final_id = translate_csv(file_id=file_id, source_lang=source_lang, target_lang=target_lang)
99
  elif file_type == "Word":
100
- final_id = translate_docx(file_id=file_id, file_name=file_name, source_lang=source_lang, target_lang=target_lang)
101
  else:
102
  st.error("❌ Loại file không hỗ trợ!")
103
  return
104
 
105
- progress_bar.progress(80)
106
-
107
- with st.spinner("📦 Đang tải file đã dịch..."):
108
- file_io, file_name = fetch_file_from_mongodb(file_type.lower(), "final_file", final_id)
109
- progress_bar.progress(100)
110
 
111
- if file_io:
112
  st.success("🎉 File đã được dịch thành công!")
113
- st.download_button("⬇️ Tải file về", data=file_io.getvalue(), file_name=file_name)
 
114
  else:
115
- st.error("❌ Không thể tải xuống file. Vui lòng thử lại!")
 
116
 
 
117
  if uploaded_file and st.button("🚀 Upload và dịch ngay!"):
118
  with st.spinner("🔎 Đang phát hiện loại file..."):
119
  file_type = detect_file_type(uploaded_file)
 
9
  import os
10
 
11
  dotenv.load_dotenv(".env")
 
12
  genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
13
 
14
+ # Cấu hình giao diện
15
  st.set_page_config(page_title="Translate Your File", page_icon="🌍", layout="centered")
16
 
17
  # CSS custom
 
54
  color: black !important;
55
  border-radius: 8px;
56
  }
 
57
  .stSelectbox label {
58
  margin-bottom: 0.2rem;
59
  font-weight: bold;
 
63
  </style>
64
  """, unsafe_allow_html=True)
65
 
66
+ # Upload file
67
  with st.container():
68
  st.markdown("### 📂 Chọn file để dịch")
69
  uploaded_file = st.file_uploader("Kéo thả hoặc chọn file", type=['pptx', 'xlsx', 'csv', 'docx'])
70
 
71
+ # Lựa chọn ngôn ngữ
72
  with st.container():
73
  col1, col2 = st.columns(2)
74
 
 
80
  st.markdown('<p style="font-size:16px; font-weight:bold; margin-bottom:4px;">🌐 Ngôn ngữ muốn dịch sang</p>', unsafe_allow_html=True)
81
  target_lang = st.selectbox(" ", ["chinese", "english", "vietnamese"], key="target_lang")
82
 
83
+ # Xử lý file trực tiếp
84
  def process_file(file, file_type):
85
  progress_bar = st.progress(0)
86
+ file_name = file.name
87
+ progress_bar.progress(10)
 
 
88
 
89
  with st.spinner("🔍 Đang xử lý và dịch tài liệu..."):
90
  if file_type == "PPTX":
91
+ output_io, output_name = translate_pptx(file, file_name, source_lang=source_lang, target_lang=target_lang)
 
92
  elif file_type == "Excel":
93
+ output_io, output_name = translate_xlsx(file, file_name, source_lang=source_lang, target_lang=target_lang)
94
  elif file_type == "CSV":
95
+ output_io, output_name = translate_csv(file, file_name, source_lang=source_lang, target_lang=target_lang)
96
  elif file_type == "Word":
97
+ output_io, output_name = translate_docx(file, file_name, source_lang=source_lang, target_lang=target_lang)
98
  else:
99
  st.error("❌ Loại file không hỗ trợ!")
100
  return
101
 
102
+ progress_bar.progress(100)
 
 
 
 
103
 
104
+ if output_io:
105
  st.success("🎉 File đã được dịch thành công!")
106
+ print(f" File đã dịch: {output_name}")
107
+ st.download_button("⬇️ Tải file về", data=output_io.getvalue(), file_name=output_name)
108
  else:
109
+ st.error("❌ Xảy ra lỗi khi xử lý file.")
110
+
111
 
112
+ # Nút xử lý
113
  if uploaded_file and st.button("🚀 Upload và dịch ngay!"):
114
  with st.spinner("🔎 Đang phát hiện loại file..."):
115
  file_type = detect_file_type(uploaded_file)
powerpoint/__pycache__/pptx.cpython-310.pyc CHANGED
Binary files a/powerpoint/__pycache__/pptx.cpython-310.pyc and b/powerpoint/__pycache__/pptx.cpython-310.pyc differ
 
powerpoint/pptx.py CHANGED
@@ -3,15 +3,13 @@ import zipfile
3
  import shutil
4
  from utils.utils import unzip_office_file, translate_text, preprocess_text, postprocess_text
5
  from powerpoint.xml_handling import *
6
- from pymongo import MongoClient
7
- import gridfs
8
  from io import BytesIO
9
 
10
- def create_pptx_and_store_in_mongodb(temp_dir, pptx_filename):
11
  """
12
- Tạo file PPTX từ thư mục chứa nội dung đã giải nén và lưu vào MongoDB mà không lưu file trên ổ cứng.
13
  """
14
- pptx_buffer = BytesIO()
15
 
16
  with zipfile.ZipFile(pptx_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
17
  for root_dir, _, files in os.walk(temp_dir):
@@ -20,45 +18,32 @@ def create_pptx_and_store_in_mongodb(temp_dir, pptx_filename):
20
  arcname = os.path.relpath(file_path, temp_dir)
21
  zipf.write(file_path, arcname)
22
 
23
- pptx_buffer.seek(0)
 
24
 
25
- client = MongoClient("mongodb+srv://admin:[email protected]/?retryWrites=true&w=majority&appName=Cluster0")
26
- db = client['pptx']
27
- fs = gridfs.GridFS(db, collection='final_file')
28
-
29
- file_id = fs.put(pptx_buffer, filename=pptx_filename)
30
-
31
- print(f"PPTX đã được lưu vào MongoDB với ID: {file_id}")
32
- client.close()
33
-
34
- return file_id
35
-
36
- def translate_and_replace_pptx(xml_folder, file_name, source_lang='vn', target_lang='en', slides_per_batch=5):
37
  slides_dir = os.path.join(xml_folder, "ppt/slides")
38
  all_slides = sorted([f for f in os.listdir(slides_dir)
39
  if f.startswith("slide") and f.endswith(".xml")],
40
  key=lambda x: int(x[5:-4]))
41
 
42
- # Xử lý theo từng batch slide
43
  for i in range(0, len(all_slides), slides_per_batch):
44
  batch_slides = all_slides[i:i + slides_per_batch]
45
- slide_text_mapping = {}
46
- smartart_text_mapping = {}
47
 
48
  for slide_file in batch_slides:
49
  slide_index = int(slide_file[5:-4])
50
  slide_path = os.path.join(slides_dir, slide_file)
51
- slide_text_mapping[slide_index] = extract_text_from_slide(slide_path) # Lấy list các tuple (text, rPr)
52
 
53
- # Xử lý SmartArt qua file .rels của slide
54
  rels_file = os.path.join(xml_folder, "ppt/slides/_rels", slide_file + ".rels")
55
  base_path = os.path.join(xml_folder, "ppt")
56
  smartart_data_path = get_smartart_data_file(rels_file, base_path)
57
  if smartart_data_path:
58
- smartart_text_mapping[slide_index] = extract_text_from_smartart(smartart_data_path) # Lấy list các tuple (text, rPr)
59
 
60
-
61
- # Gộp text để dịch theo batch, giữ lại rPr
62
  combined_slide_text_list = []
63
  for slide_index in sorted(slide_text_mapping.keys()):
64
  combined_slide_text_list.extend(slide_text_mapping[slide_index])
@@ -66,48 +51,33 @@ def translate_and_replace_pptx(xml_folder, file_name, source_lang='vn', target_l
66
  combined_smartart_text_list = []
67
  for slide_index in sorted(smartart_text_mapping.keys()):
68
  combined_smartart_text_list.extend(smartart_text_mapping[slide_index])
69
-
70
- # Tách text ra khỏi tuple để dịch
71
  slide_texts_to_translate = [text for text, _ in combined_slide_text_list]
72
  smartart_texts_to_translate = [text for text, _ in combined_smartart_text_list]
73
 
74
- # Dịch văn bản slide và SmartArt
75
- combined_slide_text_string = preprocess_text(slide_texts_to_translate)
76
- combined_smartart_text_string = preprocess_text(smartart_texts_to_translate)
77
-
78
- translated_slide_string = translate_text(combined_slide_text_string, source_lang, target_lang)
79
- translated_smartart_string = translate_text(combined_smartart_text_string, source_lang, target_lang)
80
 
81
- # Postprocess để có list các văn bản đã dịch
82
  translated_slide_texts = postprocess_text(translated_slide_string)
83
  translated_smartart_texts = postprocess_text(translated_smartart_string)
84
 
85
- # **Quan trọng:** Tạo danh sách tuple (translated_text, rPr)
86
  translated_slide_data = []
87
  for i, (original_text, rPr) in enumerate(combined_slide_text_list):
88
- if i < len(translated_slide_texts):
89
- translated_slide_data.append((translated_slide_texts[i], rPr))
90
- else:
91
- translated_slide_data.append(("", rPr)) # Trường hợp không đủ translated text
92
-
93
  translated_smartart_data = []
94
  for i, (original_text, rPr) in enumerate(combined_smartart_text_list):
95
- if i < len(translated_smartart_texts):
96
- translated_smartart_data.append((translated_smartart_texts[i], rPr))
97
- else:
98
- translated_smartart_data.append(("", rPr)) # Trường hợp không đủ translated text
99
 
100
- # Thay thế văn bản trong slide
101
- slide_index = 0
102
  for slide_index in sorted(slide_text_mapping.keys()):
103
  slide_file = f"slide{slide_index}.xml"
104
  slide_path = os.path.join(slides_dir, slide_file)
105
  num_texts = len(slide_text_mapping[slide_index])
106
  replace_data = translated_slide_data[:num_texts]
107
- replace_text_in_slide(slide_path, replace_data) # truyền vào danh sách (translated_text, rPr)
108
- translated_slide_data = translated_slide_data[num_texts:] # Cập nhật danh sách cho slide tiếp theo
109
 
110
- # Thay thế văn bản trong SmartArt
111
  for slide_index in sorted(smartart_text_mapping.keys()):
112
  rels_file = os.path.join(xml_folder, "ppt/slides/_rels", f"slide{slide_index}.xml.rels")
113
  base_path = os.path.join(xml_folder, "ppt")
@@ -115,23 +85,19 @@ def translate_and_replace_pptx(xml_folder, file_name, source_lang='vn', target_l
115
  if smartart_data_path:
116
  num_texts = len(smartart_text_mapping[slide_index])
117
  replace_data = translated_smartart_data[:num_texts]
118
- replace_text_in_smartart(smartart_data_path, replace_data, None) # truyền vào danh sách (translated_text, rPr)
119
- translated_smartart_data = translated_smartart_data[num_texts:] # Cập nhật danh sách cho slide tiếp theo
120
-
121
- file_id = create_pptx_and_store_in_mongodb(xml_folder, file_name)
122
- return file_id
123
-
124
- def translate_pptx(pptx_id, file_name, source_lang='vn', target_lang='en', slides_per_batch=5):
125
- client = MongoClient("mongodb+srv://admin:[email protected]/?retryWrites=true&w=majority&appName=Cluster0")
126
- db = client['pptx']
127
- fs = gridfs.GridFS(db, collection='root_file')
128
-
129
- ppt_file = fs.get(pptx_id)
130
- prs = BytesIO(ppt_file.read())
131
-
132
- xml_folder = unzip_office_file(prs)
133
- file_id = translate_and_replace_pptx(xml_folder, file_name, source_lang, target_lang, slides_per_batch=slides_per_batch)
134
- shutil.rmtree(xml_folder)
135
 
136
- return file_id
 
 
 
 
 
 
 
 
 
 
137
 
 
 
3
  import shutil
4
  from utils.utils import unzip_office_file, translate_text, preprocess_text, postprocess_text
5
  from powerpoint.xml_handling import *
 
 
6
  from io import BytesIO
7
 
8
+ def create_pptx_from_dir(temp_dir, pptx_filename):
9
  """
10
+ Tạo file PPTX từ thư mục chứa nội dung đã giải nén và trả về BytesIO object.
11
  """
12
+ pptx_buffer = BytesIO()
13
 
14
  with zipfile.ZipFile(pptx_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
15
  for root_dir, _, files in os.walk(temp_dir):
 
18
  arcname = os.path.relpath(file_path, temp_dir)
19
  zipf.write(file_path, arcname)
20
 
21
+ pptx_buffer.seek(0)
22
+ return pptx_buffer, pptx_filename
23
 
24
+ def translate_and_replace_pptx(xml_folder, source_lang='vn', target_lang='en', slides_per_batch=5):
 
 
 
 
 
 
 
 
 
 
 
25
  slides_dir = os.path.join(xml_folder, "ppt/slides")
26
  all_slides = sorted([f for f in os.listdir(slides_dir)
27
  if f.startswith("slide") and f.endswith(".xml")],
28
  key=lambda x: int(x[5:-4]))
29
 
 
30
  for i in range(0, len(all_slides), slides_per_batch):
31
  batch_slides = all_slides[i:i + slides_per_batch]
32
+ slide_text_mapping = {}
33
+ smartart_text_mapping = {}
34
 
35
  for slide_file in batch_slides:
36
  slide_index = int(slide_file[5:-4])
37
  slide_path = os.path.join(slides_dir, slide_file)
38
+ slide_text_mapping[slide_index] = extract_text_from_slide(slide_path)
39
 
 
40
  rels_file = os.path.join(xml_folder, "ppt/slides/_rels", slide_file + ".rels")
41
  base_path = os.path.join(xml_folder, "ppt")
42
  smartart_data_path = get_smartart_data_file(rels_file, base_path)
43
  if smartart_data_path:
44
+ smartart_text_mapping[slide_index] = extract_text_from_smartart(smartart_data_path)
45
 
46
+ # Gộp text
 
47
  combined_slide_text_list = []
48
  for slide_index in sorted(slide_text_mapping.keys()):
49
  combined_slide_text_list.extend(slide_text_mapping[slide_index])
 
51
  combined_smartart_text_list = []
52
  for slide_index in sorted(smartart_text_mapping.keys()):
53
  combined_smartart_text_list.extend(smartart_text_mapping[slide_index])
54
+
55
+ # Dịch
56
  slide_texts_to_translate = [text for text, _ in combined_slide_text_list]
57
  smartart_texts_to_translate = [text for text, _ in combined_smartart_text_list]
58
 
59
+ translated_slide_string = translate_text(preprocess_text(slide_texts_to_translate), source_lang, target_lang)
60
+ translated_smartart_string = translate_text(preprocess_text(smartart_texts_to_translate), source_lang, target_lang)
 
 
 
 
61
 
 
62
  translated_slide_texts = postprocess_text(translated_slide_string)
63
  translated_smartart_texts = postprocess_text(translated_smartart_string)
64
 
 
65
  translated_slide_data = []
66
  for i, (original_text, rPr) in enumerate(combined_slide_text_list):
67
+ translated_slide_data.append((translated_slide_texts[i] if i < len(translated_slide_texts) else "", rPr))
68
+
 
 
 
69
  translated_smartart_data = []
70
  for i, (original_text, rPr) in enumerate(combined_smartart_text_list):
71
+ translated_smartart_data.append((translated_smartart_texts[i] if i < len(translated_smartart_texts) else "", rPr))
 
 
 
72
 
 
 
73
  for slide_index in sorted(slide_text_mapping.keys()):
74
  slide_file = f"slide{slide_index}.xml"
75
  slide_path = os.path.join(slides_dir, slide_file)
76
  num_texts = len(slide_text_mapping[slide_index])
77
  replace_data = translated_slide_data[:num_texts]
78
+ replace_text_in_slide(slide_path, replace_data)
79
+ translated_slide_data = translated_slide_data[num_texts:]
80
 
 
81
  for slide_index in sorted(smartart_text_mapping.keys()):
82
  rels_file = os.path.join(xml_folder, "ppt/slides/_rels", f"slide{slide_index}.xml.rels")
83
  base_path = os.path.join(xml_folder, "ppt")
 
85
  if smartart_data_path:
86
  num_texts = len(smartart_text_mapping[slide_index])
87
  replace_data = translated_smartart_data[:num_texts]
88
+ replace_text_in_smartart(smartart_data_path, replace_data, None)
89
+ translated_smartart_data = translated_smartart_data[num_texts:]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ def translate_pptx(file_obj: BytesIO, file_name: str, source_lang='vn', target_lang='en', slides_per_batch=5):
92
+ """
93
+ Hàm chính: nhận file PPTX (BytesIO), dịch, và trả về BytesIO của file đã dịch.
94
+ """
95
+ file_obj.seek(0)
96
+ xml_folder = unzip_office_file(file_obj)
97
+
98
+ translate_and_replace_pptx(xml_folder, source_lang, target_lang, slides_per_batch)
99
+
100
+ translated_io, translated_filename = create_pptx_from_dir(xml_folder, file_name)
101
+ shutil.rmtree(xml_folder)
102
 
103
+ return translated_io, translated_filename
test.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 2,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
@@ -30,7 +30,7 @@
30
  },
31
  {
32
  "cell_type": "code",
33
- "execution_count": 5,
34
  "metadata": {},
35
  "outputs": [],
36
  "source": [
@@ -78,7 +78,7 @@
78
  },
79
  {
80
  "cell_type": "code",
81
- "execution_count": 2,
82
  "metadata": {},
83
  "outputs": [],
84
  "source": [
@@ -119,7 +119,7 @@
119
  },
120
  {
121
  "cell_type": "code",
122
- "execution_count": 20,
123
  "metadata": {},
124
  "outputs": [],
125
  "source": [
@@ -150,7 +150,7 @@
150
  },
151
  {
152
  "cell_type": "code",
153
- "execution_count": 21,
154
  "metadata": {},
155
  "outputs": [],
156
  "source": [
@@ -181,36 +181,36 @@
181
  },
182
  {
183
  "cell_type": "code",
184
- "execution_count": 24,
185
  "metadata": {},
186
  "outputs": [
187
  {
188
  "name": "stdout",
189
  "output_type": "stream",
190
  "text": [
191
- "✅ File đã được tải về: D:\\Show_me_everything\\Machine Translation\\input\\MXL1166配套表.docx\n"
192
  ]
193
  }
194
  ],
195
  "source": [
196
- "download_input_from_mongodb(file_id=\"684002b9047f70beae0bdf2e\", save_name=\"MXL1166配套表.docx\", db_name=\"word\", collection_name=\"root_file\")"
197
  ]
198
  },
199
  {
200
  "cell_type": "code",
201
- "execution_count": 25,
202
  "metadata": {},
203
  "outputs": [
204
  {
205
  "name": "stdout",
206
  "output_type": "stream",
207
  "text": [
208
- "✅ File đã được tải về: D:\\Show_me_everything\\Machine Translation\\output\\MXL1147配套表.docx\n"
209
  ]
210
  }
211
  ],
212
  "source": [
213
- "download_output_from_mongodb(file_id=\"68400205047f70beae0bdf2a\", save_name=\"MXL1147配套表.docx\", db_name=\"word\", collection_name=\"final_file\")"
214
  ]
215
  },
216
  {
@@ -244,7 +244,7 @@
244
  },
245
  {
246
  "cell_type": "code",
247
- "execution_count": 6,
248
  "metadata": {},
249
  "outputs": [
250
  {
@@ -252,12 +252,12 @@
252
  "output_type": "stream",
253
  "text": [
254
  "✅ Đã xóa 0 file trong collection 'root_file' của db 'word'\n",
255
- "✅ Đã xóa 4 file trong collection 'root_file' của db 'excel'\n",
256
- "✅ Đã xóa 0 file trong collection 'root_file' của db 'pptx'\n",
257
  "✅ Đã xóa 0 file trong collection 'root_file' của db 'csv'\n",
258
  "✅ Đã xóa 0 file trong collection 'final_file' của db 'word'\n",
259
- "✅ Đã xóa 4 file trong collection 'final_file' của db 'excel'\n",
260
- "✅ Đã xóa 0 file trong collection 'final_file' của db 'pptx'\n",
261
  "✅ Đã xóa 0 file trong collection 'final_file' của db 'csv'\n"
262
  ]
263
  }
@@ -270,16 +270,16 @@
270
  },
271
  {
272
  "cell_type": "code",
273
- "execution_count": 7,
274
  "metadata": {},
275
  "outputs": [
276
  {
277
  "data": {
278
  "text/plain": [
279
- "91.66238403320312"
280
  ]
281
  },
282
- "execution_count": 7,
283
  "metadata": {},
284
  "output_type": "execute_result"
285
  }
@@ -291,11 +291,71 @@
291
  "for db_name in ['word', 'exce', 'pptx', 'csv']:\n",
292
  " db = client[db_name]\n",
293
  " stats = db.command(\"dbstats\")\n",
294
- " db_size = stats.get(\"dataSize\", 0)\n",
295
  " total_size += db_size\n",
296
  "total_size / (1024** 2)"
297
  ]
298
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  {
300
  "cell_type": "code",
301
  "execution_count": 9,
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 1,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
 
30
  },
31
  {
32
  "cell_type": "code",
33
+ "execution_count": 2,
34
  "metadata": {},
35
  "outputs": [],
36
  "source": [
 
78
  },
79
  {
80
  "cell_type": "code",
81
+ "execution_count": 3,
82
  "metadata": {},
83
  "outputs": [],
84
  "source": [
 
119
  },
120
  {
121
  "cell_type": "code",
122
+ "execution_count": 13,
123
  "metadata": {},
124
  "outputs": [],
125
  "source": [
 
150
  },
151
  {
152
  "cell_type": "code",
153
+ "execution_count": 16,
154
  "metadata": {},
155
  "outputs": [],
156
  "source": [
 
181
  },
182
  {
183
  "cell_type": "code",
184
+ "execution_count": 20,
185
  "metadata": {},
186
  "outputs": [
187
  {
188
  "name": "stdout",
189
  "output_type": "stream",
190
  "text": [
191
+ "✅ File đã được tải về: D:\\Show_me_everything\\Machine Translation\\input\\szero-point-ieks-essays-1350537845-9781350537842_compress.docx\n"
192
  ]
193
  }
194
  ],
195
  "source": [
196
+ "download_input_from_mongodb(file_id=\"6843696876015abc15cc759f\", save_name=\"szero-point-ieks-essays-1350537845-9781350537842_compress.docx\", db_name=\"word\", collection_name=\"root_file\")"
197
  ]
198
  },
199
  {
200
  "cell_type": "code",
201
+ "execution_count": 17,
202
  "metadata": {},
203
  "outputs": [
204
  {
205
  "name": "stdout",
206
  "output_type": "stream",
207
  "text": [
208
+ "✅ File đã được tải về: D:\\Show_me_everything\\Machine Translation\\output\\samsung_presentation_vietnamese.pptx\n"
209
  ]
210
  }
211
  ],
212
  "source": [
213
+ "download_output_from_mongodb(file_id=\"684194c376015abc15cc7428\", save_name=\"samsung_presentation_vietnamese.pptx\", db_name=\"pptx\", collection_name=\"final_file\")"
214
  ]
215
  },
216
  {
 
244
  },
245
  {
246
  "cell_type": "code",
247
+ "execution_count": 4,
248
  "metadata": {},
249
  "outputs": [
250
  {
 
252
  "output_type": "stream",
253
  "text": [
254
  "✅ Đã xóa 0 file trong collection 'root_file' của db 'word'\n",
255
+ "✅ Đã xóa 0 file trong collection 'root_file' của db 'excel'\n",
256
+ "✅ Đã xóa 3 file trong collection 'root_file' của db 'pptx'\n",
257
  "✅ Đã xóa 0 file trong collection 'root_file' của db 'csv'\n",
258
  "✅ Đã xóa 0 file trong collection 'final_file' của db 'word'\n",
259
+ "✅ Đã xóa 0 file trong collection 'final_file' của db 'excel'\n",
260
+ "✅ Đã xóa 3 file trong collection 'final_file' của db 'pptx'\n",
261
  "✅ Đã xóa 0 file trong collection 'final_file' của db 'csv'\n"
262
  ]
263
  }
 
270
  },
271
  {
272
  "cell_type": "code",
273
+ "execution_count": 9,
274
  "metadata": {},
275
  "outputs": [
276
  {
277
  "data": {
278
  "text/plain": [
279
+ "0.0"
280
  ]
281
  },
282
+ "execution_count": 9,
283
  "metadata": {},
284
  "output_type": "execute_result"
285
  }
 
291
  "for db_name in ['word', 'exce', 'pptx', 'csv']:\n",
292
  " db = client[db_name]\n",
293
  " stats = db.command(\"dbstats\")\n",
294
+ " db_size = stats.get(\"StorageSize\", 0)\n",
295
  " total_size += db_size\n",
296
  "total_size / (1024** 2)"
297
  ]
298
  },
299
+ {
300
+ "cell_type": "code",
301
+ "execution_count": 16,
302
+ "metadata": {},
303
+ "outputs": [
304
+ {
305
+ "name": "stdout",
306
+ "output_type": "stream",
307
+ "text": [
308
+ "✅ Đã lấy 0 dữ liệu đo lường.\n",
309
+ "⚠️ Không có dữ liệu đo lường.\n"
310
+ ]
311
+ }
312
+ ],
313
+ "source": [
314
+ "import requests\n",
315
+ "from requests.auth import HTTPDigestAuth\n",
316
+ "import datetime\n",
317
+ "\n",
318
+ "# ==== Cấu hình ====\n",
319
+ "PUBLIC_KEY = 'uetgyqkj'\n",
320
+ "PRIVATE_KEY = '892caec5-8474-4043-862b-f4d4c617daa2'\n",
321
+ "GROUP_ID = '67db8bf4ed971c2114aad7f1#' # còn gọi là Project ID\n",
322
+ "CLUSTER_NAME = 'Cluster0'\n",
323
+ "\n",
324
+ "# ==== Lấy metric dung lượng dữ liệu ====\n",
325
+ "url = f\"https://cloud.mongodb.com/api/atlas/v1.0/groups/{GROUP_ID}/clusters/{CLUSTER_NAME}/measurements\"\n",
326
+ "\n",
327
+ "params = {\n",
328
+ " \"granularity\": \"PT1M\", # lấy theo từng phút\n",
329
+ " \"period\": \"PT1H\", # 5 phút gần nhất\n",
330
+ " \"m\": \"DATA_SIZE_TOTAL\", # metric cần lấy\n",
331
+ "}\n",
332
+ "\n",
333
+ "response = requests.get(\n",
334
+ " url,\n",
335
+ " auth=HTTPDigestAuth(PUBLIC_KEY, PRIVATE_KEY),\n",
336
+ " params=params\n",
337
+ ")\n",
338
+ "\n",
339
+ "# ==== Xử lý kết quả ====\n",
340
+ "if response.status_code == 200:\n",
341
+ " data = response.json()\n",
342
+ " measurements = data.get(\"measurements\", [])\n",
343
+ " print(f\"✅ Đã lấy {len(measurements)} dữ liệu đo lường.\")\n",
344
+ " if measurements:\n",
345
+ " datapoints = measurements[0].get(\"dataPoints\", [])\n",
346
+ " if datapoints:\n",
347
+ " latest_point = [d for d in datapoints if d['value'] is not None][-1]\n",
348
+ " value_bytes = latest_point['value']\n",
349
+ " ts = latest_point['timestamp']\n",
350
+ " print(f\"✅ Dung lượng hiện tại: {value_bytes / (1024**2):.2f} MB (timestamp: {ts})\")\n",
351
+ " else:\n",
352
+ " print(\"⚠️ Không có datapoint nào.\")\n",
353
+ " else:\n",
354
+ " print(\"⚠️ Không có dữ liệu đo lường.\")\n",
355
+ "else:\n",
356
+ " print(f\"❌ Lỗi {response.status_code}: {response.text}\")\n"
357
+ ]
358
+ },
359
  {
360
  "cell_type": "code",
361
  "execution_count": 9,
word/__pycache__/word_helper.cpython-310.pyc CHANGED
Binary files a/word/__pycache__/word_helper.cpython-310.pyc and b/word/__pycache__/word_helper.cpython-310.pyc differ
 
word/word_helper.py CHANGED
@@ -8,8 +8,6 @@ import re
8
  import time
9
  import dotenv
10
  import os
11
- from pymongo import MongoClient
12
- import gridfs
13
  from io import BytesIO
14
 
15
  dotenv.load_dotenv(".env")
@@ -253,18 +251,13 @@ def merge_elements(doc):
253
  current_run = [element]
254
  return doc
255
 
256
- def translate_docx(file_id, source_lang="English", target_lang="Vietnamese", file_name=''):
257
- """Translates a Word document and saves the output to MongoDB."""
258
- client = MongoClient(os.getenv("MONGODB_URI"))
259
- db = client["word"]
260
- fs_input = gridfs.GridFS(db, collection="root_file")
261
- fs_output = gridfs.GridFS(db, collection="final_file")
262
 
263
- # Lấy file gốc từ MongoDB
264
- input_file = fs_input.get(file_id)
265
- doc = Document(BytesIO(input_file.read()))
266
 
267
- # Dịch nội dung
268
  doc = merge_elements(doc)
269
 
270
  print('Translating paragraphs.')
@@ -286,13 +279,8 @@ def translate_docx(file_id, source_lang="English", target_lang="Vietnamese", fil
286
  translate_header_footer(doc, source_lang, target_lang)
287
  print('Done translating headers & footers.')
288
 
289
- # Lưu tài liệu đã dịch vào MongoDB
290
  output_stream = BytesIO()
291
  doc.save(output_stream)
292
  output_stream.seek(0)
293
 
294
- translated_file_id = fs_output.put(output_stream, filename=file_name)
295
- client.close()
296
-
297
- print(f"Translation complete! Saved to MongoDB with id: {translated_file_id}")
298
- return translated_file_id
 
8
  import time
9
  import dotenv
10
  import os
 
 
11
  from io import BytesIO
12
 
13
  dotenv.load_dotenv(".env")
 
251
  current_run = [element]
252
  return doc
253
 
254
+ def translate_docx(uploaded_file, file_name, source_lang="English", target_lang="Vietnamese"):
255
+ """
256
+ Translates a Word document passed as a Streamlit UploadedFile and returns a BytesIO object.
257
+ """
 
 
258
 
259
+ doc = Document(uploaded_file)
 
 
260
 
 
261
  doc = merge_elements(doc)
262
 
263
  print('Translating paragraphs.')
 
279
  translate_header_footer(doc, source_lang, target_lang)
280
  print('Done translating headers & footers.')
281
 
 
282
  output_stream = BytesIO()
283
  doc.save(output_stream)
284
  output_stream.seek(0)
285
 
286
+ return output_stream, file_name