大專案中網頁多語系的維護方式

也許在許多專案上我們所說的Multi Language僅有繁體中文、簡體中文和英文,但在比較大型且跨國的專案上,可能會涉及更多的語言,例如:日文、俄文、德文、法文、西班牙文…等等,通常這些軟體內容的翻譯,需要經過更專業的單位來進行,也許這個單位不僅僅需要具備有這些國家的語言能力,也要具備有相當的軟體知識,才能配合當地民情翻譯出正確的文字,這部分往往需要專業的翻譯單位來進行。

而專業的翻譯單位通常並不具有軟體的製作能力,所以在軟體上要如何快速地進行協同作業,就變成一項非常重要的工作,而在許多專案上,我們會讓翻譯單位透過Excel來提供各國語系的翻譯文字,我們則透過軟體進行轉換,將其轉換至軟體能快速讀取的格式,講白一點就是將Excel檔案轉換成XML格式,並提供其他軟體進行讀取。

整體流程會如上圖所示,在拿到一個翻譯社提供所有語系的Excel資料後,我們會進行轉換,將文字轉換成多個XML檔案,並打包成一個ZIP檔讓轉換者進行下載。

今天我們主要來分享上圖藍色部分的處理程序,也就是那一隻轉換程式的結構和做法,我們將轉換程式設計成網頁版本,藉此提升易用性,而轉換程式操作Flow大致如下:

  1. 使用者上傳檔案(限制僅能上傳Excel檔案)
  2. 給出ZIP下載連結(提供使用者下載所有語系的XML檔)

操作上非常簡單,僅有上述這兩個步驟,而程式設計上採用JSP架構其運作邏輯如下:

  1. 檢查上傳檔案的格式、容量及相關資訊
  2. 擷取Excel中第一張工作表(也可以依照工作表名稱擷取)
  3. 將Excel中第一列視為語系標題,並當作存檔名稱(例如:English.xml)
  4. 將剩下來的每一列轉換為該檔案的語系資料,並建立XML檔案
  5. 將所有建立好的XML檔案進行打包(ZIP)
  6. 更新頁面產生ZIP檔下載路徑

在該轉換程式中,另外有利用到下述的JAVA Library:

  1. Apache POI – 處理與解析Excel檔案
  2. DOM4J – 建立XML檔案
  3. Apache Commons – 處理檔案上傳

以下是轉換的程式碼:

<%@ page contentType="text/html; charset=UTF-8"%>

<%@ page import="java.io.File"%>
<%@ page import="java.text.*" %>
<%@ page import="java.util.*" %>
<%@ page import="java.util.Iterator"%>
<%@ page import="java.util.List"%>
<%@ page import="org.apache.commons.fileupload.*"%>
<%@ page import="org.apache.commons.io.FilenameUtils"%>

<%@ page import="java.util.zip.ZipEntry"%>
<%@ page import="java.util.zip.ZipOutputStream"%>

<%@ page import="java.io.FileInputStream"%>
<%@ page import="java.io.FileOutputStream"%>
<%@ page import="java.io.IOException"%>
<%@ page import="java.io.OutputStreamWriter"%>
<%@ page import="java.nio.charset.Charset"%>

<%@ page import="org.apache.poi.hssf.usermodel.HSSFRow"%>
<%@ page import="org.apache.poi.hssf.usermodel.HSSFSheet"%>
<%@ page import="org.apache.poi.hssf.usermodel.HSSFWorkbook"%>

<%@ page import="org.dom4j.io.OutputFormat"%>
<%@ page import="org.dom4j.io.XMLWriter"%>
<%@ page import="org.dom4j.Document"%>
<%@ page import="org.dom4j.DocumentHelper"%>
<%@ page import="org.dom4j.Element"%>


