LinkedInが1泊で19言語にローカライズする方法

「プログラマーがインターフェイスに新しい行を追加した後、彼女は19の言語に翻訳し、SVNに身を置き、午前中にリリースの準備ができていた」と、外国語への製品ローカリゼーションの禁断の成果を味わった開発者の夢です。 アルコノストでこの夢を実現させないようにし、少なくともそれに近づけるようにしています。 はい、この記事で説明したものと同様のソリューションは、LinkedIn開発者だけでなく、単なる人間にも存在します。



LinkedInでのプロセスの構築方法について-この記事(注意-Java)。









国際化はLinkedInの運用と開発に重大な影響を及ぼします。LinkedInは現在19の言語で利用可能です。 ローカライズされたテキストの作業を高速化するために、国際エンジニアリングチームは、翻訳されたコンテンツ文字列を作業サービスに動的に導入するシステムを開発しました。



新しいシステムにより、コンテンツを迅速に変更できます。開発者を巻き込むことなく、迅速な編集と変更を行い、サービスを再構築して再起動します。



はじめに



すべてのコンテンツは、最初に開発者と製品マネージャーによって英語で書かれています。 通常、翻訳が必要なテキストはプロパティファイルに含まれています。



add_to_network__send_invitation=Send invitation add_to_network__cancel=Cancel add_to_network__add_message=Add a personal message add_to_network__user_message=I'd like to add you to my professional network.\n\n- {0}
      
      





その後、当社のフルタイムローカリゼーションチームがコンテンツを異なる言語に翻訳します。 たとえば、同じプロパティファイルがイタリア語に翻訳されています。



 add_to_network__send_invitation=Invia un invito add_to_network__cancel=Annulla add_to_network__add_message=Aggiungi un messaggio personale add_to_network__user_message=Vorrei aggiungerti alla mia rete professionale.\n\n\n\n-{0}
      
      





ページにテキストを表示するには、指定されたキーを使用してユーザーロケールのプロパティファイルにアクセスするテンプレートの国際化機能を使用します。



 <form> <label for="message">${i18n('add_to_network__add_message')}</label> <textarea id="message">${i18n('add_to_network__user_message', fullname)}</textarea> <input type="submit" value="${i18n('add_to_network__send_invitation')}"/> <input type="button" value="${i18n('add_to_network__cancel')}"/> </form>
      
      





古いように



動的言語ロードを実装する前に、システムはすべてのプロパティファイルをアプリケーションコード(WAR)とともに1つのアーティファクトに収集しました。 これにより、特定の問題が発生しました。



  1. 翻訳を追加するということは、サービス全体を再構築して再起動することを意味しました。
  2. 翻訳エラーが発生した場合、単に古いバージョンのテキストに戻すことは不可能でした。アプリケーションコードを前のバージョンにロールバックする必要がありました。
  3. 翻訳は多くのサービスで使用できます。その場合、すべての翻訳を再構築して再起動する必要がありました。


新しい方法で



新しいシステムは、アプリケーションコードとは別にプロパティファイルを収集してアップロードします。 言語パックの概念を導入しました-これは、特定の言語のすべての翻訳されたコンテンツを含むJARファイルです。 このような言語パックの更新バージョンは、いつでもWebサーバーにアップロードできます。 エラーが検出された場合、いつでもロールバックできます。



新しいリソースパックライブラリを追加しました。これは、新しい言語パックの可用性を決定し、更新された翻訳の使用を開始します。これらすべては、サービスを再起動することなく行われます。 ライブラリが翻訳を見つけられない場合、元の英語の文字列を使用します。



プロセス



新しい翻訳の導入は全体像の一部に過ぎません。また、新しく新しく変更された行をすばやく見つけて翻訳者に配信し、翻訳の結果を言語パックに含める方法を見つける必要もありました。 これは私達のプロセスがどのように見えるかです:



