Update Korean translations in reports and UI components

- Revised text in various components to improve Korean translations for better clarity and user experience.
- Updated report generation logic to reflect changes in the summary and detail sheet titles, ensuring consistency in language.
- Enhanced integration tests to validate the updated translations in generated reports and UI elements.
This commit is contained in:
DongHeon Jang 2026-04-17 12:02:25 +09:00
parent a01aa46f3e
commit eb7df53eff
10 changed files with 55 additions and 57 deletions

View File

@ -47,7 +47,7 @@ window.HANWHA_FORMS = [
{ {
"code": "REPORT_HINT", "code": "REPORT_HINT",
"level": "INFO", "level": "INFO",
"text": "batch가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다." "text": "배치가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다."
} }
] ]
}, },
@ -60,7 +60,7 @@ window.HANWHA_FORMS = [
{ {
"code": "UPLOAD_HINT", "code": "UPLOAD_HINT",
"level": "INFO", "level": "INFO",
"text": "invalid 샘플로 오류 시나리오를 확인한 뒤 valid 샘플을 재업로드합니다." "text": "오류 샘플로 오류 시나리오를 확인한 뒤 정상 샘플을 재업로드합니다."
} }
] ]
} }
@ -436,7 +436,7 @@ function renderUploads() {
<button class="secondary" data-action="reload-uploads">내역 새로고침</button> <button class="secondary" data-action="reload-uploads">내역 새로고침</button>
<button class="ghost" data-action="download-sample" data-sample-code="trial-balance-invalid">오류 샘플</button> <button class="ghost" data-action="download-sample" data-sample-code="trial-balance-invalid">오류 샘플</button>
<button class="ghost" data-action="download-sample" data-sample-code="trial-balance-valid">정상 TB</button> <button class="ghost" data-action="download-sample" data-sample-code="trial-balance-valid">정상 TB</button>
<button class="ghost" data-action="download-sample" data-sample-code="forecast-valid">정상 Forecast</button> <button class="ghost" data-action="download-sample" data-sample-code="forecast-valid">정상 전망</button>
</div> </div>
</div> </div>
<div class="panel"> <div class="panel">
@ -592,7 +592,7 @@ function shellContent(content) {
<aside class="sidebar"> <aside class="sidebar">
<div class="brand"> <div class="brand">
<h1>Hanwha Nexacro Demo</h1> <h1>Hanwha Nexacro Demo</h1>
<p>Spec driven preview generated from Nexacro DSL</p> <p>Nexacro DSL에서 생성한 미리보기 화면</p>
</div> </div>
<div class="nav-list">${renderNav()}</div> <div class="nav-list">${renderNav()}</div>
<div class="panel" style="margin-top: 18px;"> <div class="panel" style="margin-top: 18px;">

View File

@ -47,7 +47,7 @@ window.HANWHA_FORMS = [
{ {
"code": "REPORT_HINT", "code": "REPORT_HINT",
"level": "INFO", "level": "INFO",
"text": "batch가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다." "text": "배치가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다."
} }
] ]
}, },
@ -60,7 +60,7 @@ window.HANWHA_FORMS = [
{ {
"code": "UPLOAD_HINT", "code": "UPLOAD_HINT",
"level": "INFO", "level": "INFO",
"text": "invalid 샘플로 오류 시나리오를 확인한 뒤 valid 샘플을 재업로드합니다." "text": "오류 샘플로 오류 시나리오를 확인한 뒤 정상 샘플을 재업로드합니다."
} }
] ]
} }

View File

