728x90
반응형
- 본 내용은 과거 번역 파일 정리 자동화 프로그램 작업에 대한 업무 일지이다.
Properties 파일 구조
- properties 파일: <단어> = <번역된 단어> 형태로 관리됨
- 기능에 따라 폴더가 구분되어있음. 이 폴더명을 back/<폴더명> 명으로 사용
- 파일 위치 경로: /assets/backend/
- ko.properties, en.properties로 파일이 나뉘어져 있다.
- 두 파일은 같은 key를 공유하고 있기 때문에, 하나의 시트에 합쳐서 보여질 수 있게 한다.
- key, ko, en 으로 칼럼을 잡았다.
코드
- 번역 파일이 저장되어 있는 디렉토리 내의 파일들 조회
package read
// Import 내용들
// 디렉토리 내의 파일/폴더들 리스트 조회
func ReadDir(dir string) []os.DirEntry {
entries, err := os.ReadDir(dir)
if err != nil {
log.Fatalf("Failed to Read Dir Error: %v", err)
return nil
}
return entries
}
// 파일 읽기
func ReadFile(fileDir string) ([]fs.DirEntry, string) {
fileList, readErr := os.ReadDir(fileDir)
if readErr != nil {
log.Printf("Can't Read json file: %v", readErr)
return nil, fileDir
}
return fileList, fileDir
}
- Property 파일 내의 데이터 처리하여 저장할 수 있게 가공
package read
// Import 내용들
type PropertyStruct struct {
Code string
Ko string
En string
}
func ReadProperties(fileList []fs.DirEntry, pwd string) []PropertyStruct {
var propertyDataDataList []PropertyStruct
for _, file := range fileList {
fileName := file.Name()
fileNameWithoutFormat := strings.Split(fileName, ".properties")[0]
if fileNameWithoutFormat == "example" {
continue
}
fileNameLength := len(fileNameWithoutFormat)
langCode := fileNameWithoutFormat[fileNameLength-2 : fileNameLength]
if langCode == "en" || langCode == "ko" {
// 파일 열기
file, err := os.Open(pwd + "/" + fileName)
if err != nil {
log.Printf("[Properties] data Open Error: %v", err)
}
defer file.Close()
// 스캐너로 파일 한 줄씩 읽기
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// 빈 줄 또는 주석(#, ;) 무시
if strings.TrimSpace(line) == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
continue
}
// key=value 포맷으로 나누기
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if langCode == "ko" {
convertedString, convTerr := utils.DecodeUnicodeEscapes(value)
if convTerr != nil {
log.Printf("Convert To Utf8 Err: %v", convTerr)
}
value = convertedString
}
isFound := false
for i := range propertyDataDataList {
data := &propertyDataDataList[i]
if data.Code == key {
isFound = true
switch langCode {
case "en":
data.En = value
case "ko":
data.Ko = value
}
break // 값을 찾았으므로 루프를 종
}
}
if !isFound {
propertyData := PropertyStruct{Code: key} // 새 구조체 생성
switch langCode {
case "en":
propertyData.En = value
case "ko":
propertyData.Ko = value
}
propertyDataDataList = append(propertyDataDataList, propertyData)
}
}
}
if err := scanner.Err(); err != nil {
log.Printf("Scan Error: %v", err)
}
}
}
return propertyDataDataList
}
- 처리한 데이터들을 받아 액셀파일 생성
package write
// Import 내용들
type FileWork struct {
file *excelize.File
FileName string
SheetLength int
}
// 칼럼 세팅
func (excelFile *FileWork)HandlePropertiesColumns () {
excelFile.file.SetCellValue(sheetName, "A1", "Code")
excelFile.file.SetCellValue(sheetName, "B1", "Ko")
excelFile.file.SetCellValue(sheetName, "C1", "En")
}
// 데이터 레코드 입력
func (excelFile *FileWork)HandlePropertiesRows(dataList []read.PropertyStruct) {
for i := 0; i <= len(dataList)-1; i += 1 {
excelFile.file.SetCellValue(sheetName, fmt.Sprintf("A%d", i+2), dataList[i].Code)
excelFile.file.SetCellValue(sheetName, fmt.Sprintf("B%d", i+2), dataList[i].Ko)
excelFile.file.SetCellValue(sheetName, fmt.Sprintf("C%d", i+2), dataList[i].En)
}
}
// 액셀 파일 저장
func (file *FileWork) SaveExcelFile() {
savingErr := file.file.SaveAs(file.FileName)
if savingErr != nil {
log.Printf("Saving File Error: %v", savingErr)
} else {
log.Printf("Saving Success: %s", file.FileName)
}
}
func OpenSaveExcelWithProperties(sheetName string, index int, excelFileName string, pwd string, dataList []read.PropertyStruct) {
file := HandleFiles(sheetName, index, excelFileName, pwd, dataList)
sheetIndex := file.HandleSheet(sheetName)
log.Printf("Handled Sheet Index: %d", sheetIndex)
file.file.HandlePropertiesColumns()
file.HandlePropertiesRows(dataList)
file.SaveExcelFile()
}
- property 파일들이 저장되어 있는 디렉토리 내의 폴더와 파일 리스트 내에서 property 파일들만 읽어 처리하기
package libraries
// Import 내용들
// 위의 함수들 호출하여 properties 파일 처리 및 액셀 파일 생성
func CreatePropertiesTranslationData(fileName string, pwd string) {
entries := read.ReadDir(pwd + "/assets/backend")
log.Printf("[PROPERTIES] Entry Length: %d", len(entries))
for i, folderData := range entries {
folderName := folderData.Name()
var propertyFileList []fs.DirEntry
var proPwd string
// 리드미 파일 혹은 예시 파일들은 제외
if folderName == "README.md" || folderName == "example.properties" {
continue
}
// 디렉토리인지 파일인지 여부 체크하여 분기처리
if folderData.IsDir() {
log.Printf("[PROPERTIES] Folder Name: %s", folderName)
propertyFileList, proPwd = read.ReadFile(pwd + "/assets/common/" + folderName)
if propertyFileList == nil {
log.Printf("[PROPERTIES] The File is not readable: %s", proPwd)
continue
}
} else {
log.Printf("[PROPERTIES] Common File Name: %s", folderName)
propertyFileList, proPwd = read.ReadFile(pwd + "/assets/common")
if propertyFileList == nil {
log.Printf("[PROPERTIES] The File is not readable: %s", proPwd)
continue
}
folderName = "default"
}
fileDataList := read.ReadProperties(propertyFileList, proPwd)
log.Printf("File Data List: %d", len(fileDataList))
write.OpenSaveExcelWithProperties(folderName, i, fileName, proPwd, fileDataList)
}
}
정리
- 디렉토리 내의 번역 파일 / 하위 디렉토리를 조회
- 번역 파일 / 하위 디렉토리 내부의 번역 파일 읽기
- 데이터를 파싱하여 원하는 형태로 나눠 구조체 안에서 관리
- 액셀 파일 생성
문제해결
- 디렉토리 내부의 하위 디렉토리를 따로 감지하여 파일 읽는 방법
- golang에 디렉토리 여부를 체크할 수 있는 기능이 내장되어 있음
- 함수의 분리
- 로직을 최소 기능 단위로 분리하여 함수 먼저 작성
- 함수 Receiver를 이용해 클래스 내부 메서드처럼 활용할 수 있게 작성
- 에러 발생 시 처리
- 요구사항 상, 프로그램이 종료 되기보다 에러 발생 부분은 건너뛰고 처리
- 로그를 확인하여 해당 부분은 매뉴얼하게 체크하는 방식을 선택함
728x90
반응형
'기록 > 업무일지' 카테고리의 다른 글
[업무일지] 오케스트레이션 관련 제안 (1) | 2024.11.26 |
---|---|
[업무일지] 서버 구성 관련 제안 정리 (1) | 2024.11.25 |
[업무일지:번역파일] 액셀 자동화 업무일지 - 2 json 파일 읽기 (2) | 2024.11.08 |
[업무일지:번역파일] 액셀 자동화 업무 일지 - 1 설계 (7) | 2024.11.07 |