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

也許在許多專案上我們所說的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...

101,329 Responses

  1. MicahTails表示:

    Us Mex Pharm: Us Mex Pharm – mexican pharmacy

  2. DennisDor表示:

    UsMex Pharm: certified Mexican pharmacy – mexico drug stores pharmacies

  3. SteveViand表示:

    https://usmexpharm.com/# Us Mex Pharm

  4. Cletusiders表示:

    USMexPharm: UsMex Pharm – Us Mex Pharm

  5. RandyKargo表示:

    usa mexico pharmacy mexican pharmacy mexican mail order pharmacies

  6. MicahTails表示:

    Mexican pharmacy ship to USA: Us Mex Pharm – certified Mexican pharmacy

  7. RandyKargo表示:

    USMexPharm UsMex Pharm Mexican pharmacy ship to USA

  8. 7Slots表示:

    7slots Casino – Premium Kumarhane Keyfi

    Dijital kumarhane dünyasında 7 Slots Casino, oyunculara özel avantajlar sunuyor https://wiki.morx.in/index.php/7slots_Casino_%E2%80%93_En_%C4%B0yi_Kumarhane_Keyfi_2025. Lisanslı ve güvenilir hizmet ile öne çıkan bu platform, Türk oyuncular için ideal bir seçim.

    7 Slots Casino Kazanç Fırsatları

    Özel Turnuvalar

    7 Slots bünyesinde yüzlerce yüksek RTP’li oyun bulunmaktadır. Microgaming gibi lider yazılım şirketleri tarafından geliştirilen bu oyunlar, kesintisiz eğlence vaat ediyor.

    7 Slots Yatırım İkramiyeleri

    Cashback Fırsatları

    Yeni oyuncular 7 Slots Casino’da özel promosyonlar ile karşılanıyor. aylık turnuvalar sayesinde her seviyeden oyuncu ekstra kazanç elde edebiliyor.

    Kripto Para Alternatifleri

    7 Slots, slot meraklıları için özel ödeme çözümleri sunmaktadır. Bitcoin gibi gizlilik odaklı sistemler ile anında işlem garantileniyor.

    7slots Casino Mobil Uyumluluk

    Modern oyuncular için 7slots casino online tam mobil uyum sunuyor. Android cihazlarda kesintisiz oyun keyfi ile mobil cihazınızda bahis yapabilirsiniz.

    Müşteri Desteği

    7 Slots Casino hızlı çözüm üreten temsilciler ile hızlı çözüm üreten bir hizmet sunmaktadır. WhatsApp bağlantısı gibi pratik destek yolları sayesinde sorunlarınız anında çözülür.

    7 Slots Casino Güvenlik Önlemleri

    Uluslararası lisans ile 7slots casino online, kişisel verilerin korunması konusunda üst düzey önlemler uygulamaktadır. şeffaf ödeme sistemleri ile kalitesini belgelemiş bir platformdur.

    Değerlendirme

    7slots casino online, Türk oyunculara özel bir dijital kumarhane sunmaktadır. geniş ödeme seçenekleri ile sektörde öne çıkan bir platform olarak dikkat çekmektedir.

  9. WilliamWhesy表示:

    “You have a government that is reckless about what is going to happen to Guyana,” said Melinda Janki, an international lawyer in Guyana who is handling several lawsuits against Exxon. It’s pursuing “a supposed course of development that is actually backward and destructive,” she told CNN.
    kelpdao
    And while plenty of Guyanese people welcome the new oil industry, some say Guyana’s startling economic statistics do not reflect a real-world prosperity for ordinary people, many of whom are struggling with the higher prices accompanying the oil boom. Inflation rose 6.6% in 2023, with prices of some foods shooting up much more rapidly.

    “Since the oil extraction began in Guyana, we have noticed that our cost of living has gone sky high,” said Wintress White, of Red Thread, a non-profit that focuses on improving living conditions for Guyanese women. “The money is not trickling down to the masses,” she told CNN.

    CNN contacted President Ali, the Ministry of Natural Resources and the Ministry of Finance for comment but received no response.
    Guyana, a former Dutch then British colony which gained independence in 1966, is one of only a handful of countries that is a “carbon sink,” meaning it stores more planet-heating pollution than it produces. This is due to its vast rainforest; trees remove carbon dioxide from the atmosphere as they grow.

    The country has protected its biodiversity where others have destroyed theirs, President Ali said in a BBC interview last year. In 2009, the country signed an agreement with Norway, which promised Guyana more than $250 million to preserve its 18.5 million hectares, or nearly 46 million acres, of forests.

    Ali insists the country can balance climate leadership and fossil fuel exploitation. The new oil wealth will allow Guayana to develop, including building climate adaptations such as sea walls, he has said. He has also pointed to the continued failures of wealthy countries, already grown rich on their own fossil fuels, to help poorer countries with climate finance.

    But there are concerns Guyana could fall victim to the “resource curse,” in which vast, new wealth ?can actually make life worse for those who live there.

  10. RandyKargo表示:

    mexican online pharmacies prescription drugs mexican pharmacy Us Mex Pharm

  11. DennisDor表示:

    mexican rx online: Us Mex Pharm – certified Mexican pharmacy

  12. Allanref表示:

    A tiny rainforest country is growing into a petrostate. A US oil company could reap the biggest rewards
    kyberswap
    Guyana’s destiny changed in 2015. US fossil fuel giant Exxon discovered nearly 11 billion barrels of oil in the deep water off the coast of this tiny, rainforested country.

    It was one of the most spectacular oil discoveries of recent decades. By 2019, Exxon and its partners, US oil company Hess and China-headquartered CNOOC, had started producing the fossil fuel.? They now pump around 650,000 barrels of oil a day, with plans to more than double this to 1.3 million by 2027.

    Guyana now has the world’s highest expected oil production growth through 2035.

    This country — sandwiched between Brazil, Venezuela and Suriname — has been hailed as a climate champion for the lush, well-preserved forests that carpet nearly 90% of its land. It is on the path to becoming a petrostate at the same time as the impacts of the fossil fuel-driven climate crisis escalate.

    While the government says environmental protection and an oil industry can go hand-in-hand, and low-income countries must be allowed to exploit their own resources, critics say it’s a dangerous path in a warming world, and the benefits may ultimately skew toward Exxon — not Guyana.
    Since Exxon’s transformative discovery, Guyana’s government has tightly embraced oil as a route to prosperity. In December 2019, then-President David Granger said in a speech, “petroleum resources will be utilized to provide the good life for all … Every Guyanese will benefit.”

    It’s a narrative that has continued under current President Mohamed Irfaan Ali, who says new oil wealth will allow Guyana to develop better infrastructure, healthcare and climate adaptation.

  13. TerryDek表示:

    The voice of ‘White Lotus’ star Walton Goggins is the lullaby we didn’t know we needed
    jumper exchange
    While his “White Lotus” character Rick has been the source of some stress this season, Walton Goggins is here to soothe us into a state of dreamy sleep to make up for it.

    The actor has partnered with relaxation and meditation app Calm for one of their famed Sleep Stories, lending his smoky voice to a fable titled “The Yard Sale.”

    Goggins announced the Sleep Story on his verified Instagram on Tuesday, writing, “A friend once said to me the first question you ask someone shouldn’t be, ‘How are you?’ but rather, ‘How did you sleep last night?’ I agree.”

    The post included an excerpt from the story, in which Goggins is heard languidly instructing listeners to relax their bodies and get into bed. “You could even climb into a hammock,” he added. “I wouldn’t do that because I’ve never gracefully got in or out of one.”

    In the caption, the actor also wrote that he “wanted to create a Sleep Story that feels dreamlike, helping people slow their minds down by wandering through a yard sale (which happens to be one of my favorite things to do), uncovering hidden treasures.”

    “It’s the Walton Goggins version of counting sheep. I hope you enjoy,” he added.

    Other celebrities who have read bedtime stories in the hopes of putting audiences to sleep include Dolly Parton and the late Jimmy Stewart, whose voice was featured in a Calm Christmas Sleep Story in 2023 thanks to generative AI technology.

    Goggins currently stars on “The White Lotus,” where his character is often the most stressed out and tortured of the ensemble, at one point setting a slew of snakes free.

  14. DennisDor表示:

    USMexPharm: UsMex Pharm – UsMex Pharm

  15. Cletusiders表示:

    USMexPharm: certified Mexican pharmacy – UsMex Pharm

  16. SteveViand表示:

    https://usmexpharm.shop/# Us Mex Pharm

  17. MicahTails表示:

    mexico drug stores pharmacies: USMexPharm – Mexican pharmacy ship to USA

  18. Cletusiders表示:

    Us Mex Pharm: certified Mexican pharmacy – Us Mex Pharm

  19. MichaelCible表示:

    Мы стремимся сделать процесс ремонта максимально быстрым и удобным для клиентов. Все работы выполняются с использованием оригинальных запчастей или проверенных аналогов, в зависимости от вашего выбора.
    https://iphone-remont.by/remont-iphone/14-pro-max/vosstanovlenie-funkcii-face-idFace ID на айфоне 14 починили быстро, очень доволен!
    Наша команда готова помочь вам решить любые технические проблемы, связанные с iPhone, iPad, MacBook и другими устройствами. Мы гарантируем качество выполненных работ и предоставляем гарантию на все виды услуг.

    Важно: i-Guru не является авторизованным сервисным центром Apple и не имеет прямых связей с компанией Apple Inc. Мы предоставляем независимые услуги по ремонту устройств.

  20. DennisDor表示:

    UsMex Pharm: Us Mex Pharm – mexican mail order pharmacies

  21. SteveViand表示:

    http://usmexpharm.com/# USMexPharm

  22. RandyKargo表示:

    Us Mex Pharm mexican pharmacy usa mexico pharmacy

  23. MicahTails表示:

    Us Mex Pharm: certified Mexican pharmacy – certified Mexican pharmacy

  24. JorgeIcova表示:

    кра сайт – кракен тор, Площадка кракен

  25. SteveViand表示:

    https://usmexpharm.com/# USMexPharm

發佈回覆給「SteveViand」的留言 取消回覆

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