@ -57,7 +57,7 @@ this.txLoadReports = function(obj, e)
<Grid id="grdJobLogs" taborder="1" left="36" top="420" width="1368" height="320" binddataset="dsJobLogs"> <Grid id="grdJobLogs" taborder="1" left="36" top="420" width="1368" height="320" binddataset="dsJobLogs">
<Formats><Format id="default"><Columns><Column size="228"/><Column size="228"/><Column size="228"/><Column size="228"/><Column size="228"/><Column size="228"/></Columns><Rows><Row size="32" band="head"/><Row size="28"/></Rows><Band id="head"><Cell col="0" text="로그ID"/><Cell col="1" text="작업유형"/><Cell col="2" text="참조ID"/><Cell col="3" text="레벨"/><Cell col="4" text="메시지"/><Cell col="5" text="생성시각"/></Band><Band id="body"><Cell col="0" text="bind:id"/><Cell col="1" text="bind:jobType"/><Cell col="2" text="bind:referenceId"/><Cell col="3" text="bind:logLevel"/><Cell col="4" text="bind:logMessage"/><Cell col="5" text="bind:createdAt"/></Band></Format></Formats> <Formats><Format id="default"><Columns><Column size="228"/><Column size="228"/><Column size="228"/><Column size="228"/><Column size="228"/><Column size="228"/></Columns><Rows><Row size="32" band="head"/><Row size="28"/></Rows><Band id="head"><Cell col="0" text="로그ID"/><Cell col="1" text="작업유형"/><Cell col="2" text="참조ID"/><Cell col="3" text="레벨"/><Cell col="4" text="메시지"/><Cell col="5" text="생성시각"/></Band><Band id="body"><Cell col="0" text="bind:id"/><Cell col="1" text="bind:jobType"/><Cell col="2" text="bind:referenceId"/><Cell col="3" text="bind:logLevel"/><Cell col="4" text="bind:logMessage"/><Cell col="5" text="bind:createdAt"/></Band></Format></Formats>
</Grid> </Grid>
<Static id="staMessage0" taborder="900" left="36" top="824" width="1368" height="20" text="batch가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다."/> <Static id="staMessage0" taborder="900" left="36" top="824" width="1368" height="20" text="배치가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다."/>
<Layouts> <Layouts>
<Layout width="1440" height="900" screenid="Desktop_screen"/> <Layout width="1440" height="900" screenid="Desktop_screen"/>
</Layouts> </Layouts>

View File

@ -67,7 +67,7 @@ this.actUpload = function(obj, e)
<Grid id="grdIssues" taborder="1" left="36" top="486" width="1368" height="300" binddataset="dsIssues"> <Grid id="grdIssues" taborder="1" left="36" top="486" width="1368" height="300" binddataset="dsIssues">
<Formats><Format id="default"><Columns><Column size="342"/><Column size="342"/><Column size="342"/><Column size="342"/></Columns><Rows><Row size="32" band="head"/><Row size="28"/></Rows><Band id="head"><Cell col="0" text="행번호"/><Cell col="1" text="오류코드"/><Cell col="2" text="오류메시지"/><Cell col="3" text="등급"/></Band><Band id="body"><Cell col="0" text="bind:rowNumber"/><Cell col="1" text="bind:issueCode"/><Cell col="2" text="bind:issueMessage"/><Cell col="3" text="bind:severityCode"/></Band></Format></Formats> <Formats><Format id="default"><Columns><Column size="342"/><Column size="342"/><Column size="342"/><Column size="342"/></Columns><Rows><Row size="32" band="head"/><Row size="28"/></Rows><Band id="head"><Cell col="0" text="행번호"/><Cell col="1" text="오류코드"/><Cell col="2" text="오류메시지"/><Cell col="3" text="등급"/></Band><Band id="body"><Cell col="0" text="bind:rowNumber"/><Cell col="1" text="bind:issueCode"/><Cell col="2" text="bind:issueMessage"/><Cell col="3" text="bind:severityCode"/></Band></Format></Formats>
</Grid> </Grid>
<Static id="staMessage0" taborder="900" left="36" top="824" width="1368" height="20" text="invalid 샘플로 오류 시나리오를 확인한 뒤 valid 샘플을 재업로드합니다."/> <Static id="staMessage0" taborder="900" left="36" top="824" width="1368" height="20" text="오류 샘플로 오류 시나리오를 확인한 뒤 정상 샘플을 재업로드합니다."/>
<Layouts> <Layouts>
<Layout width="1440" height="900" screenid="Desktop_screen"/> <Layout width="1440" height="900" screenid="Desktop_screen"/>
</Layouts> </Layouts>

