jquery/vue/react前端多語言國際化翻譯方案指南

Tz一號發表於2021-09-03

本文章共3470字,預計閱讀時間5-10分鐘。

國際化-前言

每個開發者能希望編寫的程式可以讓全世界的使用者使用,它要求從產品中抽離所有地域語言,國家/地區和文化相關的元素。換種說法,應用程式的功能和程式碼設計時考慮在不同地區執行的需要,其程式碼適應不同區域要求。開發這樣的的過程,就稱為國際化( internationalization),簡稱i18n。

i18n(其來源是英文單詞 internationalization的首末字元i和n,18為中間的字元數)是“國際化”的簡稱。在資訊領域,國際化(i18n)指讓產品(出版物,軟體,硬體等)無需做大的改變就能夠適應不同的語言和地區的需要。對程式來說,在不修改內部程式碼的情況下,能根據不同語言及地區顯示相應的介面。

在全球化的時代,國際化尤為重要,因為產品的潛在使用者可能來自世界的各個角落!

國際化-範圍

國際化與本地化工作的焦點包括但不限於如下:

語言

  • 不同國家的語言;
  • 文字書寫方向;(比如德語是從左到右,而波斯語、希伯來語和阿拉伯語是由右到左。)
  • 圖片中包含的文字;
  • 程式中的音訊;
  • 程式中的視訊字幕;

文化

  • 圖片和顏色:這牽涉到理解和文化適宜的議題;
  • 名字和稱謂;
  • 政府給定的編碼(如美國的社會安全碼,英國的National Insurance number,愛沙尼亞的Isikukood及其它各國的身份證號碼)和護照;
  • 電話號碼字首、地址和國際郵遞區號;
  • 貨幣 (符號、貨幣標誌的位置);
  • 長度、容積、重量單位;
  • 標準紙張大小;

書寫習慣

  • 日期跟時間的格式,包含各式日曆。
  • 時區(在國際場合會使用世界標準時間)
  • 數字格式(小數點、分隔點的位置、分隔所用的字元)

產品和服務所要面向的法規

程式的內容、運營方式及方向需要遵守當地法律、法規;

多語言翻譯方案

目前,並沒有非常完美,適用所有情況的多語言方案,所以找到一個比較契合自己團隊的方案,並做一系列配製方法去用,是比較耗時的一項工程。是否需要花時間成本來做到前端國際化,完全取決分析自身團隊的需求。

使用外掛線上翻譯

隨著全球化網路時代的到來,語言障礙已經成為二十一世紀社會發展的重要瓶頸,實現任意時間、任意地點、任意語言的無障礙自由溝通是人類追求的一個夢想。這僅是全球化背景下的一個小縮影。在社會快速發展的程式中,線上翻譯扮演越來越重要的角色。

執行規則

將單詞序列(一個或多個句子)作為輸入,並生成單詞的輸出序列,這是通過遞迴神經網路(RNN)實現的。具體來說,就是讓兩個將與某個特殊令牌一起執行的遞迴神經網路嘗試根據前一個序列來預測後一個狀態序列。

它主要由編碼器和解碼器兩部分構成,因此有時候被稱為編碼器-解碼器網路。

· 編碼器:使用多個深度神經網路層,將輸入單詞轉換為相應的隱藏向量。每個向量代表當前單詞及其語境。

· 解碼器:與編碼器類似。它將編碼器生成的隱藏向量、自身的隱藏狀態和當前單詞作為輸入,從而生成下一個隱藏向量,最終預測下一個單詞。

谷歌外掛線上翻譯

谷歌不再提供對 Google 翻譯的網站翻譯器的新訪問。此更改不會影響網站翻譯器的現有使用。

谷歌鼓勵希望翻譯網頁的使用者使用支援本地翻譯的瀏覽器。

效果圖示例:

程式碼示例

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Translate</title>
</head>
<body>
    <p>你好 世界!</p>
    <!--谷歌翻譯 -->
    <!--    如需隱藏/更改一些樣式,可使用css操作-->
    <div id="google_translate_element"></div>
