#SingleInstance,Force
#NoEnv
SetBatchLines,-1
;###############################################################################################################################
; Path to dump of untranslated lines (by setting update -> file in config.json)
sKeyDumpRawPath := "D:\D\5\AHKVNBrowser\RE\r55217\Setup\Dictionaries\Dump_2.5.2.json"
; Omit keys in dump that only contain ascii (no reason to translate ascii). Boolean. 1 = 'true', 0 = 'false'
; Keys in the dump's "graphic" section are never omitted, even though they're all ascii.
bOmitAsciiKeys := 1
; json files containing translations for various keys in order of prefered usage (sDictionary1 has highest priority)
; if a key has no translation in the dictionary 1, it will check dictionary 2, then 3.
sDictionary1Path := "D:\D\5\AHKVNBrowser\RE\r55217\Setup\Dictionaries\Dictionary_MINE.json"
sDictionary2Path := "D:\D\5\AHKVNBrowser\RE\r55217\Setup\Dictionaries\Dictionary_2.5.2_Variant B.json"
sDictionary3Path := "D:\D\5\AHKVNBrowser\RE\r55217\Setup\Dictionaries\Dictionary_2.5.2_Variant A.json"
; File path to write the new merged dictionary to. Should usually point to the game's ".\data\others\translate\assets\RJ162718.json" file
; This is ordered just like the original dump, so the line order of the game's ks files is conserved
; (important to understand the meaning of multi-line sentences while translating)
sOutputFile := "D:\D\5\AHKVNBrowser\RE\r55217\Setup\[patch] TransFix_v5.2_r5.3.1m\Translate Strings\data\others\translate\assets\RJ162718.json"
; Create a couple of other json files in the script's folder for debug and translation comparison purposes. Boolean.
; Useful to compare different translations with tools like WinMerge, since the output is all in the exact same order (the order of the original dump)
bCreateDebug := 1
;###############################################################################################################################
Dictionary1_obj := ReadDictionary(sDictionary1Path)
Dictionary2_obj := ReadDictionary(sDictionary2Path)
Dictionary3_obj := ReadDictionary(sDictionary3Path)
sOutputString := ProcessKeyDump(sKeyDumpRawPath,Dictionary1_obj,Dictionary2_obj,Dictionary3_obj,bOmitAsciiKeys)
FileDelete,%sOutputFile%
FileAppend,%sOutputString%,*%sOutputFile%,UTF-8-RAW
if (bCreateDebug=1)
{
if (sDictionary1Path != "") && (IsObject(Dictionary1_obj))
{
sOutputString := ProcessKeyDump(sKeyDumpRawPath,Dictionary1_obj,"","",bOmitAsciiKeys)
SplitPath,sDictionary1Path,,,FExt,FNameNoExt
FileDelete,%A_ScriptDir%\[Debug] %FNameNoExt%.%FExt%
FileAppend,%sOutputString%,*%A_ScriptDir%\[Debug] %FNameNoExt%.%FExt%,UTF-8-RAW
}
if (sDictionary2Path != "") && (IsObject(Dictionary2_obj))
{
sOutputString := ProcessKeyDump(sKeyDumpRawPath,Dictionary2_obj,"","",bOmitAsciiKeys)
SplitPath,sDictionary2Path,,,FExt,FNameNoExt
FileDelete,%A_ScriptDir%\[Debug] %FNameNoExt%.%FExt%
FileAppend,%sOutputString%,*%A_ScriptDir%\[Debug] %FNameNoExt%.%FExt%,UTF-8-RAW
}
if (sDictionary3Path != "") && (IsObject(Dictionary3_obj))
{
sOutputString := ProcessKeyDump(sKeyDumpRawPath,Dictionary3_obj,"","",bOmitAsciiKeys)
SplitPath,sDictionary3Path,,,FExt,FNameNoExt
FileDelete,%A_ScriptDir%\[Debug] %FNameNoExt%.%FExt%
FileAppend,%sOutputString%,*%A_ScriptDir%\[Debug] %FNameNoExt%.%FExt%,UTF-8-RAW
}
}
ExitApp
;##################################################################################
;##################################################################################
;##################################################################################
ReadDictionary(sFilePath)
{
FileRead,sJSON1_old,*P65001 %sFilePath%
iLevel:=0
sSection:=""
iCount:=0
obj_return := {}
loop,parse,sJSON1_old,`n,`r
{
sText:=Trim(A_LoopField)
if (sText="{")
{
iLevel++
iFoundKey:=0
iFoundLang:=0
continue
}
else if (sText="},")||(sText="}")
{
if (iLevel=3)
{
if !IsObject(obj_return[sSection])
{
clipboard:=A_Index
msgbox,,Error A,Section %sSection% not yet part of return object.
}
PairObj:={}
obj_return[sSection][sEntryJP] := sEntryEN
}
iLevel--
continue
}
if (iLevel=1)
{
if RegExMatch(sText,"^""([a-z]*)"": \[$",sKey)
{
iLevel++
if (sKey1!="text")&&(sKey1!="chara")&&(sKey1!="hint")&&(sKey1!="graphic")&&(sKey1!="eval")&&(sKey1!="branch")
{
clipboard:=A_Index
msgbox,,Error B,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
sSection:=sKey1
if !IsObject(obj_return[sSection])
obj_return[sSection] := {}
continue
}
else
{
clipboard:=A_Index
msgbox,,Error C,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
else if (iLevel=2)
{
if (sText="],")||(sText="]")
{
iLevel--
continue
}
else if (sText="")
{
}
else
{
clipboard:=A_Index
msgbox,,Error D,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
else if (iLevel=3)
{
pos1:=instr(sText,"""key"":",0,1)
pos2:=instr(sText,"""en-US"":",0,1)
if (pos1=1)
{
if (iFoundKey=1)
{
clipboard:=A_Index
msgbox,,Error E,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
else
{
iFoundKey=1
sVal:=substr(sText,7)
sVal:=Trim(sVal," ")
sFirst:=substr(sVal,1,1)
sLast:=substr(sVal,strlen(sVal),1)
sPreLast:=substr(sVal,strlen(sVal)-1,1)
if (iFoundLang=1) ; language entry "en-US" was first -> this "key" entry is second -> no comma
{
if (sFirst!="""")||(sLast!="""")
{
clipboard:=A_Index
msgbox,,Error F,Bad formating in %sText%
}
else
{
sEntryJP:=substr(sVal,2,strlen(sVal)-2)
}
}
else ; language entry "en-US" still missing -> this "key" goes first -> has comma
{
if (sFirst!="""")||(sPreLast!="""")||(sLast!=",")
{
clipboard:=A_Index
msgbox,,Error G,Bad formating in %sText%
}
else
{
sEntryJP:=substr(sVal,2,strlen(sVal)-3)
}
}
}
}
else if (pos2=1)
{
if (iFoundLang=1)
{
clipboard:=A_Index
msgbox,,Error H,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
else
{
iFoundLang=1
sVal:=substr(sText,9)
sVal:=Trim(sVal," ")
sFirst:=substr(sVal,1,1)
sLast:=substr(sVal,strlen(sVal),1)
sPreLast:=substr(sVal,strlen(sVal)-1,1)
if (iFoundKey=0) ; "key" entry still missing -> this language entry goes first -> has comma
{
if (sVal="null,")
sEntryEN:="null"
else
{
if (sFirst!="""")||(sPreLast!="""")||(sLast!=",")
{
clipboard:=A_Index
msgbox,,Error I,Bad formating in %sText%
}
else
{
sEntryEN:=substr(sVal,2,strlen(sVal)-3)
}
}
}
else ; "key" entry already exists -> this language entry is second -> no comma
{
if (sVal="null")
sEntryEN:=sVal
else
{
if (sFirst!="""")||(sLast!="""")
{
clipboard:=A_Index
msgbox,,Error J,Bad formating in %sText%
}
else
{
sEntryEN:=substr(sVal,2,strlen(sVal)-2)
}
}
}
}
}
else
{
clipboard:=A_Index
msgbox,,Error K,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
else
{
clipboard:=A_Index
msgbox,,Error L,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
return obj_return
}
ProcessKeyDump(sFilePath,dictobj1,dictobj2,dictobj3,bOmitAsciiKeys)
{
if !FileExist(sFilePath)
{
msgbox,,Error,ProcessKeyDump() can't find file:`n%sFilePath%
ExitApp
}
FileRead,sJSON1_old,*P65001 %sFilePath%
iLevel:=0
sSection:=""
iCount:=0
obj_return := {}
sReturn := ""
loop,parse,sJSON1_old,`n,`r
{
sText:=Trim(A_LoopField)
if (sText="{")
{
iLevel++
iFoundKey:=0
iFoundLang:=0
if (iLevel!=3)
sReturn .= A_LoopField "`n"
continue
}
else if (sText="},")||(sText="}")
{
if (iLevel=3)
{
if !IsObject(obj_return[sSection])
{
clipboard:=A_Index
msgbox,,Error A,Section %sSection% not yet part of return object.
}
obj_return[sSection][sEntryJP] := sEntryEN
if (sSection="graphic")
{
; Since this sections contains only ascii image pathes for buttons and ascii translated Tooltip texts, we need to get it this way
bNonAscii=1
}
else
{
bNonAscii=0
if (bOmitAsciiKeys=1)
{
loop,parse,sEntryJP
{
k_unicode:=asc(A_LoopField)
if (k_unicode>256) ;(k_unicode>=0x2000)
{
bNonAscii=1
break
}
}
}
}
if (bNonAscii=1)||(bOmitAsciiKeys=0)
{
sReturn .= " {`n ""key"": """ sEntryJP """,`n"
;-----------------------------------------------------
; This is where the translations are actually applied
if IsObject(dictobj1)
{
sEntryEN1 := dictobj1[sSection][sEntryJP]
if (sEntryEN1!="") && (sEntryEN1!="null")
sEntryEN := sEntryEN1
}
if IsObject(dictobj2)
{
sEntryEN2 := dictobj2[sSection][sEntryJP]
if ((sEntryEN="") || (sEntryEN="null")) && (sEntryEN2!="") && (sEntryEN2!="null")
sEntryEN := sEntryEN2
}
if IsObject(dictobj3)
{
sEntryEN3 := dictobj3[sSection][sEntryJP]
if ((sEntryEN="") || (sEntryEN="null")) && (sEntryEN3!="")&&(sEntryEN3!="null")
sEntryEN := sEntryEN3
}
;-----------------------------------------------------
; We could do some formating with string replace at this point if necessary.
sEntryEN := StrReplace(sEntryEN, " [p]", "[p]")
sEntryEN := StrReplace(sEntryEN, " [r]", "[r]")
sEntryEN := StrReplace(sEntryEN, " [l]", "[l]")
sEntryEN := StrReplace(sEntryEN, " [lr]", "[lr]")
sEntryEN := StrReplace(sEntryEN," [lr_]","[lr_]")
;-----------------------------------------------------
if (sEntryEN="null")
sReturn .= " ""en-US"": null`n " sText "`n"
else
sReturn .= " ""en-US"": """ sEntryEN """`n " sText "`n"
}
}
else
sReturn .= A_LoopField "`n"
iLevel--
continue
}
if (iLevel=1)
{
if RegExMatch(sText,"^""([a-z]*)"": \[$",sKey)
{
iLevel++
if (sKey1!="text")&&(sKey1!="chara")&&(sKey1!="hint")&&(sKey1!="graphic")&&(sKey1!="eval")&&(sKey1!="branch")
{
clipboard:=A_Index
msgbox,,Error B,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
sSection:=sKey1
if !IsObject(obj_return[sSection])
obj_return[sSection] := {}
sReturn .= A_LoopField "`n"
continue
}
else
{
clipboard:=A_Index
msgbox,,Error C,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
else if (iLevel=2)
{
if (sText="],")||(sText="]")
{
iLevel--
sReturn .= A_LoopField "`n"
continue
}
else if (sText="")
{
}
else
{
clipboard:=A_Index
msgbox,,Error D,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n_%sText%_
}
}
else if (iLevel=3)
{
pos1:=instr(sText,"""key"":",0,1)
pos2:=instr(sText,"""en-US"":",0,1)
if (pos1=1)
{
if (iFoundKey=1)
{
clipboard:=A_Index
msgbox,,Error E,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
else
{
iFoundKey=1
sVal:=substr(sText,7)
sVal:=Trim(sVal," ")
sFirst:=substr(sVal,1,1)
sLast:=substr(sVal,strlen(sVal),1)
sPreLast:=substr(sVal,strlen(sVal)-1,1)
if (iFoundLang=1) ; language entry "en-US" was first -> this "key" entry is second -> no comma
{
if (sFirst!="""")||(sLast!="""")
{
clipboard:=A_Index
msgbox,,Error F,Bad formating in %sText%
}
else
{
sEntryJP:=substr(sVal,2,strlen(sVal)-2)
}
}
else ; language entry "en-US" still missing -> this "key" goes first -> has comma
{
if (sFirst!="""")||(sPreLast!="""")||(sLast!=",")
{
clipboard:=A_Index
msgbox,,Error G,Bad formating in %sText%
}
else
{
sEntryJP:=substr(sVal,2,strlen(sVal)-3)
}
}
}
}
else if (pos2=1)
{
if (iFoundLang=1)
{
clipboard:=A_Index
msgbox,,Error H,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
else
{
iFoundLang=1
sVal:=substr(sText,9)
sVal:=Trim(sVal," ")
sFirst:=substr(sVal,1,1)
sLast:=substr(sVal,strlen(sVal),1)
sPreLast:=substr(sVal,strlen(sVal)-1,1)
if (iFoundKey=0) ; "key" entry still missing -> this language entry goes first -> has comma
{
if (sVal="null,")
sEntryEN:="null"
else
{
if (sFirst!="""")||(sPreLast!="""")||(sLast!=",")
{
clipboard:=A_Index
msgbox,,Error I,Bad formating in %sText%
}
else
{
sEntryEN:=substr(sVal,2,strlen(sVal)-3)
}
}
}
else ; "key" entry already exists -> this language entry is second -> no comma
{
if (sVal="null")
sEntryEN:=sVal
else
{
if (sFirst!="""")||(sLast!="""")
{
clipboard:=A_Index
msgbox,,Error J,Bad formating in %sText%
}
else
{
sEntryEN:=substr(sVal,2,strlen(sVal)-2)
}
}
}
}
}
else
{
clipboard:=A_Index
msgbox,,Error K,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
else
{
clipboard:=A_Index
msgbox,,Error L,Level %iLevel% / Key %sSection% / Line %A_Index%`n`n"%sText%"
}
}
sReturn:=RTrim(sReturn,"`r`n")
; Remove unnecessary comma when retransitioning from CJK-> Ascii keys before the end of a section
sReturn := StrReplace(sReturn,"`n },`n ],`n","`n }`n ],`n")
return sReturn
}