Code: Select all
;- TOP
; AZJIO 2026.02.02
EnableExplicit
; DebugLevel 1 ; разрешить сообщения отладчика, а чтобы можно было отключить и не мешать GUI-обработчику.
;- ● Enumeration
Enumeration
#RE_Overview
#RE_Description
#RE_Parameter
#RE_OptionalParameter
#RE_Remarks
#RE_Code
#RE_Translate
EndEnumeration
#Source = 0
#FileCache = 1
Structure TranslateData
Original.s
Translate.s
Length.i
EndStructure
XIncludeFile "ForHelpTranslator.pb"
Global NewList TranslateList.TranslateData()
;- ● Define
Define NewList Files.s()
Define Dim aTrslText.s(0)
Define Path$
Define StartTime
Define Text$
Define Preview$
Define CurPath$, CurFileName$
Define PathDone$, PathCache$, CacheFileFound, FileDone$, FileCache$
Define Length, bytes, *Buffer
Define CacheText$
Define i
Define IsTranslateGUI = 0
Define log$
; Может пусть Define будут, а в функции сделать Shared
Define hwnd, hwndBtn, hwndRTF1, hwndRTF2
;- ● ini
Define ini$
Define SourceLang$ = "en"
Define TranslateLang$ = "ru"
Define Only$
ini$ = GetPathPart(ProgramFilename()) + "HelpTranslator.ini"
If OpenPreferences(ini$)
Path$ = ReadPreferenceString("Path", "")
PathCache$ = ReadPreferenceString("PathCache", GetPathPart(Path$) + "Cache" + #PS$)
PathDone$ = ReadPreferenceString("PathDone", GetPathPart(Path$) + "Done" + #PS$)
SourceLang$ = ReadPreferenceString("SourceLang", SourceLang$)
TranslateLang$ = ReadPreferenceString("TranslateLang", TranslateLang$)
Only$ = ReadPreferenceString("Only", "")
IsTranslateGUI = ReadPreferenceInteger("IsTranslateGUI", 0)
ClosePreferences()
EndIf
; Если путь не существует, то делать нечего, завершаем работу программы
If FileSize(Path$) <> -2
Path$ = PathRequester("Open", GetCurrentDirectory())
If FileSize(Path$) <> -2
End
EndIf
EndIf
If FileSize(PathCache$) <> -2
PathCache$ = GetPathPart(Path$ + "Cache" + #PS$)
EndIf
If FileSize(PathDone$) <> -2
PathDone$ = GetPathPart(Path$ + "Done" + #PS$)
EndIf
If Right(Path$, 1) <> #PS$
Path$ + #PS$
EndIf
If Right(PathCache$, 1) <> #PS$
PathCache$ + #PS$
EndIf
If Right(PathDone$, 1) <> #PS$
PathDone$ + #PS$
EndIf
If Len(TranslateLang$) <> 2
TranslateLang$ = Left(GetOSLanguage(), 2)
If MessageRequester("Use?", "Do you want to use the translation language '" + TranslateLang$ + "'", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
End
EndIf
If Len(TranslateLang$) <> 2
MessageRequester("Program shutdown", "The translation language is not specified, for example ru")
End
EndIf
EndIf
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
IsTranslateGUI = 0 ; где бы флаг не был назначен для линукса сброс в 0.
; надо функции TranslateGUI() заключить в запрет для линукса
CompilerEndIf
;- ● Declare
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Declare.s TranslateGUI(Text$)
If IsTranslateGUI
hwnd = WinGetHandle(0, "QTranslate", 1, #Title, 1) ;
Debug "hwnd: " + Hex(hwnd), 1
If hwnd
hwndBtn = WinGetHandle(hwnd, "Button", 9, #Class, 0) ; Кнопка "Перевести"
Debug "hwndBtn: " + Hex(hwndBtn), 1
hwndRTF1 = WinGetHandle(hwnd, "RICHEDIT50W", 1, #Class, 0) ; окно исходного текста
Debug "hwndRTF1: " + Hex(hwndRTF1), 1
hwndRTF2 = WinGetHandle(hwnd, "RICHEDIT50W", 2, #Class, 0) ; окно переведённого текста
Debug "hwndRTF2: " + Hex(hwndRTF2), 1
EndIf
If hwnd = 0 Or hwndBtn = 0 Or hwndRTF1 = 0 Or hwndRTF2 = 0
MessageRequester("Program shutdown", "Window elements were not found:" + #LF$ + #LF$ + Hex(hwnd) + " " + Hex(hwndBtn) + " " + Hex(hwndRTF1) + " " + Hex(hwndRTF2))
End
EndIf
SendMessage_(hwndRTF2, #WM_SETTEXT, 0, @"") ; очистить окно
SetForegroundWindow(hwnd)
EndIf
CompilerEndIf
StartTime = ElapsedMilliseconds()
;- ● RegularExpression
CreateRegularExpression(#RE_Overview, "^@Overview\s++\K(.+?)(?=^@(CommandList|ExampleFile|SupportedOS))", #PB_RegularExpression_DotAll | #PB_RegularExpression_MultiLine)
CreateRegularExpression(#RE_Description, "^@Description\s++\K(.+?)(?=^@(Parameter|OptionalParameter|NoReturnValue|ReturnValue|Remarks|Example|SeeAlso|SupportedOS))", #PB_RegularExpression_DotAll | #PB_RegularExpression_MultiLine)
CreateRegularExpression(#RE_Parameter, "^@Parameter[^\n]*?\n\s+\K(.+?)(?=^@(Parameter|OptionalParameter|NoReturnValue|ReturnValue|Remarks|Example|SeeAlso|SupportedOS))", #PB_RegularExpression_DotAll | #PB_RegularExpression_MultiLine)
CreateRegularExpression(#RE_OptionalParameter, "^@OptionalParameter[^\n]*?\n\s+\K(.+?)(?=^@(OptionalParameter|NoReturnValue|ReturnValue|Remarks|Example|SeeAlso|SupportedOS))", #PB_RegularExpression_DotAll | #PB_RegularExpression_MultiLine)
CreateRegularExpression(#RE_Remarks, "^@Remarks\s++\K(.+?)(?=^@(Example|SeeAlso|SupportedOS))", #PB_RegularExpression_DotAll | #PB_RegularExpression_MultiLine)
CreateRegularExpression(#RE_Code, "^@Code\s++\K(.+?)(?=^@(EndCode|SeeAlso|SupportedOS))", #PB_RegularExpression_DotAll | #PB_RegularExpression_MultiLine)
CreateRegularExpression(#RE_Translate, "(.+?)(?:\n\}—————●—————\{\n)(.+?)(?:\n\}==================================●==================================\{\n)", #PB_RegularExpression_DotAll)
If Not (ForceDirectories(Path$ + "cache") And ForceDirectories(Path$ + "done"))
MessageRequester("Program shutdown", "The cache/done folder could not be created")
End
EndIf
FileSearch(Files(), Path$, "txt", 0)
;-┌─For─┐
ForEach Files()
CacheFileFound = 1
; временно переводим только HID.txt
If Asc(Only$) And Files() <> Path$ + Only$
Continue
EndIf
; CurPath$ = GetPathPart(Files())
; CurFileName$ = GetFilePart(Files(), #PB_FileSystem_NoExtension)
CurFileName$ = GetFilePart(Files())
FileCache$ = PathCache$ + CurFileName$
FileDone$ = PathDone$ + CurFileName$
If FileSize(FileCache$) = -1
CacheFileFound = 0
If CreateFile(#FileCache, FileCache$)
CacheFileFound = -1
CloseFile(#FileCache)
EndIf
EndIf
If ReadFile(#Source, Files()) And CacheFileFound ; #PB_UTF8
; While Eof(#Source) = 0
; ; читаем строку
; ReadString(#Source)
; Wend
Length = Lof(#Source)
*Buffer = AllocateMemory(Length + 2)
If *Buffer
bytes = ReadData(#Source, *Buffer, Length)
If bytes
Text$ = PeekS(*Buffer, bytes, #PB_UTF8)
EndIf
FreeMemory(*Buffer)
EndIf
CloseFile(#Source)
ClearList(TranslateList())
If CacheFileFound = 1 ; если существует кеш, то вместо того чтобы переводить файл выполняем взятие из кеша
Debug "Cache", 1
If ReadFile(#FileCache, FileCache$)
Length = Lof(#FileCache)
*Buffer = AllocateMemory(Length + 2)
If *Buffer
bytes = ReadData(#FileCache, *Buffer, Length)
If bytes
CacheText$ = PeekS(*Buffer, bytes, #PB_UTF8)
EndIf
FreeMemory(*Buffer)
EndIf
CloseFile(#FileCache)
EndIf
If Asc(CacheText$)
; MessageRequester("Error", CacheText$)
If ExamineRegularExpression(#RE_Translate, CacheText$)
While NextRegularExpressionMatch(#RE_Translate)
If AddElement(TranslateList())
TranslateList()\Original = RegularExpressionGroup(#RE_Translate, 1)
TranslateList()\Translate = RegularExpressionGroup(#RE_Translate, 2)
EndIf
Wend
EndIf
EndIf
Else
Debug "Translate", 1
;- ● Examine
If ExamineRegularExpression(#RE_Overview, Text$)
While NextRegularExpressionMatch(#RE_Overview)
If AddElement(TranslateList())
TranslateList()\Original = RegularExpressionMatchString(#RE_Overview)
EndIf
Wend
EndIf
If ExamineRegularExpression(#RE_Description, Text$)
While NextRegularExpressionMatch(#RE_Description)
If AddElement(TranslateList())
TranslateList()\Original = RegularExpressionMatchString(#RE_Description)
EndIf
Wend
EndIf
If ExamineRegularExpression(#RE_Parameter, Text$)
While NextRegularExpressionMatch(#RE_Parameter)
If AddElement(TranslateList())
TranslateList()\Original = RegularExpressionMatchString(#RE_Parameter)
EndIf
Wend
EndIf
If ExamineRegularExpression(#RE_OptionalParameter, Text$)
While NextRegularExpressionMatch(#RE_OptionalParameter)
If AddElement(TranslateList())
TranslateList()\Original = RegularExpressionMatchString(#RE_OptionalParameter)
EndIf
Wend
EndIf
If ExamineRegularExpression(#RE_Remarks, Text$)
While NextRegularExpressionMatch(#RE_Remarks)
If AddElement(TranslateList())
TranslateList()\Original = RegularExpressionMatchString(#RE_Remarks)
EndIf
Wend
EndIf
If ExamineRegularExpression(#RE_Code, Text$)
While NextRegularExpressionMatch(#RE_Code)
; разобрать код на комментарии
GetCommentLines(RegularExpressionMatchString(#RE_Code), aTrslText())
If Not (ArraySize(aTrslText()) = 0 And Asc(aTrslText(0)) = 0) ; если код с комментариями, то
For i = 0 To ArraySize(aTrslText())
If aTrslText(i) <> ";"
If AddElement(TranslateList())
TranslateList()\Original = aTrslText(i)
EndIf
EndIf
Next
EndIf
Wend
EndIf
EndIf
; Выполняем перевод строк
Preview$ = ""
ForEach TranslateList()
TranslateList()\Original = RTrimChar(TranslateList()\Original, #CRLF$ + #TAB$ + " ")
TranslateList()\Original = LTrimChar(TranslateList()\Original, #CRLF$ + #TAB$ + " ")
TranslateList()\Length = Len(TranslateList()\Original)
; TranslateList()\Translate = TranslateList()\Original ; для теста чтобы не дёргать переводчик, проверям все ли строки захватывает
If CacheFileFound < 1 ; выполняем перевод если не было файла или создан пустой
If IsTranslateGUI
TranslateList()\Translate = TranslateGUI(TranslateList()\Original)
Else
TranslateList()\Translate = TranslateText(TranslateList()\Original, SourceLang$, TranslateLang$)
EndIf
EndIf
TranslateList()\Translate = RTrimChar(TranslateList()\Translate, #CRLF$ + #TAB$ + " ")
TranslateList()\Translate = LTrimChar(TranslateList()\Translate, #CRLF$ + #TAB$ + " ")
If Asc(TranslateList()\Translate) = 0
; Debug "Пустая строка", 1
Debug "Empty line: " + TranslateList()\Original, 1
log$ + "Empty line: " + TranslateList()\Original + #LF$
DeleteElement(TranslateList())
Continue ; если пустая строка, то замену не производить
EndIf
Preview$ + TranslateList()\Original + #LF$ + "}—————●—————{" + #LF$ + TranslateList()\Translate + #LF$ + "}==================================●=================================={" + #LF$
Next
Debug "ListSize: " + Str(ListSize(TranslateList())), 1
Unique(TranslateList())
Debug "ListSize: " + Str(ListSize(TranslateList())), 1
;- ● Sort
; Сортируем, чтобы длинные строки были в начале и не были частью других длинных строк
SortStructuredList(TranslateList(), #PB_Sort_Descending, OffsetOf(TranslateData\Length), TypeOf(TranslateData\Length))
ForEach TranslateList()
Text$ = ReplaceString(Text$, TranslateList()\Original, TranslateList()\Translate)
; Debug "Original|" + TranslateList()\Original + "|", 2
; Debug "Translate|" + TranslateList()\Translate + "|", 2
Next
; Если кеша не существовало, то записываем в файл
If CacheFileFound < 1 And OpenFile(#FileCache, FileCache$)
If Not WriteString(#FileCache, Preview$)
MessageRequester("Error", "The file could not be written to the folder 'cache': " + CurFileName$)
EndIf
CloseFile(#FileCache)
EndIf
If OpenFile(#Source, FileDone$)
; MessageRequester("Error", Text$)
If Not WriteString(#Source, Text$)
MessageRequester("Error", "The file could not be written to the 'done' folder: " + CurFileName$)
EndIf
CloseFile(#Source)
EndIf
SetClipboardText(Preview$)
EndIf
Next
Debug "Time = " + FormatNumber((ElapsedMilliseconds() - StartTime)/1000, 2, ".", "") + " s", 1
If Asc(log$)
MessageRequester("", log$)
EndIf
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Procedure.s TranslateGUI(Text$)
Shared hwnd, hwndBtn, hwndRTF1, hwndRTF2
Shared log$
Protected length
Protected trnslt$
Protected count, *m
SendMessage_(hwndRTF1, #WM_SETTEXT, 0, @Text$) ; вставить текст для перевода
Delay(100)
SendMessage_(hwndBtn, #BM_CLICK, 0, 0) ; нажимает кнопку "Перевести"
Delay(200)
Repeat
length = SendMessage_(hwndRTF2, #WM_GETTEXTLENGTH, 0, 0)
If count > 100
Break
EndIf
If length = 0
count + 1
Delay(200)
Continue
EndIf
; trnslt$ = Space(length + 2)
*m = AllocateMemory(length*2 + 2)
If *m
SendMessage_(hwndRTF2, #WM_GETTEXT, length, *m) ; Считываем текст с окна перевода
trnslt$ = PeekS(*m)
If trnslt$ = "The language cannot be determined. Choose the language yourself." ; а вот тут эта фраза может быть на другом языке
log$ + "The language cannot be determined. Choose the language yourself." + Text$ + #LF$
trnslt$ = ""
EndIf
SendMessage_(hwndRTF2, #WM_SETTEXT, 0, @"") ; очистить окно
FreeMemory(*m)
Break
EndIf
ForEver
ProcedureReturn trnslt$
EndProcedure
CompilerEndIf