</body>
<script src="http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
<script>
  function googleTranslateElementInit({
    new google.translate.TranslateElement({
      // includedLanguages: 'de,en,es,fr,it', 需要下拉翻譯支援哪些語言,預設全部
      layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL
    }, 'google_translate_element');
  }
</script>
</html>

線上示例/原始碼地址

線上示例:點此檢視- 谷歌外掛線上翻譯(需要可以訪問google)

原始碼地址:https://github.com/Tzlibai/Demo/tree/master/i18n/google

注:目前瀏覽器基本都內建了- 網頁線上翻譯功能**

PS: 谷歌翻譯外掛會在替換文字時修改標籤(DOM結構)會導致Vue、React這種基於virtual dom的框架產生問題

問題:難以避免的誤差

機器翻譯其誤差在所難免,原因在於,機器翻譯運用語言學原理,機器自動識別語法,呼叫儲存的詞庫,自動進行對應翻譯,但是因語法、詞法、句法發生變化或者不規則,出現錯誤是難免的,比如《大話西遊》中**“給我一個殺你的理由,先”**中,“先”字意義上其實是起修飾限制作用,但在機器翻譯時就會有不同的意思。

機器畢竟是機器,沒有人對語言的特殊感情。它怎麼會感受“萬里悲秋常作客,百年多病獨登臺”的無限悲涼之意?畢竟漢語因其詞法、語法、句法的變化及其語境的更換,其意思大相徑庭。

維護多套頁面/語言程式碼

顧名思義:**不同語言編寫不同的頁面。**假設需要支援3種語言,此時需要編寫三種不同的頁面,這樣的弊端是當頁面需要維護修改時,需要對不同的頁面進行更改

效果圖示例:

線上示例/原始碼地址

線上示例:點此檢視- 維護多套頁面/語言程式碼

原始碼地址:https://github.com/Tzlibai/Demo/tree/master/i18n/more

語言包配置檔案

將所有的語言資源放在獨立的資料夾下,以每個欄位唯一標識,去找到不同語言相對應的欄位,以顯示來完成前端國際化。 這樣在html我們只需要輸出識別符號,在js中配置好功能、路徑,我們就可以讓它自行去語言資源包中找到對應語言欄位以顯示。

jQuery - 多語言翻譯

程式碼示例

主要步驟如下:

  • jquery.js檔案

  • jquery.i18n.properties.js檔案

  • 各個國家語言包(.properties格式檔案,每個國家語言包目錄下都會有該檔案,只是配置不同)

    // commion.properties 檔案格式如下
    // (key) = (value)
    

    hi=Hello world!

  • index 編寫邏輯,讀寫cookie、引入語言包、根據class渲染文案

整理完成後 目錄如下圖:

html檔案如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--    這裡是設定id為i18n_pagename, 引入了資原始檔common-->
    <meta id="i18n_pagename" content="common">
    <title>jQuery - 多語言翻譯</title>
</head>
<body>
<!--  這裡面class=”i18n”寫法,下邊在js裡面我們可以根據類選擇器獲取需要國際化的地方,-->
<!--  然後name=”hi”這裡面的 hi 跟我們語言包裡面資原始檔裡面的key保持一致-->
    <label class="i18n" name="hi"></label>
    <br>
<!--切換按鈕-->
    <select id="language">
        <option value="zh-CN">中文</option>
        <option value="en">英語</option>
        <option value="idn">印尼語語</option>
    </select>
</body>
<script src="./i18n/jquery.js"></script>
<script src="./i18n/jquery.i18n.properties.js.js"></script>
<script>
  /**
   * cookie操作
   */

  var getCookie = function(name, value, options{
    if (typeof value != 'undefined') { // name and value given, set cookie
      options = options || {};
      if (value === null) {
        value = '';
        options.expires = -1;
      }
      var expires = '';
      if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
        var date;
        if (typeof options.expires == 'number') {
          date = new Date();
          date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
        } else {
          date = options.expires;
        }
        expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
      }
      var path = options.path ? '; path=' + options.path : '';
      var domain = options.domain ? '; domain=' + options.domain : '';
      var s = [cookie, expires, path, domain, secure].join('');
      var secure = options.secure ? '; secure' : '';
      var c = [name, '='encodeURIComponent(value)].join('');
      var cookie = [c, expires, path, domain, secure].join('')
      document.cookie = cookie;
    } else { // only name given, get cookie
      var cookieValue = null;
      if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
          var cookie = jQuery.trim(cookies[i]);
          // Does this cookie string begin with the name we want?
          if (cookie.substring(0, name.length + 1) == (name + '=')) {
            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
            break;
          }
        }
      }
      return cookieValue;
    }
  };

  /**
   * 獲取瀏覽器語言型別
   * @return {string} 瀏覽器國家語言
   */

  var getNavLanguage = function(){
    if(navigator.appName == "Netscape"){
      var navLanguage = navigator.language;
      return navLanguage.substr(0,2);
    }
    return false;
  }

  /**
   * 設定語言型別: 預設為中文
   */

  var i18nLanguage = "zh-CN";
  /*
  設定一下網站支援的語言種類
   */

  var webLanguage = ['zh-CN''idn''en'];

  /**
   * 執行頁面i18n方法
   * @return
   */

  var execI18n = function(){
    /*
    獲取一下資原始檔名
     */

    var optionEle = $("#i18n_pagename");
    if (optionEle.length < 1) {
      console.log("未找到頁面名稱元素,請在頁面寫入\n <meta id=\"i18n_pagename\" content=\"頁面名(對應語言包的語言檔名)\">");
      return false;
    };
    var sourceName = optionEle.attr('content');
    sourceName = sourceName.split('-');
    /*
    首先獲取使用者瀏覽器裝置之前選擇過的語言型別
     */

    if (getCookie("userLanguage")) {
      i18nLanguage = getCookie("userLanguage");
    } else {
      // 獲取瀏覽器語言
      var navLanguage = getNavLanguage();
      if (navLanguage) {
        // 判斷是否在網站支援語言陣列裡
        var charSize = $.inArray(navLanguage, webLanguage);
        if (charSize > -1) {
          i18nLanguage = navLanguage;
          // 存到快取中
          getCookie("userLanguage",navLanguage);
        };
      } else{
        console.log("not navigator");
        return false;
      }
    }
    /* 需要引入 i18n 檔案*/
    if ($.i18n == undefined) {
      console.log("請引入i18n js 檔案")
      return false;
    };

    /*
    這裡需要進行i18n的翻譯
     */

    jQuery.i18n.properties({
      name : sourceName, //資原始檔名稱
      path : 'i18n/' + i18nLanguage +'/'//資原始檔路徑
      mode : 'map'//用Map的方式使用資原始檔中的值
      language : i18nLanguage,
      callback : function({//載入成功後設定顯示內容
        var insertEle = $(".i18n");
        console.log(".i18n 寫入中...");
        insertEle.each(function({
          // 根據i18n元素的 name 獲取內容寫入
          $(this).html($.i18n.prop($(this).attr('name')));
        });
        console.log("寫入完畢");

        console.log(".i18n-input 寫入中...");
        var insertInputEle = $(".i18n-input");
        insertInputEle.each(function({
          var selectAttr = $(this).attr('selectattr');
          if (!selectAttr) {
            selectAttr = "value";
          };
          $(this).attr(selectAttr, $.i18n.prop($(this).attr('selectname')));
        });
        console.log("寫入完畢");
      }
    });
  }

  /*頁面執行載入執行*/
  $(function(){
    /*執行I18n翻譯*/
    execI18n();
    /*將語言選擇預設選中快取中的值*/
    $("#language option[value="+i18nLanguage+"]").attr("selected",true);
    /* 選擇語言 */
    $("#language").on('change'function({
      var language = $(this).children('option:selected').val()
      console.log(language);
      getCookie("userLanguage",language,{
        expires30,
        path:'/'
      });
      location.reload();
    });
  });
</script>
</html>

如開啟檔案遇到跨域、403、第三方元件需要國際化問題可參考:jquery.i18n國際話及問題整理 - 隔壁丿老蘇

線上示例/原始碼地址

線上示例:點此檢視- jQuery - 多語言翻譯

原始碼地址:https://github.com/Tzlibai/Demo/tree/master/i18n/jquery

Vue - 多語言翻譯

Vue I18n 是 Vue.js 的國際化外掛。它可以輕鬆地將一些本地化功能整合到你的 Vue.js 應用程式中。

由於官方文件表述的已經十分詳細,這裡就不做贅述;

該外掛的解決方案也是基於多個國家配置不同的語言文案,通過對應key動態渲染。

React - 多語言翻譯

i18next 不僅僅是提供標準的 i18n 功能,例如(複數、上下文、插值、格式)。它為您提供了一個完整的解決方案,將您的產品從 Web 本地化到移動裝置和桌面。

由於官方文件表述的已經十分詳細,這裡也就不做贅述;

該外掛的解決方案也是基於多個國家配置不同的語言文案,通過對應key動態渲染。

番外

:lang選擇器

:lang() CSS 偽類基於元素語言來匹配頁面元素。:lang() 偽類選擇器並不那麼出名。但是,此偽類選擇器非常酷,因為即使在元素外部宣告瞭語言,它也可以根據 lang 屬性識別內容的語言。

// html
<div lang="en"><q>This English quote has a <q>nested</q> quote inside.</q></div>
<div lang="fr"><q>This French quote has a <q>nested</q> quote inside.</q></div>
<div lang="de"><q>This German quote has a <q>nested</q> quote inside.</q></div>

// css
:lang(en) > q { color: red; }
:lang(fr) > q { color: blue; }
:lang(de) > q { color: pink; }

參考資料

結尾

好了,以上就是本篇全部文章內容啦。

如果遇到問題或者有其他意見可以在下方評論區貼出!

碼字不易。如果覺得本篇文章對你有幫助的話,希望你可以留言點贊支援一下,非常感謝~

只要有樹葉飛舞的地方,火就會燃燒,火的影子照耀著村子,新的樹葉就會發芽。

相關文章