View File

@ -101,7 +101,7 @@ public class ConsolidationService {
); );
} }
reportService.generateReports(runId, fiscalPeriod, payload); reportService.generateReports(runId, fiscalPeriod, payload);
String summary = "rows=" + payload.getContributionRows().size() + ", eliminations=" + payload.getEliminationRows().size() + ", forecast=" + payload.getForecastRows().size(); String summary = "기여내역=" + payload.getContributionRows().size() + "건, 제거내역=" + payload.getEliminationRows().size() + "건, 전망=" + payload.getForecastRows().size() + "";
consolidationMapper.markRunSuccess(runId, summary); consolidationMapper.markRunSuccess(runId, summary);
consolidationMapper.insertJobLog("CONSOLIDATION", runId, "SUCCESS", "집계와 리포트 생성을 완료했습니다."); consolidationMapper.insertJobLog("CONSOLIDATION", runId, "SUCCESS", "집계와 리포트 생성을 완료했습니다.");
} catch (Exception exception) { } catch (Exception exception) {
@ -155,7 +155,7 @@ public class ConsolidationService {
eliminationRow.put("partnerEntityCode", partnerEntityCode); eliminationRow.put("partnerEntityCode", partnerEntityCode);
eliminationRow.put("accountCode", accountCode); eliminationRow.put("accountCode", accountCode);
eliminationRow.put("eliminationAmount", weightedAmount.negate().setScale(2, RoundingMode.HALF_UP)); eliminationRow.put("eliminationAmount", weightedAmount.negate().setScale(2, RoundingMode.HALF_UP));
eliminationRow.put("note", "Intercompany elimination"); eliminationRow.put("note", "내부거래 제거");
payload.getEliminationRows().add(eliminationRow); payload.getEliminationRows().add(eliminationRow);
} }
} }

View File

