@@ -187,7 +187,8 @@ def load_settings(self):
187187 "combine" : True ,
188188 "export_subtitles" : False ,
189189 "normalize" : False ,
190- "trim" : False
190+ "trim" : False ,
191+ "lexicon" : {}
191192 }
192193 if os .path .exists (CONFIG_FILE ):
193194 try :
@@ -677,6 +678,75 @@ def on_lang_ui_change(choice):
677678
678679 ctk .CTkLabel (thread_frame , text = "(More threads = High RAM usage)" , text_color = "orange" ).pack (side = "left" , padx = 10 )
679680
681+ def build_lexicon_tab (self , parent ):
682+ parent .grid_columnconfigure (0 , weight = 1 )
683+ parent .grid_rowconfigure (1 , weight = 1 ) # List area expands
684+
685+ # 1. Add New Entry
686+ add_frame = ctk .CTkFrame (parent )
687+ add_frame .grid (row = 0 , column = 0 , sticky = "ew" , padx = 10 , pady = 10 )
688+
689+ ctk .CTkLabel (add_frame , text = "Original Text:" ).pack (side = "left" , padx = 5 )
690+ self .lex_orig_var = ctk .StringVar ()
691+ ctk .CTkEntry (add_frame , textvariable = self .lex_orig_var , width = 150 ).pack (side = "left" , padx = 5 )
692+
693+ ctk .CTkLabel (add_frame , text = "Replacement:" ).pack (side = "left" , padx = 5 )
694+ self .lex_replace_var = ctk .StringVar ()
695+ ctk .CTkEntry (add_frame , textvariable = self .lex_replace_var , width = 150 ).pack (side = "left" , padx = 5 )
696+
697+ ctk .CTkButton (add_frame , text = "Add Rule" , command = self .add_lexicon_rule ).pack (side = "left" , padx = 10 )
698+
699+ # 2. List
700+ self .lex_list_frame = ctk .CTkScrollableFrame (parent )
701+ self .lex_list_frame .grid (row = 1 , column = 0 , sticky = "nsew" , padx = 10 , pady = 5 )
702+
703+ self .refresh_lexicon_list ()
704+
705+ # 3. Help Text
706+ ctk .CTkLabel (parent , text = "Note: Replacements are case-insensitive. Applied before generation." , text_color = "gray" ).grid (row = 2 , column = 0 , pady = 5 )
707+
708+ def add_lexicon_rule (self ):
709+ orig = self .lex_orig_var .get ().strip ()
710+ rep = self .lex_replace_var .get ().strip ()
711+
712+ if not orig :
713+ messagebox .showwarning ("Error" , "Original text cannot be empty." )
714+ return
715+
716+ if "lexicon" not in self .settings :
717+ self .settings ["lexicon" ] = {}
718+
719+ self .settings ["lexicon" ][orig ] = rep
720+ self .lex_orig_var .set ("" )
721+ self .lex_replace_var .set ("" )
722+ self .save_settings ()
723+ self .refresh_lexicon_list ()
724+
725+ def delete_lexicon_rule (self , key ):
726+ if key in self .settings .get ("lexicon" , {}):
727+ del self .settings ["lexicon" ][key ]
728+ self .save_settings ()
729+ self .refresh_lexicon_list ()
730+
731+ def refresh_lexicon_list (self ):
732+ for widget in self .lex_list_frame .winfo_children ():
733+ widget .destroy ()
734+
735+ lexicon = self .settings .get ("lexicon" , {})
736+ if not lexicon :
737+ ctk .CTkLabel (self .lex_list_frame , text = "No rules defined." , text_color = "gray" ).pack (pady = 10 )
738+ return
739+
740+ for i , (orig , rep ) in enumerate (lexicon .items ()):
741+ row = ctk .CTkFrame (self .lex_list_frame )
742+ row .pack (fill = "x" , pady = 2 )
743+
744+ ctk .CTkLabel (row , text = orig , width = 150 , anchor = "w" , font = ("Consolas" , 12 )).pack (side = "left" , padx = 10 )
745+ ctk .CTkLabel (row , text = "->" , width = 30 ).pack (side = "left" )
746+ ctk .CTkLabel (row , text = rep , width = 150 , anchor = "w" , font = ("Consolas" , 12 )).pack (side = "left" , padx = 10 )
747+
748+ ctk .CTkButton (row , text = "X" , width = 30 , fg_color = "#c42b1c" , command = lambda k = orig : self .delete_lexicon_rule (k )).pack (side = "right" , padx = 5 )
749+
680750 def create_widgets (self ):
681751 # Header
682752 self .grid_columnconfigure (0 , weight = 1 )
@@ -699,6 +769,9 @@ def create_widgets(self):
699769 mix_tab = self .main_tabs .add ("Custom Voice" )
700770 self .build_mixing_tab (mix_tab )
701771
772+ lex_tab = self .main_tabs .add ("Lexicon" )
773+ self .build_lexicon_tab (lex_tab )
774+
702775 # Actions (Global)
703776 action_frame = ctk .CTkFrame (self )
704777 action_frame .grid (row = 2 , column = 0 , sticky = "ew" , padx = 10 , pady = 10 )
@@ -864,7 +937,8 @@ def preview_conversion(self):
864937 'volume' : self .volume_var .get (),
865938 'pitch' : self .pitch_var .get (),
866939 'normalize' : self .normalize_audio .get (),
867- 'trim_silence' : self .trim_silence .get ()
940+ 'trim_silence' : self .trim_silence .get (),
941+ 'lexicon' : self .settings .get ('lexicon' , {})
868942 }
869943
870944 # Temp file
@@ -941,7 +1015,8 @@ def start_conversion(self):
9411015 'volume' : self .volume_var .get (),
9421016 'pitch' : self .pitch_var .get (),
9431017 'normalize' : self .normalize_audio .get (),
944- 'trim_silence' : self .trim_silence .get ()
1018+ 'trim_silence' : self .trim_silence .get (),
1019+ 'lexicon' : self .settings .get ('lexicon' , {})
9451020 }
9461021
9471022 # 3. Start
0 commit comments