vueプロジェクトは多言語切り替えの構想を実現します。

7050 ワード

Webプロジェクトの多言語(i 18 n、すなわち国際化)は、一般的なニーズです。
  • 各言語の単独開発ページは、CMSなどのウェブサイト
  • に適用されます。
  • 多言語テキストとページ構造が分離され、実行時に動的に置換されます。単一ページアプリケーション(SPA)
  • に適用されます。
  • は直接ウェブページを使ってプラグインを訳して、機械は訳します。この効果はあまり理想的ではないですが、いくつかの限界があります。
  • 問題
    各案はそれぞれの長所と限界があります。具体的な項目は実際の状況によって選択します。最近仕事で出会った需要は既存のプロジェクトをベースにして、多言語バージョンを迅速に発売することです。プロジェクトはVue.jsに基づいて開発されました。もう多くのバージョンを繰り返しました。実は最初は多言語を計画していましたが、vue-i 18 nプラグインも導入しました。このプラグインは上の第二の案で、JSONファイルで多言語のテキストリソースを管理して、Vueコンポーネントのテンプレートの中でキーを通してテキストを引用します。しかし、これらの英語のキーを管理するのは面倒くさいので、名前をつけるのは大変です。また、コードを読むときもキーから素早く対応する中国語を識別するのは難しいです。後にVSコードに関連するプラグインがあります。対応する中国語を表示できますが、コードを探すのはちょっと面倒です。加えて、製品の多言語バージョンはずっとスケジュールを提示していません。時間が長くなると面倒くさくなります。ゆっくりとテンプレートに中国語を書いていきます。
    結局、来るべきものが来ました。社長が急に英語版を出すと言いました。あとは他の言語があります。最初の考えはChromeブラウザのGoogle翻訳機能を直接使って、どうやって早く来ますか?しかし、テストをして、多くの問題を発見しました。まず、マシンの効果は必ず割引されますが、まだ許容範囲内です。最も重要なのは機能の使用に影響します。どんな問題ですか?プロジェクトはVue.jsで開発された単一ページアプリケーションなので、ページの内容は完全にJSで動的にレンダリングされています。いくつかのダイアログ内のテキストGoogle翻訳は無視されます。また、Google翻訳はDOMテキストノードのみを処理しており、input入力ボックス内の文字(placholderを含む)は無視されている。最も深刻な問題は、Google翻訳処理後のDOM元素が、Vue応答式の特性を失い、データが変化してもDOM内の文字は更新されなくなるということです。
    ブラウザGoogle翻訳のソリューションを継続するなら、これらの問題を解決します。Google翻訳用のJSスクリプトがブラウザVMに組み込まれていることがデバッグで分かりました。HTTPで翻訳サービスを呼び出し、DOM要素を修正します。JSスクリプトは圧縮混淆されています。フォーマット後も見苦しいです。DOMを更新するコードを探して、自分のロジックで上書きしますか?目が見えなくなりました。もういいです。

    以上の理由から、ブラウザが持っているGoogle翻訳プログラムはほとんど考えられなくなりました。
    第二のシナリオしか残っていません。言語設定ファイルとページ構造は分離されています。前に述べましたが、vue-i 18 nの使用は徹底していません。すべての部品を再規格化すれば、仕事量が多すぎます。既存のコードを修正しなくてもテキスト翻訳が可能な方法がありますか?自然とGoogleの翻訳の考えを思い付いて、直接ページに対して結果を誇張して翻訳を行います。自分の翻訳の利点は、DOM操作を精密に制御することができます。例えば、入力ボックスのテキストとplacceholderも翻訳できます。同時に、研究の結果、VueコンポーネントがデータバインディングによってレンダリングされたDOM要素は、テキストの内容がinnerHTMLまたはinnerTextによって直接修正できないため、応答式が無効になることがわかった。解決策は、そのサブ要素、すなわちテキストノード(nodeTypeは3のノード)を操作して、そのtextConteet属性を修正することである。
    マルチ言語設定マップ
    Google翻訳と違って、私達は静的な翻訳を採用しています。つまり、多言語構成ファイルを通してマッピングします。vue-i18n は各言語にJSONファイルを用意し、属性名は英語で、名前空間(マルチレベルオブジェクト)で命名衝突を回避する。私は直接簡略化しました。JSオブジェクトですべての言語バージョンを保存します。キーの名前はページで使う中国語です。これらの中国語は日々積み重ねられた開発反復によって数百の文書に散在しています。私のやり方はVS Codeグローバル正規検索を使って検索結果をコピーし、JS方法を書いてこれらの文字列をJSオブジェクトに処理します。

    中国語の正則にマッチします。
    [A-Z]*[/u 4 e 00-\u 9 fa5][、!0-9 a-zA-Z\u 4 e 00-\u 9 fa 5*。
    結果を翻訳ツールにコピーして翻訳し、関数を書いてこれらのテキストをオブジェクトに統合し、labels.jsファイルに保存します。
    
    var kv = dist.reduce((acc,cur, index) => {
    acc[cur]=en[index] || cur;return acc;
    },{})
    オブジェクトの構造は大体以下の通りです。
    
    // labels.js
    export default {
         : {
      en: 'Customer Name',
     },
     //     ,     
     '  {0}      ': {
      en: '{0} unregistered',
     },
     xxxx: {
      en: 'XXX',
     }
    }
    操作DOM
    Google翻訳と似ています。私たちも後でDOMを更新する方式で通訳します。単一ページアプリケーションなので、ユーザの操作に伴って、DOMはどんどん更新されます。最初の考えは全体のbodyの変化を监视して、回调の中でまたDOMを更新します。モニターDOMの変化には、元のAPIがあります。MuttionObserverです。
    
    mounted() {
     this.observeDOM(document.body);
    },
    methods: {
     observeDOM(el) {
      let mutationTimer;
      const vm = this;
      const observer = new MutationObserver(() => {
       //     debounce    ,         
       clearTimeout(mutationTimer);
       mutationTimer = setTimeout(() => {
        if (!vm.mutationFromTrans) {
         translate();
         vm.mutationFromTrans = true;
         setTimeout(() => {
          vm.mutationFromTrans = false;
         }, 300);
        }
       }, 100);
      });
      const options = {
       childList: true, //   node        
       subtree: true, //   node       
       attributes: true, //   node     
       characterData: true, //                            。
      };
      if (this.language === 'en') {
       observer.observe(el, options);
      }
     },
    }
    しかし、試してみると、これは無線循環を引き起こし、DOMの変化はユーザ操作か翻訳そのものかを判断していないからです。だからコードの中に判断を入れましたが、結果はまだよくないです。このような操作の価格は大きすぎて、ページの性能は大きな影響を受けました。そしてもう一つの明らかな問題があります。新しいインターフェースに入るとフラッシュして、中国語から英語になります。この体験は大変でした。後に改善の方法があります。
    翻訳
    まず通訳の過程を見に来ました。翻訳とは、多言語構成オブジェクトから該当する属性名を検索し、対応する言語の属性値を取得することです。これは静的なテキストにとっては比較的簡単で、直接属性名を使えばいいです。動態的なテキストはどう処理しますか?中国語と英語の表現が違っているので、このようなテキストは簡単に複数の部分に分割して単独で処理するのではなく、英語の表現で動的なデータを置換します。私のやり方は書式付きのキーを使って、{0}のようなプレースホルダを使います。検索するときは、固定テキストと優先的にマッチします。ほとんどの場合は固定テキストであり、しかもこのマッチングはO(1)時間の複雑度であり、優先判断は性能を向上させる。マッチングに失敗した時に、事前に作成された正規のリストを巡回しました。成功すれば、正規のマッチングしたグループを抽出して、ダイナミックデータを交換します。もし失敗したら、対応する翻訳がないと説明して、直接元の文字列を返してもいいです。
    
    const keys = Object.keys(words);
    //       ,          
    const regExps = keys.reduce((acc, key) => {
     //      
     if (key.indexOf('{0}') > -1) {
      const reg = new RegExp(key.replace('{0}', '(.+)'));
      acc.push({
       expression: reg,
       key,
      });
     }
     return acc;
    }, []);
    export function translate(el = document.body, lang = 'en') {
     const kv = words;
     if (!el.querySelectorAll) {
      return;
     }
     const _trans = label => {
      const text = label?.trim?.();
      if (!text) {
       return label;
      }
      if (kv[text]?.[lang]) {
       return kv[text]?.[lang];
      }
      for (let index = 0; index < regExps.length; index++) {
       const regItem = regExps[index];
       const m = text.match(regItem.expression);
       if (m) {
        return kv[regItem.key][lang].replace('{0}', m[1]);
       }
      }
      return text;
     };
     [...el.querySelectorAll('*')].forEach(node => {
      //       node.innerText,   Vue     
      // node.innerText = kv[node.innerText?.trim?.()] || node.innerText;
      if (node.nodeName === 'INPUT' && node.type === 'text') {
       node.value = _trans(node.value);
       node.placeholder = _trans(node.placeholder);
      }
      const textNodes = [...node.childNodes].filter(n => n.nodeType === 3);
      textNodes.forEach(textNode => {
       textNode.textContent = _trans(textNode.textContent);
      });
     });
    }
    改善後のDOM操作
    前に述べたように、DOMレンダリング後に翻訳を実行すると、ページの性能が非常に悪いです。そこで、Vue自身のレンダリングプロセスを思い出しました。Vueコンポーネントのレンダリングプロセスをブロックして、いくつかの追加のロジックを挿入してもいいですか?ソースをすりますを通じて(通って)発見して、Vue原型の上に〓〓があります。patchu方法は、DOMを更新するたびに実行されます。ここから始めて、この方法を書き直して、まだ文書ツリーにマウントされていないDOM元素に対して翻訳操作を行います。
    
    const __patch__ = Vue.prototype.__patch__;
    Vue.prototype.__patch__ = function() {
     const elm = __patch__.apply(this, arguments);
     if (this.$store?.getters?.language) {
      translate(elm, this.$store?.getters?.language);
     }
     return elm;
    };
    これで、多言語翻訳がほぼ完成しました。吟味した結果、この案は比較的手間が省けて、また需要が完成できるようになりました。もちろん、このような方案は多かれ少なかれページの性能に対して一定の影響があって、結局DOM更新の時間を増加しました。特に動的なテキストが多い場合は、正則を遍歴してマッチングし、比較的時間がかかります。もっといい案があれば、メッセージをください。
    以上がevueプロジェクトの多言語切り替えを実現する構想の詳細です。vueプロジェクトの多言語切り替えに関する資料は他の関連記事に注目してください。