@ -122,9 +122,9 @@ public class ReportService {
try (XSSFWorkbook workbook = new XSSFWorkbook()) { try (XSSFWorkbook workbook = new XSSFWorkbook()) {
Map<String, CellStyle> styles = createExcelStyles(workbook); Map<String, CellStyle> styles = createExcelStyles(workbook);
buildSummarySheet(workbook, fiscalPeriod, payload, styles); buildSummarySheet(workbook, fiscalPeriod, payload, styles);
buildDetailSheet(workbook, "Contributions", fiscalPeriod, payload.getContributionRows(), styles); buildDetailSheet(workbook, "기여내역", fiscalPeriod, payload.getContributionRows(), styles);
buildDetailSheet(workbook, "Eliminations", fiscalPeriod, payload.getEliminationRows(), styles); buildDetailSheet(workbook, "제거내역", fiscalPeriod, payload.getEliminationRows(), styles);
buildDetailSheet(workbook, "Forecast", fiscalPeriod, payload.getForecastRows(), styles); buildDetailSheet(workbook, "전망", fiscalPeriod, payload.getForecastRows(), styles);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream); workbook.write(outputStream);
@ -137,13 +137,13 @@ public class ReportService {
Document document = new Document(PageSize.A4.rotate(), 36, 36, 36, 36); Document document = new Document(PageSize.A4.rotate(), 36, 36, 36, 36);
PdfWriter.getInstance(document, outputStream); PdfWriter.getInstance(document, outputStream);
document.open(); document.open();
addPdfTitle(document, "Hanwha Consolidation Summary", 18, new Color(24, 54, 93)); addPdfTitle(document, "한화 연결 집계 보고서", 18, new Color(24, 54, 93));
addPdfTitle(document, "Fiscal period " + fiscalPeriod + " | Generated " + generatedAt(), 10, new Color(90, 96, 106)); addPdfTitle(document, "회계기간 " + fiscalPeriod + " | 생성시각 " + generatedAt(), 10, new Color(90, 96, 106));
document.add(new Paragraph(" ")); document.add(new Paragraph(" "));
addSummaryTable(document, payload.getMetrics()); addSummaryTable(document, payload.getMetrics());
addDetailTable(document, "Contributions", payload.getContributionRows(), 8); addDetailTable(document, "기여내역", payload.getContributionRows(), 8);
addDetailTable(document, "Eliminations", payload.getEliminationRows(), 8); addDetailTable(document, "제거내역", payload.getEliminationRows(), 8);
addDetailTable(document, "Forecast", payload.getForecastRows(), 8); addDetailTable(document, "전망", payload.getForecastRows(), 8);
document.close(); document.close();
return outputStream.toByteArray(); return outputStream.toByteArray();
} }
@ -154,24 +154,24 @@ public class ReportService {
ConsolidationPayload payload, ConsolidationPayload payload,
Map<String, CellStyle> styles Map<String, CellStyle> styles
) { ) {
Sheet sheet = workbook.createSheet("Summary"); Sheet sheet = workbook.createSheet("요약");
sheet.setDisplayGridlines(false); sheet.setDisplayGridlines(false);
Row titleRow = sheet.createRow(0); Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(28); titleRow.setHeightInPoints(28);
createCell(titleRow, 0, "Hanwha Consolidation Summary", styles.get("title")); createCell(titleRow, 0, "한화 연결 집계 요약", styles.get("title"));
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 3)); sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 3));
Row subtitleRow = sheet.createRow(1); Row subtitleRow = sheet.createRow(1);
createCell(subtitleRow, 0, "Fiscal period " + fiscalPeriod + " | Generated " + generatedAt(), styles.get("subtitle")); createCell(subtitleRow, 0, "회계기간 " + fiscalPeriod + " | 생성시각 " + generatedAt(), styles.get("subtitle"));
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 3)); sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 3));
Row sectionRow = sheet.createRow(3); Row sectionRow = sheet.createRow(3);
createCell(sectionRow, 0, "Performance Snapshot", styles.get("section")); createCell(sectionRow, 0, "주요 지표", styles.get("section"));
Row headerRow = sheet.createRow(4); Row headerRow = sheet.createRow(4);
createCell(headerRow, 0, "Metric", styles.get("header")); createCell(headerRow, 0, "항목", styles.get("header"));
createCell(headerRow, 1, "Value", styles.get("header")); createCell(headerRow, 1, "", styles.get("header"));
int rowIndex = 5; int rowIndex = 5;
for (Map.Entry<String, BigDecimal> metric : payload.getMetrics().entrySet()) { for (Map.Entry<String, BigDecimal> metric : payload.getMetrics().entrySet()) {
@ -200,16 +200,16 @@ public class ReportService {
Row titleRow = sheet.createRow(0); Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(24); titleRow.setHeightInPoints(24);
createCell(titleRow, 0, sheetName + " Detail", styles.get("title")); createCell(titleRow, 0, sheetName, styles.get("title"));
mergeIfNeeded(sheet, 0, columnCount); mergeIfNeeded(sheet, 0, columnCount);
Row subtitleRow = sheet.createRow(1); Row subtitleRow = sheet.createRow(1);
createCell(subtitleRow, 0, "Fiscal period " + fiscalPeriod + " | Rows " + rows.size(), styles.get("subtitle")); createCell(subtitleRow, 0, "회계기간 " + fiscalPeriod + " | 건수 " + rows.size(), styles.get("subtitle"));
mergeIfNeeded(sheet, 1, columnCount); mergeIfNeeded(sheet, 1, columnCount);
if (rows.isEmpty()) { if (rows.isEmpty()) {
Row emptyRow = sheet.createRow(3); Row emptyRow = sheet.createRow(3);
createCell(emptyRow, 0, "No data available", styles.get("empty")); createCell(emptyRow, 0, "데이터가 없습니다.", styles.get("empty"));
sheet.setColumnWidth(0, 24 * 256); sheet.setColumnWidth(0, 24 * 256);
return; return;
} }
@ -370,8 +370,8 @@ public class ReportService {
table.setWidthPercentage(100f); table.setWidthPercentage(100f);
table.setSpacingBefore(6f); table.setSpacingBefore(6f);
table.setSpacingAfter(12f); table.setSpacingAfter(12f);
table.addCell(pdfHeaderCell("Metric")); table.addCell(pdfHeaderCell("항목"));
table.addCell(pdfHeaderCell("Value")); table.addCell(pdfHeaderCell(""));
for (Map.Entry<String, BigDecimal> metric : metrics.entrySet()) { for (Map.Entry<String, BigDecimal> metric : metrics.entrySet()) {
table.addCell(pdfBodyCell(label(metric.getKey()), Element.ALIGN_LEFT)); table.addCell(pdfBodyCell(label(metric.getKey()), Element.ALIGN_LEFT));
table.addCell(pdfBodyCell(format(metric.getValue(), metric.getKey()), Element.ALIGN_RIGHT)); table.addCell(pdfBodyCell(format(metric.getValue(), metric.getKey()), Element.ALIGN_RIGHT));
@ -387,7 +387,7 @@ public class ReportService {
document.add(section); document.add(section);
if (rows.isEmpty()) { if (rows.isEmpty()) {
document.add(new Paragraph("No data available", FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 10, new Color(120, 120, 120)))); document.add(new Paragraph("데이터가 없습니다.", FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 10, new Color(120, 120, 120))));
return; return;
} }
@ -411,7 +411,7 @@ public class ReportService {
if (rows.size() > maxRows) { if (rows.size() > maxRows) {
document.add(new Paragraph( document.add(new Paragraph(
"Showing first " + maxRows + " of " + rows.size() + " rows.", "전체 " + rows.size() + "건 중 상위 " + maxRows + "건만 표시합니다.",
FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 9, new Color(120, 120, 120)) FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 9, new Color(120, 120, 120))
)); ));
} }
@ -470,22 +470,22 @@ public class ReportService {
private static Map<String, String> buildLabels() { private static Map<String, String> buildLabels() {
Map<String, String> labels = new LinkedHashMap<>(); Map<String, String> labels = new LinkedHashMap<>();
labels.put("acceptedTrialRows", "Accepted Trial Rows"); labels.put("acceptedTrialRows", "승인된 TB 건수");
labels.put("acceptedForecastRows", "Accepted Forecast Rows"); labels.put("acceptedForecastRows", "승인된 전망 건수");
labels.put("grossContributionKrw", "Gross Contribution (KRW)"); labels.put("grossContributionKrw", "총 기여금액 (KRW)");
labels.put("eliminationKrw", "Elimination (KRW)"); labels.put("eliminationKrw", "제거금액 (KRW)");
labels.put("forecastKrw", "Forecast (KRW)"); labels.put("forecastKrw", "전망금액 (KRW)");
labels.put("netContributionKrw", "Net Contribution (KRW)"); labels.put("netContributionKrw", "순 기여금액 (KRW)");
labels.put("entityCode", "Entity Code"); labels.put("entityCode", "법인코드");
labels.put("accountCode", "Account Code"); labels.put("accountCode", "계정코드");
labels.put("partnerEntityCode", "Partner Entity"); labels.put("partnerEntityCode", "상대법인코드");
labels.put("translatedAmount", "Translated Amount"); labels.put("translatedAmount", "환산금액");
labels.put("ownershipRatio", "Ownership Ratio"); labels.put("ownershipRatio", "지분율");
labels.put("finalAmount", "Final Amount"); labels.put("finalAmount", "최종반영금액");
labels.put("internalTrade", "Internal Trade"); labels.put("internalTrade", "내부거래 여부");
labels.put("eliminationAmount", "Elimination Amount"); labels.put("eliminationAmount", "제거금액");
labels.put("note", "Note"); labels.put("note", "비고");
labels.put("scenarioCode", "Scenario"); labels.put("scenarioCode", "시나리오");
return labels; return labels;
} }
} }

View File

@ -96,9 +96,9 @@ class ConsolidationIntegrationTest {
.andReturn(); .andReturn();
try (var workbook = WorkbookFactory.create(new ByteArrayInputStream(excelDownload.getResponse().getContentAsByteArray()))) { try (var workbook = WorkbookFactory.create(new ByteArrayInputStream(excelDownload.getResponse().getContentAsByteArray()))) {
Assertions.assertEquals("Hanwha Consolidation Summary", workbook.getSheet("Summary").getRow(0).getCell(0).getStringCellValue()); Assertions.assertEquals("한화 연결 집계 요약", workbook.getSheet("요약").getRow(0).getCell(0).getStringCellValue());
Assertions.assertEquals("Entity Code", workbook.getSheet("Contributions").getRow(3).getCell(0).getStringCellValue()); Assertions.assertEquals("법인코드", workbook.getSheet("기여내역").getRow(3).getCell(0).getStringCellValue());
Assertions.assertNotNull(workbook.getSheet("Forecast")); Assertions.assertNotNull(workbook.getSheet("전망"));
} }
} }
@ -152,7 +152,7 @@ class ConsolidationIntegrationTest {
.andReturn(); .andReturn();
try (var workbook = WorkbookFactory.create(new ByteArrayInputStream(excelDownload.getResponse().getContentAsByteArray()))) { try (var workbook = WorkbookFactory.create(new ByteArrayInputStream(excelDownload.getResponse().getContentAsByteArray()))) {
Assertions.assertEquals("No data available", workbook.getSheet("Forecast").getRow(3).getCell(0).getStringCellValue()); Assertions.assertEquals("데이터가 없습니다.", workbook.getSheet("전망").getRow(3).getCell(0).getStringCellValue());
} }
} }

View File

@ -59,5 +59,4 @@ transactions:
messages: messages:
- code: REPORT_HINT - code: REPORT_HINT
level: INFO level: INFO
text: batch가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다. text: 배치가 생성한 Excel/PDF를 내려받고 최근 로그를 확인합니다.

View File

@ -103,5 +103,4 @@ actions:
messages: messages:
- code: UPLOAD_HINT - code: UPLOAD_HINT
level: INFO level: INFO
text: invalid 샘플로 오류 시나리오를 확인한 뒤 valid 샘플을 재업로드합니다. text: 오류 샘플로 오류 시나리오를 확인한 뒤 정상 샘플을 재업로드합니다.

View File

@ -368,7 +368,7 @@ function renderUploads() {
<button class="secondary" data-action="reload-uploads">내역 새로고침</button> <button class="secondary" data-action="reload-uploads">내역 새로고침</button>
<button class="ghost" data-action="download-sample" data-sample-code="trial-balance-invalid">오류 샘플</button> <button class="ghost" data-action="download-sample" data-sample-code="trial-balance-invalid">오류 샘플</button>
<button class="ghost" data-action="download-sample" data-sample-code="trial-balance-valid">정상 TB</button> <button class="ghost" data-action="download-sample" data-sample-code="trial-balance-valid">정상 TB</button>
<button class="ghost" data-action="download-sample" data-sample-code="forecast-valid">정상 Forecast</button> <button class="ghost" data-action="download-sample" data-sample-code="forecast-valid">정상 전망</button>
</div> </div>
</div> </div>
<div class="panel"> <div class="panel">
@ -524,7 +524,7 @@ function shellContent(content) {
<aside class="sidebar"> <aside class="sidebar">
<div class="brand"> <div class="brand">
<h1>{{appTitle}}</h1> <h1>{{appTitle}}</h1>
<p>Spec driven preview generated from Nexacro DSL</p> <p>Nexacro DSL에서 생성한 미리보기 화면</p>
</div> </div>
<div class="nav-list">${renderNav()}</div> <div class="nav-list">${renderNav()}</div>
<div class="panel" style="margin-top: 18px;"> <div class="panel" style="margin-top: 18px;">