<%!
    //允許上傳的檔案
  String allowedFileTypes = ".xls";
  
  //建立目錄
    public void newFolder(String folderPath) {
        try {
            String filePath = folderPath;
            filePath = filePath.toString();
            java.io.File myFilePath = new java.io.File(filePath);
            if (!myFilePath.exists()) {
                myFilePath.mkdir();
            }
        }
        catch(Exception e) {
            System.out.println("建立目錄錯誤");
            //e.printStackTrace();
        }
    }
   
   // 轉換XLS為XML的主程式 ; 參數1.欲轉換的Excel工作表編號; 參數2.轉換的檔案路徑與檔名; 參數3.XML儲存的檔案路徑;
   public static String convertSheet(int sheetNumber, String conversionFile, String conversionXMLFilePath) {
     
     String convertStatus = "0"; // 輸出轉換狀態 ; 0 是失敗; 1是成功
     String conversionXMLFileName = null; // XML檔名
     String conversionXMLFile = null; // XML完整路徑與檔名
     
     // 產生儲存XML檔案的資料夾
     File file = new File(conversionXMLFilePath);
     if(!file.exists()){
         file.mkdirs();
     }
     
     // 開始讀取XLS檔案
     HSSFWorkbook book = null;
     try {
       book = new HSSFWorkbook(new FileInputStream(conversionFile));
     } catch (IOException e) {
       System.out.println("IOException : " + e);
     }
     
     HSSFSheet sheet = book.getSheetAt(sheetNumber); // 打開對應編號的工作表
     HSSFRow row = sheet.getRow(0);// 取得工作表的第一列資料
     String cell;
     int totalRows = sheet.getPhysicalNumberOfRows(); // 取得工作表中所有的列數
     int totalCol = row.getPhysicalNumberOfCells(); // 取的工作表中所有的欄數
     
     // 開始建立XML檔並將XLS內容建入
     for (int j = 1; j < totalCol; j++){
       Document document = DocumentHelper.createDocument();
       Element root = document.addElement("root");
       
       for (int i = 0; i < totalRows; i++){
         row = sheet.getRow(i);
         try {
           cell = row.getCell(j).toString();
           if(i==0) {
             conversionXMLFileName = cell;
             conversionXMLFile = conversionXMLFilePath + conversionXMLFileName + ".xml";
           }else {
             root.addElement("row_" + (i+1)).addCDATA(cell);
             /*
             if(sheetNumber == 0) {
               root.addElement("tag_" + (i-1), cell);
             }else {
               root.addElement(xmlKeyboardTitle[(i-1)], cell);
             }
             */
           }
         } catch (NullPointerException e) {
           break;
         }
       }
       
       File storedFile = new File(conversionXMLFile);
       
       if(storedFile.exists())
         storedFile.delete();

       FileOutputStream fos = null;
       OutputStreamWriter osw = null;
       XMLWriter writer = null;
       try {
         storedFile.createNewFile();
         OutputFormat format = OutputFormat.createPrettyPrint();  
               format.setEncoding("utf-8");
               fos = new FileOutputStream(storedFile);
               osw = new OutputStreamWriter(fos, Charset.forName("utf-8"));
               writer = new XMLWriter(osw, format);
               writer.write(document);
       } catch (IOException e) {
         System.out.println("IOException : " + e);
       } finally {
         try {
           if(writer != null) writer.close();
           if(osw != null) osw.close();
           if(fos != null) fos.close();
           convertStatus = "1";
         } catch (IOException e) {
           System.out.println("IOException : " + e);
         }
       }
     }
     return convertStatus; // 回覆轉換狀態
   }
   
   List<String> filesListInDir = new ArrayList<String>();
   
   public void zipDirectory(File dir, String zipDirName) {
     
     filesListInDir = new ArrayList<String>();
     
        try {
          
            populateFilesList(dir);
            //now zip files one by one
            //create ZipOutputStream to write to the zip file
            FileOutputStream fos = new FileOutputStream(zipDirName);
            ZipOutputStream zos = new ZipOutputStream(fos);
            for(String filePath : filesListInDir){
                System.out.println("Zipping "+filePath);
                //for ZipEntry we need to keep only relative file path, so we used substring on absolute path
                ZipEntry ze = new ZipEntry(filePath.substring(dir.getAbsolutePath().length()+1, filePath.length()));
                zos.putNextEntry(ze);
                //read the file and write to ZipOutputStream
                FileInputStream fis = new FileInputStream(filePath);
                byte[] buffer = new byte[1024];
                int len;
                while ((len = fis.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }
                zos.closeEntry();
                fis.close();
            }
            zos.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
   private void populateFilesList(File dir) throws IOException {
        File[] files = dir.listFiles();
        for(File file : files){
            if(file.isFile()) filesListInDir.add(file.getAbsolutePath());
            else populateFilesList(file);
        }
    }
  
%>

<%

String messageReturn = "";
  
try{
  
    request.setCharacterEncoding("utf-8");
    DiskFileUpload fileUpload = new DiskFileUpload();
  List<FileItem> fileItems = fileUpload.parseRequest(request);
  FileItem fileItem = fileItems.get(0);
  
    //原始上傳檔案名稱
  String originalFileName = fileItem.getName();
    //out.print("originalFileName : " + originalFileName + "<br>");
    
  if (originalFileName != null && !"".equals(originalFileName)) {

        originalFileName = FilenameUtils.getName(originalFileName);
        String extension = FilenameUtils.getExtension(originalFileName);
        
    //判斷檔案格式是否允許
    //out.print("extension : " + extension + "<br>");
        if (allowedFileTypes.indexOf(extension.toLowerCase()) != -1) {
            String filePath = this.getServletContext().getRealPath(request.getRequestURI().substring(request.getContextPath().length()));
            String savePath = new File(filePath).getParent() + "/upload";
          //out.println("savePath = " + savePath + "<br>");
      newFolder(savePath);
      
          String savePathAndName = savePath + "/" + originalFileName;
            //out.print(savePathAndName);
      
          File f = new File(savePathAndName);
          if(!f.exists()){
            f.createNewFile();
          }
          fileItem.write(f);
            
            //messageReturn += "File path : " + savePath + "<br>";
            
             String xmlSavePath = savePath + "/xml/";
             //messageReturn += "xmlSavePath : " + xmlSavePath + "<br>";
            
            if("1".equals(convertSheet(0, savePathAndName , xmlSavePath))){
              messageReturn += "File converted successfully.<br>";
        }else{
          messageReturn += "File conversion failed.<br>";
        };
        
        /*
        xmlSavePath = savePath + "/xml/keyboard/";
        
        if("1".equals(convertSheet(1, savePathAndName , xmlSavePath))){
              messageReturn += "Keyboard sheet conversion succeeded.<br>";
        }else{
          messageReturn += "Keyboard sheet conversion fail.<br>";
        };
        */
      
        java.io.File myDelFile = new java.io.File(savePath + "/All.zip");
        myDelFile.delete();
        
        zipDirectory(new File(savePath + "/xml/"), savePath + "/All.zip");
        
        messageReturn += "<a href='upload/All.zip' target='_blank'>Download Link</a><br>";

        } else {
          messageReturn += "上傳錯誤 : 上傳的檔案不能是" + extension + ",僅允許xls格式<br>";
        }
    }
}catch(Exception e){
  //e.printStackTrace();
}

%>

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>AIOT CC Multi-Language Convertion Tool</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <style>
        *{
            font-family: 微軟正黑體;
        }
        h2{
            text-align: center;
        }
        .marginBottom20{
            margin-bottom: 20px;
        }
        #uploadBtn{
            margin: auto;
            display: block;
        }
        #messageDiv{
          color: red;
          text-align:center;
        }
    </style>
</head>

<body>

    <div class="container">
        <h2 class="marginBottom20">AIOT CC Multi-Language Convertion Tool</h2>
        <div class="form-group text-center">
          <form name="upload" enctype="multipart/form-data" method="post" action="index.jsp" onsubmit="return check_select()">
            <input type="file" name="file" id="file" size="60" maxlength="20" placeholder="*.xls" class="marginBottom20">
            <input id="uploadBtn" type="submit" value="轉換" class="btn btn-primary">
        </form>
      </div>
      <div id="messageDiv"><% out.print(messageReturn); %></div>
    </div>
    
</body>

</html>
<script>
    function check_select(form) {
        if (file.value == "") {
            alert("請選擇檔案");
            return false;
        } else {
            // document.getElementById("uploadBtn").disabled = true;
            //document.getElementById("msgDiv").innerHTML = "檔案上傳中,請稍候";
            return true;
        }
    }
</script>

You may also like...

104,318 Responses

  1. Williecrady表示:

    Permainan slot bisa dimainkan dengan berbagai taruhan https://garuda888.top/# Slot menawarkan kesenangan yang mudah diakses

  2. DavidCooth表示:

    http://bonaslot.site/# Kasino sering memberikan hadiah untuk pemain setia

  3. Aaronbus表示:

    preman69 preman69 Slot dengan tema film terkenal menarik banyak perhatian

  4. В Vovan Casino вы откроете для себя уникальный игровой опыт. В нашем казино вы найдете разнообразие игр, включая карточные игры, слоты и рулетку. Для наших гостей доступны специальные предложения, которые помогают усилить азарт. Каждый день в Vovan Casino — это возможность испытать удачу. Участвуйте в эксклюзивных событиях Vovan Casino, чтобы получить незабываемые эмоции. Это еще и возможность сэкономить время, а также получить максимум от игрового процесса. Каждая игра дарит вам не только удовольствие, но и шанс стать победителем — https://vovan-casinowave.icu/. Когда лучше всего начинать играть? Ответ прост: в любое время! Вот несколько советов, которые помогут вам сделать игру комфортнее: Рекомендуем ознакомиться с нашими правилами, чтобы сделать игровой процесс приятным и безопасным. Опытным участникам доступны особые условия, которые позволят вам добиться лучшего результата. Если вы давно не играли, начните с бесплатных демо-версий игр.

  5. Feel free to visit my web page … best Online Poker sites

  6. DavidCooth表示:

    http://slotdemo.auction/# Banyak kasino memiliki program loyalitas untuk pemain

  7. Top Prams表示:

    Introduction To The Intermediate Guide On Good
    Prams Top Prams

  8. Williecrady表示:

    Slot menawarkan kesenangan yang mudah diakses https://garuda888.top/# Pemain sering berbagi tips untuk menang

  9. Добро пожаловать в R7 Казино, идеальное место для азартных игроков, ищущих шанс стать победителем. В R7 Казино вас ждет большой выбор увлекательных игр, от популярных слотов до эксклюзивных столов с живыми дилерами. https://10kilogramm.ru/ В R7 Казино каждая игра — это реальная возможность для выигрыша и веселья.

    Почему стоит выбрать именно нас? В R7 Казино важен каждый игрок, и мы предлагаем высокий уровень комфорта и защиты ваших данных. Кроме того, мы часто проводим акции и турниры с крупными призами, что позволяет игрокам увеличить свои шансы на успех.

    В каком случае стоит начать играть в R7 Казино? Не откладывайте, начните играть в R7 Казино уже сегодня и испытайте удачу на своем пути к победе. Вот несколько причин, почему вам стоит выбрать именно нас:

    Читайте наши условия игры, чтобы избежать неприятных ситуаций и играть с уверенностью.
    Наши постоянные игроки могут получить специальные бонусы и привилегии, доступные только им.
    Мы предлагаем демо-режимы для новых игроков, чтобы вы могли освоить игру без стресса и потерь.

    R7 Казино — это шанс для каждого на крупные выигрыши и незабываемые эмоции!

  10. DavidCooth表示:

    http://slotdemo.auction/# Kasino menyediakan layanan pelanggan yang baik

  11. DavidCooth表示:

    http://slot88.company/# Banyak pemain menikmati bermain slot secara online

  12. Mae Banowetz表示:

    I have been absent for a while, but now I remember why I used to love this web site. Thanks , I will try and check back more frequently. How frequently you update your web site?

  13. ThomasThype表示:

    Slot memberikan kesempatan untuk menang besar: slot demo gratis – demo slot pg

  14. Williecrady表示:

    Banyak kasino menawarkan permainan langsung yang seru http://preman69.tech/# п»їKasino di Indonesia sangat populer di kalangan wisatawan

  15. The Little-Known Benefits To Replacement Upvc Window Handles upvc Window Repairs near me

  16. ThomasThype表示:

    Slot dengan pembayaran tinggi selalu diminati: bonaslot – bonaslot

  17. DavidCooth表示:

    http://slot88.company/# Kasino menawarkan pengalaman bermain yang seru

  18. ThomasThype表示:

    Permainan slot bisa dimainkan dengan berbagai taruhan: slot88.company – slot 88

  19. Aaronbus表示:

    preman69.tech preman69.tech Kasino mendukung permainan bertanggung jawab

  20. A Comprehensive Guide To Second Hand Double Buggy From Start
    To Finish Ready To Grow Double Stroller (http://Www.Metooo.Com)

  21. Edna表示:

    The Reason Why Mental Health Psychiatrist Is Much More Hazardous Than You Think full mental health assessment, Edna,

  22. Alta表示:

    5 Killer Quora Answers On Psychiatric Assessment UK
    psychiatric assessment uk (Alta)

  23. DavidCooth表示:

    https://slot88.company/# Slot memberikan kesempatan untuk menang besar

  24. fit表示:

    You’ll Never Guess This Double Glazing Doors Near Me’s Benefits fit

  25. DavidCooth表示:

    http://slot88.company/# Slot dengan tema budaya lokal menarik perhatian

  26. This Is The Complete Listing Of Pram 2in1 Dos And Don’ts car seat 2 In 1 stroller

  27. Williecrady表示:

    Slot dengan grafis 3D sangat mengesankan https://bonaslot.site/# Keseruan bermain slot selalu menggoda para pemain

  28. 10 Things That Your Family Taught You About Online Psychiatric Assessment UK online psychiatric Assessment

  29. ThomasThype表示:

    Mesin slot menawarkan berbagai tema menarik: slot88 – slot 88

  30. ThomasThype表示:

    Pemain bisa menikmati slot dari kenyamanan rumah: slot 88 – slot88.company

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。