画像

  1. エンジニアは、新しいまたは更新された英語の文字列をバージョン管理システムに送信します。
  2. ローカリゼーションサーバーは、バージョン管理システムの変更を1日に1回スキャンし、新しい行と変更された行をすべて翻訳する要求を発行します。
  3. 1時間に1回、ローカリゼーションサーバーは既製の翻訳を収集します。 彼は新しいコンテンツを確認し、リポジトリ内の特定の言語のすべての翻訳を含む完全な言語パックを公開します。
  4. 実装システムは、1時間に1回、更新された言語パックをテスト用に送信し、1日に2回、稼働中のサービスにロードします。
  5. ローカリゼーションチームが翻訳を緊急に変更する必要がある場合は、ワンクリックでいつでも手動で翻訳を読み込むことができます。


下位互換性



動的言語ロードのシステムはローカライズされたテキストをコードから分離するという事実により、それらを使用するアプリケーションコードよりも前に、作業中のサービスに新しいコンテンツの行をレイアウトすることが可能になりました。 したがって、すべての国際化リソースは以前のバージョンと後方互換性があり、新しい行を導入するときに何も中断しないようにする必要があります。



このコンテキストでは、後方互換性とは、新しい行を追加することが常に安全であることを意味しますが、既存の行を変更する場合は、変数の数とタイプを変更しないでください。 たとえば、最初は次の行がありました。



 accept_invite__hello_connect=Hello {0}, would you like to connect with {1}?
      
      





いくつかの単語は簡単に変更できます。



 # This change is backwards compatible accept_invite__hello_connect=Hi {0}, would you like to add {1} to your professional network?
      
      





ただし、変数のタイプを削除、追加、または変更すると、下位互換性に違反します。これは、アプリケーションコードが以前の変数の値のみを提供するためです。



 # This change is NOT backwards compatible! accept_invite__hello_connect=Hello {0}, would you like to connect with {1}, a coworker at {2}?
      
      





コミットする前にコードを実行することにより、後方互換性を実現します。これにより、リソースの削除を防ぎ、同時に既存のテキストリソースの更新が変数セットと互換性があることを確認します。 以下のコードスニペットは、検証ロジックの一部を示しています。



 /** * Verify that translation isn't missing indexes used with the * original as well as does not specify additional indexes not * present in the original. */ private boolean verifyPlaceholderIndexMatch(String originalMessageTemplate, PlaceholderInfo originalPlaceholderInfo, PlaceholderInfo placeholderInfo) { if(originalPlaceholderInfo.keySet().equals(placeholderInfo.keySet()) == false) { // remove all index numbers in the translation from the index // set in the original to determine what's missing in the // translation Set<String> missingIndexSet = new HashSet<String>(originalPlaceholderInfo.keySet()); missingIndexSet.removeAll(placeholderInfo.keySet()); // remove all index numbers in the original from the set of // indexes in the translation to determine what extra index // we have in the translation Set<String> extraIndexSet = new HashSet<String>(placeholderInfo.keySet()); extraIndexSet.removeAll(originalPlaceholderInfo.keySet()); return false; } return true; }
      
      





19の言語が先に導入されています-その他すべて!



新しいシステムは、LinkedInの翻訳プロセスを加速しました。新しい行を実際のサービスに取り込むことがはるかに速く簡単になり、必要に応じて翻訳を徐々にロールアウトし、ロールバックする機会を得ました。 そして最も重要なことは、私たちの国際化システムは、増え続けるアプリケーション、言語、参加者に合わせて拡張できることです。





翻訳者について



この記事はAlconostによって翻訳されました。



Alconostは、60の言語でアプリケーション、ゲーム、およびサイトをローカライズします 。 ネイティブ翻訳者、言語テスト、APIを備えたクラウドプラットフォーム、継続的なローカリゼーション、プロジェクトマネージャー24時間365日、あらゆる形式の文字列リソース。



また、Google PlayとApp Storeの販売、画像、広告、教育、ティーザー、エクスプライナー、予告編のサイト向けに、 広告および教育用ビデオを作成しています。



詳細: https : //alconost.com






All Articles