機械翻訳について

13 Oracle JETアプリケーションの国際化およびローカライズ

Oracle JETには、Oracle JETアプリケーションでの国際化(I18N)、ローカライゼーション(L10N)、およびOracle National Language Support (NLS)翻訳バンドルの使用のサポートが含まれます。 Oracle JETアプリケーションを様々なロケールおよび国際的なユーザー環境で使用できるように構成します。

Oracle JETアプリケーションの国際化およびローカライズについて

国際化(I18N)とは、簡単でコスト効率よく、とりわけソフトウェアの技術的な変更をせずに、ソフトウェアを様々な言語および地域に適合できるようにするためのソフトウェア設計プロセスのことです。 ローカリゼーション(L10N)とは、実行時にロケール固有の言語および構成を使用することです。

オラクル社では、業界標準のI18NおよびL10N (World Wide Web Consortium (W3C)勧告、Unicode技術、インターネット技術タスク・フォース(IETF)仕様など)を採用して、世界の様々な言語、記述システムおよび地域規則をサポートしています。 言語およびロケールは標準言語タグで識別され、BCP 47の定義に従って処理されます。 Oracle JETには、次の表に示す言語に対するOracle National Language Support (NLS)の翻訳サポートが含まれています。

Language 言語タグ

アラビア語

ar

ポルトガル語(ブラジル)

pt

ブルガリア語 bg-BG

フランス語(カナダ)

fr-CA

中国語(簡体字)

zh-Hans(またはzh-CN)

中国語(繁体字)

zh-Hant(またはzh-TW)

クロアチア語

hr

チェコ語 cs

デンマーク語

da

オランダ語

nl

エストニア語

et

フィンランド語

fi

フランス語

fr

ドイツ語

de

ギリシャ語

el

ヘブライ語

he

ハンガリー語

hu

アイスランド語 is

イタリア語

it

日本語

ja

韓国語

ko

セルビア語(ラテン) sr-Latn

ラトビア語

lv

リトアニア語

lt

マレー語 ms-MY

ノルウェー語

no

ポーランド語

pl

ポルトガル語

pt-PT

ルーマニア

ro

ロシア語

ru

セルビア語 sr

スロバキア語

sk

スロベニア語 sl

スペイン語

es

スウェーデン語

sv

タイ語

th

トルコ語

tr

ウクライナ語 uk-UA
ベトナム語 vi

Oracle JETの翻訳はリソース・バンドルに格納されます。 独自の翻訳をバンドルに追加できます。 詳細は、「Oracle JETアプリケーションへのRequireJS翻訳バンドルの追加」を参照してください。

Oracle JETには、196以上のロケールの書式設定サポートも含まれています。 Oracle JETのロケール要素はUnicode Common Locale Data Repository (CLDR)に基づき、ロケール・バンドルに格納されます。 Unicode CLDRの詳細は、http://cldr.unicode.orgを参照してください。 サポートされているロケール・バンドルは、Oracle JET配布の次の場所にあります。

js/libs/oj/19.0.0/resources/nls

ページで使用するロケールを決定するのはアプリケーションの役目です。 一般的に、アプリケーションでは、ブラウザのロケール設定からサーバー側で計算したり、アイデンティティ・ストアに格納されているユーザー・ロケール・プリファレンスおよびアプリケーションでサポートされている翻訳言語を使用して、ロケールを決定します。

ロケールが決定したら、ロケール依存操作(リソース・バンドルのロード、日時データの書式設定など)のために、アプリケーションでこのロケールをOracle JETに通知する必要があります。 Oracle JETでは、ロケール依存操作用のロケールを次の順序で決定します:

  1. RequireJS構成のojL10nプラグインでのロケール指定。

  2. htmlタグのlang属性。

  3. navigator.languageブラウザ・プロパティ。

アプリケーションでユーザーがロケールを動的に変更するオプションが提供されない場合は、lang属性をHTMLタグに設定することをお薦めします。これを設定することで、Oracle JETのロケールを設定するのに加えて、すべてのHTML要素のロケールも設定されるためです。 Oracle JETでは、現在の言語の翻訳バンドル、および設定されているロケールのロケール・バンドルが自動的にロードされます。 ロケールを設定しない場合、Oracle JETではブラウザ・プロパティにデフォルト設定します。 ただし、アプリケーションでロケールを動的に変更するオプションが提供されている場合は、アプリケーションでRequireJSが使用されていれば、ojL10nプラグインでロケール仕様を設定することをお薦めします。 Oracle JETは、アプリケーションの初期化時にロケールおよびリソース・バンドルを自動的にロードします。

Oracle JETアプリケーションのモジュール・バンドルとしてRequireJSではなくWebpackを使用する場合は、サポートするロケールごとに1つのコード・バンドルを生成し、サポートするロケールの異なるURLに各バンドルをデプロイすることをお薦めします。 たとえば、アプリケーションURLがhttps://www.oracle.com/index.htmlで、フランス語とスペイン語のロケールをサポートする場合は、これらのロケールのバンドルをそれぞれhttps://www.oracle.com/fr/index.htmlおよびhttps://www.oracle.com/es/index.htmlにデプロイします。

最後に、Oracle JETには、ロケール・バンドルを使用するバリデータおよびコンバータが含まれています。 ページ上のロケールを変更する場合、Oracle JETコンポーネントには新しいロケールでコンテンツを表示するためのサポートが組み込まれています。 Oracle JETのバリデータおよびコンバータの詳細は、「入力の検証および変換」を参照してください。

Oracle JETアプリケーションの国際化およびローカライズ

Oracle JETの国際化およびローカリゼーションの組込みサポートを使用するようにアプリケーションを構成します。

Oracle JETの国際化およびローカリゼーション・サポートの使用

Oracle JETの国際化およびローカリゼーションの組込みサポートを使用するには、ページのhtml要素のlang属性に、サポートされている言語またはロケールの1つを指定します。 たとえば、次の設定によって、言語がフランス語(フランス)ロケールに設定されます。

<html lang="fr-FR">

フランス語(カナダ)ロケールを指定する場合は、かわりに次のように指定します。

<html lang="fr-CA">

ヒント:

locale指定は大文字と小文字が区別されません。 Oracle JETでは、FR-FRfr-frなども受け入れて、正しいリソース・バンドル・ディレクトリにマップします。

このようにロケールを指定すると、ページ上のOracle JETコンポーネントは指定された言語で表示され、ロケールに適したロケール構成が使用されます。

ロケールに関連付けられたリソース・バンドルが存在しない場合、Oracle JETでは次に該当する言語バンドルがロードされます。 次に該当する言語のバンドルがOracle JETに存在しない場合は、デフォルトのルート・バンドルが使用されます。 たとえば、Oracle JETにfr-CAの翻訳バンドルが存在しない場合は、frリソース・バンドルが検索されます。 frバンドルが存在しない場合、Oracle JETではデフォルトのルート・バンドルが使用され、文字列が英語で表示されます。

次の図では、oj-input-date-timeコンポーネントを使用してページが構成されています。 画像は、lang属性をfr-FRに変更した結果を示しています。

oj-input-date-timeフィールドに誤った値を入力すると、エラー・テキストが指定の言語で表示されます。 この例では、エラーがフランス語で表示されます。

Oracle JETでの双方向(BiDi)サポートの有効化

アラビア語やヘブライ語のように、指定した言語でデフォルトの左から右(LTR)方向ではなく右から左(RTL)方向を使用する場合は、htmlタグにdir属性を指定する必要があります。

<html dir="rtl">

次の画像は、アラビア語(エジプト)の言語コードを指定してdir属性をrtlに変更すると表示されるoj-input-date-timeフィールドを示しています。

Oracle JETアプリケーションでBiDiサポートを有効にした場合は、アプリケーションが希望のレイアウトで適切に表示され、文字列が想定どおりにレンダリングされることを確認する必要があります。

ノート:

個々のHTML要素にdir属性を設定するとページが両方の方向で表示されるため、Oracle JETではサポートしていません。 また、ページが初期化された後にdir属性をプログラムによって変更する場合は、ページをリロードするか、または各JETコンポーネントをリフレッシュする必要があります。

ロケールと方向の動的設定

アプリケーションURLのリロード時にアプリケーションのRequireJS ojL10nプラグインが読み取るアプリケーションのローカル・ストレージにキー値のペアを設定することで、そのロケールおよび方向を動的に変更するようにアプリケーションを構成できます。

次の画像は、クリックすると部門リストが表示されるメニューおよび日付ピッカーを表示するように構成された、Oracle JETアプリケーションを示しています。 デフォルトでは、アプリケーションはen-USロケールに設定されます。 メニューおよび日付ピッカーは英語で表示されています。

JET-Localization.ZIPをダウンロードし、ZIPファイルを抽出するディレクトリでOracle JET CLI ojet restoreおよびojet serveコマンドを実行する場合は、イメージに示されているOracle JETアプリケーションを実行できます。

アプリケーションには、米国、フランス、チェコ共和国およびエジプトの国旗が表示されたボタン群も含まれています。 ユーザーがフラグの1つをクリックすると、アプリケーション・ロケールはそのフラグによって表されるロケール(en-USfr-FRcs-CZ,またはar-EG)に設定されます。

ノート:

この例で使用されている国旗は説明目的でのみ使用されています。 1つの国で複数の言語が使用されていたり、ある言語が複数の国々で使用される場合があるため、国旗を使用してUI言語を選択することはお薦めできません。 実際のアプリケーションでは、国旗アイコンのかわりに、優先言語を示すクリック可能なテキストを使用できます。

次の画像は、ユーザーがエジプトの国旗をクリックした後に更新されたページを示しています。

この動作を実装するには、アプリケーションのビュー、viewModelおよびappRootDir/src/main.jsファイルを変更する必要があります。 アプリケーションのビュー・コードで、on-value-changedプロパティ変更リスナー属性は、ユーザーが選択したボタンを変更したときにコールされるsetLang関数を指定します。

<oj-buttonset-one . . . on-value-changed="[[setLang]]">

アプリケーションのviewModelコードでは、このsetLang関数は、ユーザーが選択したロケールを判断し、ユーザーの選択がブラウザ・セッション全体で保持されるようにwindow.localStorageにエントリを設定します。 関数の最後のステップは、location.reload()メソッドを使用して現在のURLをリロードすることです。

setLang = (evt) => {
    let newLocale = "";
    let lang = evt.detail.value;
    switch (lang) {
      case "Čeština":
        newLocale = "cs-CZ";
        break;
      case "Français":  
        newLocale = "fr-FR";
        break;
      case "عربي":
          newLocale = "ar-EG";
          break;        
      default:
        newLocale = "en-US";
    }
    window.localStorage.setItem('mylocale',newLocale);
    window.localStorage.setItem('mylang',lang);
    location.reload();
  };

新しく選択したロケールをアプリケーションのappRootDir/src/main.jsファイルのojL10nプラグインで設定するには、ローカル・ストレージから更新されたロケール値を読み取ってojL10nプラグインのlocale指定で設定する次のエントリを記述します。 また、指定されたロケールがアラビア語(エジプト)(ar-EG)の場合、方向をrtlに設定するチェックも含まれます。

(function () {
  ...
const localeOverride = window.localStorage.getItem("mylocale");
  if (localeOverride) {  
    // Set dir attribute on <html> element.
    // Note that other Arabic locales and Hebrew also use the rtl direction. 
    // Include a check here for other locales that your app must support.
    if(localeOverride === "ar-EG"){
      document.getElementsByTagName('html')[0].setAttribute('dir','rtl');
    } else {
      document.getElementsByTagName('html')[0].setAttribute('dir','ltr');
    }
    requirejs.config({
      config: {
        ojL10n: {
          locale: localeOverride,
        },
      },
    });
  }
})();
...

独自の翻訳文字列の定義およびOracle JET翻訳バンドルへの追加の詳細は、使用する翻訳バンドルのタイプに応じて、「Oracle JETアプリケーションへのRequireJS翻訳バンドルの追加」または「ICU翻訳バンドルのOracle JET Virtual DOMアプリケーションへの追加」を参照してください。

このアプローチを使用してアプリケーションを国際化およびローカライズする場合は、ページ上のすべてのコンポーネントおよび要素を検討し、必要な場所に翻訳文字列を指定する必要があります。 ページに多数の翻訳文字列が含まれる場合は、ページのパフォーマンスが低下する可能性があります。

また、アプリケーションでSEO(検索エンジン最適化)が重要な場合、通常は検索エンジンでJavaScriptは実行されず、静的テキストにのみアクセスすることに注意してください。

ヒント:

パフォーマンスやSEOの問題を回避するために、対象の言語にすでに翻訳されたページをアプリケーションに追加できます。 すでに翻訳されたページを使用すると、Knockoutのバインディングは実際に動的な場所に対してのみ実行されます。

通貨、日付、時間および数値の使用

Oracle JETに付属するコンバータを使用すると、ロケール設定に基づいて、日付、時間、数値および通貨が自動的に変換されます。 また、Oracle JETコンバータがアプリケーションにとって不十分な場合は、カスタム・コンバータを提供できます。 Oracle JETコンバータの詳細は、「Oracle JETコンバータについて」を参照してください。アプリケーションへのカスタム・コンバータの追加の詳細は、Oracle JETでのカスタム・コンバータの使用を参照してください。

Oracle JET RequireJS翻訳バンドルの操作

Oracle JETには、Oracle JETコンポーネントによって生成された文字列をサポートされているすべての言語に変換するRequireJS翻訳バンドルが含まれています。 独自のRequireJS翻訳バンドルをOracle JETのRequireJS翻訳バンドルとマージして追加します。

ノート:

Oracle JETアプリケーションで初めて翻訳バンドルを作成する場合は、Oracle JETでサポートされている他のタイプの翻訳バンドル(ICU翻訳バンドル)を使用することをお薦めします。 詳細は、Oracle JET仮想DOMアプリケーションでのICU翻訳バンドルの操作を参照してください。

Oracle JETの翻訳バンドルについて

Oracle JETには、Oracle JETコンポーネントで生成された文字列を、サポートされているすべての言語に翻訳する翻訳バンドルが含まれています。 独自の翻訳バンドルは、Oracle JETで使用される書式と同じ書式に従って追加できます。

Oracle JETの翻訳バンドルは、コンテンツやディレクトリ・レイアウトに対して指定された書式に従いますが、大文字/小文字および特定の文字に関してはある程度の寛大さが許容されます。

翻訳バンドルの場所

ojtranslations.jsという名前のOracle JET翻訳バンドルの場所は、次のディレクトリにあります:

libs/oj/v19.0.0/resources/nls/ojtranslations

サポートされている各言語は、nlsディレクトリ下にあるディレクトリに格納されます。 ディレクトリ名は次の規則に準拠します。

  • 言語サブタグは小文字(zhsrなど)

  • スクリプト・サブタグはタイトル・ケース(HantLatnなど)

  • 地域サブタグは大文字(HKBAなど)

言語、スクリプトおよび地域サブタグは、ハイフン(-)で区切られます。 次の図は、ディレクトリ構造の一部を示しています。

最上位レベル・モジュール

ojtranslations.jsファイルにはOracle JETで翻訳される文字列が格納され、翻訳がある言語がリストされます。 これは、最上位レベル・モジュール、つまりルート・バンドルです。 ルート・バンドルでは、文字列は英語で、ユーザーの優先言語の翻訳が使用できない場合の実行時デフォルト値です。

翻訳バンドルの書式

Oracle JETでは、最上位レベルのルート・バンドルおよび翻訳が指定の書式に準拠する必要があります。 ルート・バンドルには、Oracle JET文字列とデフォルト翻訳、および翻訳があるロケールのリストが格納されています。

define({
// root bundle
  root: {
    "oj-message":{
        fatal:"Fatal",
        error:"Error",
        warning:"Warning",
        info:"Info",
        confirmation:"Confirmation",
        "compact-type-summary":"{0}: {1}"
     },
     // ... contents omitted
  },

// supported locales.       
  "fr-CA":1,
   ar:1,
   ro:1,
   "zh-Hant":1,
   nl:1,
   it:1,
   fr:1,
   //  ... contents omitted
   tr:1,fi:1
});

文字列はネストされたJSONオブジェクトに定義されるため、各文字列は、oj-message.fataloj-message.errorなどの接頭辞がある名前で参照されます。

言語翻訳リソース・バンドルには、Oracle JET文字列定義と翻訳済文字列が格納されています。 たとえば、次のコード・サンプルは、nls/fr-CA/ojtranslations.jsに格納されているフランス語(カナダ)翻訳リソース・バンドルの一部を示しています。

define({
  "oj-message":{
     fatal:"Fatale",
     error:"Erreur",
     warning:"Avertissement",
     info:"Infos",
     confirmation:"Confirmation",
     "compact-type-summary":"{0}: {1}"
     },
    //  ... contents omitted
});

ユーザーの方言で使用可能な翻訳がない場合は、基本言語バンドルの文字列が表示されます。 ユーザーの優先言語の翻訳がない場合は、ルート言語バンドルである英語が表示されます。

名前付きメッセージ・トークン

メッセージによっては、含まれる値が実行時まで決定しない場合があります。 たとえば、"User foo was not found in group bar"というメッセージ内のfooユーザーおよびbarグループは実行時に決定します。 この場合は、次のコードに示すように、{username}{groupname}を名前付きメッセージ・トークンとして定義できます。

"MyUserKey":"User {username} was not found in group {groupname}."

実行時に、1番目の引数としてメッセージのキーが挿入され、2番目の引数としてパラメータが翻訳済パターンに挿入されてTranslations.applyParameters()メソッドがコールされ、メッセージ内のトークンの位置で実際の値に置換されます。

let parMyUserKey = { 'username': 'Foo', 'groupname': 'Test' };
let tmpString = Translations.applyParameters(MenuBundle.MyUserKey, parMyUserKey);
this.MyUserKey = Translations.getTranslatedString(tmpString);

数値メッセージ・トークン

名前付きトークンのかわりに、数値トークンを定義することもできます。 たとえば、"This item will be available in 5 days"というメッセージ内の数値5は実行時に決定します。 この場合は、次のコードに示すように、メッセージ・トークンの{0}を使用してメッセージを定義できます。

"MyKey": "This item will be available in {0} days."

1つのメッセージに最大10個の数値トークンを含めることができます。 たとえば、"Sales order {0} has {1} items"というメッセージには2つの数値トークンが含まれています。 ターゲット言語の文法で必要な場合は、翻訳時にトークンの順序が変更され、翻訳済文字列内でメッセージ・トークン{1}がメッセージ・トークン{0}の前に表示される場合があります。 applyParameters()およびgetTranslatedString()メソッドをコールするコードは、翻訳済文字列内でトークンの順序が変更されていても同じままです。

ヒント:

読みやすさおよび再利用のためには、数値トークンではなく名前付きトークンを使用してください。

リソース・バンドル文字列内のエスケープ文字

ドル記号、中カッコおよび角カッコを出力に表示する場合は、エスケープする必要があります。 次の表に示すように、文字の前にドル記号($)を追加します。

エスケープされたフォーム 出力
$$ $
${ {
$} }
$[ [
$] ]

たとえば、出力に[Date: {01/02/2020}, Time: {01:02 PM}, Cost: $38.99, Book Name: JET developer's guide]と表示する場合は、リソース・バンドル文字列に次のように入力します。

"productDetail": "$[Date: ${01/02/2020$}, Time: ${01:02 PM$}, Cost: $$38.99, Book Name: {bookName}$]"

さらに、次の例に示すように、Translations.applyParameters()メソッドを使用して、エスケープされた文字と置換トークン(ある場合)を含む文字列を返してUIに表示します:

let parProductDetail = { bookName: "JET developer's guide"};
this.productDetail = Translations.applyParameters(MenuBundle.productDetail, parProductDetail);

翻訳済文字列の書式設定

状況によっては、UIに表示されるリソース・バンドルの文字列に書式設定を適用できます。 たとえば、HTML出力でブック・タイトルがイタリックを使用してレンダリングされるように、<i>タグを適用するブック・タイトルについて検討します。 このシナリオでは、リソース・バンドルに次のエントリを定義できます:

// root bundle
"FormatTranslatedString": "The <i>{booktitle}</i> describes how to develop Oracle JET apps"

その後、次の例のように、Oracle JETのoj-bind-dom要素を使用してUIの文字列をレンダリングします:

<p><span>
  <oj-bind-dom config="{{ formatTranslatedString() }}"></oj-bind-dom>
</span></p>

注意:

oj-bind-dom要素は、整合性またはセキュリティ違反について、アプリケーションで提供されるHTML入力を検証しません。 安全でないコンテンツがページに追加されないように入力をサニタイズするのは、アプリケーションの責任です。

このviewModelでは、Oracle JETのHtmlUtilsユーティリティ・クラスを使用して、リソース・バンドルからの文字列を解析します。

. . .
import * as HtmlUtils from "ojs/ojhtmlutils";
import "ojs/ojbinddom";
. . .
class DashboardViewModel {
 . . .
  FormatTranslatedString: String;
 . . .
  formatTranslatedString = () => {

    var parBookTitle = { 'booktitle': 'Oracle JET Developer Guide' };
    let strTitle = Translations.applyParameters(MenuBundle.FormatTranslatedString, parBookTitle);
    this.FormatTranslatedString = Translations.getTranslatedString(strTitle);
   
    return {
      view: HtmlUtils.stringToNodeArray(
        `<span>${this.FormatTranslatedString}</span>`,
      ),
    };
   };
. . .

Oracle JETアプリケーションへのRequireJS翻訳バンドルの追加

アプリケーションUIで必要なカスタム文字列およびアプリケーションでサポートする翻訳を使用して、Oracle JETアプリケーションに翻訳バンドルを追加できます。

Oracle JETに翻訳バンドルを追加するには:

  1. 翻訳を定義します。

    たとえば、次のコードは、ボタン・ラベルと3つのメニュー項目を含むメニューの翻訳セットを定義します。 デフォルト言語は英語に設定され、デフォルトのラベルとメニュー項目は英語で表示されます。 ファイル内のrootオブジェクトがデフォルトのリソース・バンドルです。 その他のプロパティに、サポートされているロケールとしてfrcsおよびarがリストされています。

    define({
      "root": {
        "label": "Select a department",
        "menu1": "Sales",
        "menu2": "Human Resources",
        "menu3": "Transportation"
    },
      "fr": true,
      "cs": true,
      "ar": true
    });

    リソース名に接頭辞を追加するには(たとえば、department.labeldepartment.menu1)、次に示すように接頭辞をバンドルに追加します。

    define({
      "root": {
        "department": {
          "label": "Select a department",
          "menu1": "Sales",
          "menu2": "Human Resources",
          "menu3": "Transportation"
        }
      }
    },
      "fr": true,
      "cs": true,
      "ar": true
    });

    ロケールがフランス語ロケールに設定されている場合は、フランス語の翻訳バンドルがロードされます。 次のコードは、フランス語でのラベルとメニュー項目の定義を示しています。

    define({
      "label": "Sélectionnez un département",
      "menu1": "Ventes",
      "menu2": "Ressources humaines",
      "menu3": "Transports"
    })

    地域の方言に必要な内容を定義することによって、基本言語バンドルに対してその方言を指定することもできます。

    define({
        "label": "Canadian French message here"
    });

    ユーザーの方言で使用可能な翻訳がない場合は、基本言語バンドルの文字列が表示されます。 この例では、フランス語の翻訳を使用してメニュー項目が表示されます。 ユーザーの優先言語の翻訳がない場合は、言語に関係なくルート言語バンドルが表示されます。

  2. nlsという名前のディレクトリにあるファイル内に各定義を格納します。

    たとえば、前のステップのデフォルト翻訳は、appRootDir/src/ts/resources/nlsディレクトリにあるmenu.jsという名前のファイルに格納されます。 サポートされている翻訳は、ロケールの名前を使用する子サブディレクトリ内のmenu.jsという名前のファイルにあります:

    appRootDir/src/ts/resources/nls
    |   menu.js
    |
    +---ar
    |     menu.js
    |
    +---cs
    |     menu.js
    |
    +---fr
          menu.js

    例では、Oracle JETアプリケーションがTypeScriptベースのアプリケーションであると想定しているため、ディレクトリ名の例ではtsを使用します。 アプリケーションがJavaScriptベースの場合、ディレクトリ名はappRootDir/src/js/resources/nlsのようにjsです。

  3. 適切な翻訳を取得するように、アプリケーションのビューおよびviewModelコードの変更を構成する必要があります。

    たとえば、メニュー・ラベルがロケールに応じて英語またはフランス語に変更される次のUIを実装する場合は、次のように、ビュー・コードでbtnLocaleLabelおよびmenuNames Knockout変数を参照する必要があります:

    <oj-menu-button id="menuButton1">
      <span>
        <oj-bind-text value="[[btnLocaleLabel]]"></oj-bind-text>
      </span>
    <oj-menu id="myMenu1" slot="menu" on-oj-action="[[changeLabel]]">
      <oj-bind-for-each data="[[menuNames]]">
        <template>
          <oj-option value="[[$current.data.itemName]]" :id="[[$current.data.id]]">
    	<span>
    	  <oj-bind-text value="[[$current.data.itemName]]"></oj-bind-text>
    	</span>
          </oj-option>
      . . .
    
    画像は、インポートされたリソース・バンドルの英語およびフランス語での翻訳文字列を示しています
  4. viewModelコードでは、最初に、デフォルト文字列と翻訳済文字列を定義した翻訳バンドルをインポートします:
    import * as MenuBundle from "ojL10n!../resources/nls/menu";
  5. 次に、viewModelファイルからの次の抜粋で示すように、使用する適切な値としてインポートされた翻訳バンドル(MenuBundle)を参照するKnockout observableを宣言します:
    import * as MenuBundle from "ojL10n!../resources/nls/menu";
    
    class DashboardViewModel {
      btnLocaleLabel: ko.Observable<string>;
      menuNames: ko.ObservableArray<object>;
    
      constructor() {
        // setting up knockout observables for the 
        // button label and the menu items
        this.btnLocaleLabel = ko.observable();
        this.menuNames = ko.observableArray([{}]);
          . . .
      }
    
      loadMenu = () => {
        // These lines pull the translated values for the menu items 
        // from the appropriate resource file in the /resources/nls directory
        this.menuNames([
          { itemName: MenuBundle.menu1, id:'menu1' },
          { itemName: MenuBundle.menu2, id: 'menu2' },
          { itemName: MenuBundle.menu3, id: 'menu3' },
        ]);
        this.btnLocaleLabel(MenuBundle.label);
      };
      
      /**
       * Optional ViewModel method invoked after transition to the new View is complete.
       * That includes any possible animation between the old and the new View.
       */
      transitionCompleted(): void {
        // Call the function that pulls the translated values.
        this.loadMenu();
      }
    }
    
    export = DashboardViewModel;
  6. リソース・バンドルの文字列に、エスケープする必要があるメッセージ・トークンまたは予約文字(${}[])が含まれている場合は、Oracle JETのTranslation.applyParameters APIを使用する必要があります。

    次のコードは、エスケープする必要がある文字と、パラメータ値(トークン値とも呼ばれる)を必要とする文字列をレンダリングする方法を示しています。

    // // App UI is going to render the following strings:
    $ { } [ ]
    [The Oracle JET Developer's Guide costs $38.99]
    
    // To accomplish this, we enter the following entries in the app's resource bundle(s): 
    (appRootDir/src/ts/resources/nls/menu.js)
    . . .
            "EscapeChar": "$$ ${ $} $[ $]",
            "EscapeCharToken": "$[The {bookName} costs $$38.99$]"
        },
    . . .
    
    // appRoot/src/ts/viewModels/dashboard.ts includes the following entries:
    // Import the Translations module that includes the applyParameters() API
    import * as Translations from "ojs/ojtranslation";
    
    // Define types:
    . . .
      EscapeChar: String;
      EscapeCharToken: String;
    
    constructor() {
    
    . . .
    
    // Escape characters in resource bundle strings
    let parEscapeChar = { };
    this.EscapeChar = Translations.applyParameters(MenuBundle.EscapeChar, parEscapeChar)
    
    // Substitute a token and escape a character
    let parEscapeCharToken = { bookName: "Oracle JET Developer's Guide"};
    this.EscapeCharToken = Translations.applyParameters(MenuBundle.EscapeCharToken, parEscapeCharToken)
    
    // appRoot/src/ts/views/dashboard.html includes the following entries to render the final string
    . . .
    <oj-bind-text value="[[EscapeChar]]"></oj-bind-text>
    . . .
    <oj-bind-text value="[[EscapeCharToken]]"></oj-bind-text>
    

    JET-Localization.ZIPをダウンロードし、ZIPファイルを抽出するディレクトリでOracle JET CLI ojet restoreおよびojet serveコマンドを実行すると、これらのコード・スニペットが含まれるOracle JETアプリケーションを実行できます。

Oracle JET仮想DOMアプリケーションでのICU翻訳バンドルの操作

Oracle JETでは、Oracle JETアプリケーションがローカライズされたUI文字列を提供し、プレースホルダを使用したランタイム置換をサポートできるようにするICUメッセージ形式の変換バンドルがサポートされています。

International Components for Unicode (ICU)は、テキスト、数値、日付、時間、通貨、およびその他のロケール依存データの国際化を、ユーザーが表示できる文字列(メッセージ)のフォーマットでサポートするライブラリのセットです。 ICUによるメッセージのフォーマット方法の詳細は、そのドキュメンテーションを参照してください。

Oracle JETのICUメッセージ形式変換バンドルのサポートを使用するには、Oracle JETプロジェクトのルート・ディレクトリでOracle JET CLIのadd translationコマンドを実行します。 これにより、ランタイムICU翻訳バンドルの生成に必要なNPMパッケージ(@oracle/oraclejet-icu-l10n)がインストールされます。 また、プロジェクトのsrcディレクトリにICU翻訳バンドルを含むresourcesディレクトリが作成され、開始に役立つ次のファイルがあります。

appRootDir\src\resources
\---nls
    |   translationBundle.json
    |
    \---de
    translationBundle.json

最後に、プロジェクトのoraclejetconfig.jsonファイルを次のプロパティで更新して、Oracle JET CLIのbuildまたはserveコマンドの実行時にランタイムICU変換バンドルの生成を容易にします。

. . .
"translationIcuLibraries": "@oracle/oraclejet-icu-l10n",
. . .
"buildICUTranslationsBundle": true,
  "translation": {
    "type": "icu",
    "options": {
      "rootDir": "./src/resources/nls",
      . . .
      "supportedLocales": "de"
    }
  }
. . .

Oracle JETプロジェクトを構築する場合、@oracle/oraclejet-ICU-l10nはICU変換バンドル(translationBundle.jsonなど)を解析し、それらをTypeScriptモジュール(この例を使用してtranslationBundle.ts)に変換します。 これらのTypeScriptモジュールは、ランタイムICU変換バンドルです。 これらは、メッセージ・キーのローカライズされた文字列を取得するための関数を含むオブジェクトを返します。 この関数は、メッセージをフォーマットするためのパラメータを取ります。 複数形の場合、パラメータは常に数値です。 同じメッセージに複数のパラメータ(複数ルールの数字やテキスト・プレースホルダの文字列など)を指定できます。 パラメータ・タイプは、TypeScriptによって記述されます。

add translationコマンドが作成されるICU翻訳バンドルの例からわかるように、翻訳バンドル・ファイルには.JSONファイル拡張子が必要で、JSON形式に従う必要があります。 翻訳バンドル・リソースのキーは、常に最上位レベルに表示されます。 翻訳済リソースのサブオブジェクトの作成はサポートされていません。 コンポーネント作成者は、キー名("input_messages_error"など)に'_'のような規則ベースの文字を使用して、リソースの意図した使用方法を示すことができます。

"input_message_error": "Error",
"input_message_warning": "Warning"

値は、プレースホルダを含むことができる文字列です。

ノート:

以降の例は、読みやすくするために複数行文字列として書式設定されています。 ICU変換バンドルの実際の値は、JSON仕様で必要とされる単一行文字列である必要があります。 エディタで単語の折り返しを有効にして、便利に表示します。

@文字で始まるキーは、メタデータを指定します。 メタデータ・キーが@@で始まる場合、メタデータはグローバルとみなされます。 それ以外の場合、メタデータは、@文字に続く名前のキーに関連付けられます。

"input_message_error": "Error: {MESSAGE}",
"@input_message_error": 
   "placeholders": {
      "MESSAGE": 
         "type": "text",
         "description": "translated error message"
      }
   },
   "description": "Error with an embedded message"
}

メタデータはオプションです。 次の場合に指定します。

  1. テキスト・プレースホルダの使用は翻訳者にとっては不明であり、メタデータに余分な説明を提供することは、メッセージの残りの部分を翻訳するのに役立ちます。

  2. ルート/開発バンドルに含まれるメッセージで特定のプレースホルダが使用されていませんが、他のロケールでは対応するパラメータを設定する必要があります。

    ICU変換バンドルのビルド・プロセスは、ルート/開発バンドルのメッセージからパラメータ・タイプ情報を導出します。 他のロケールでのみ使用されるパラメータの型情報は、メタデータから取得する必要があります。

変換されたリソース内の予約済特殊文字は、#ポンドおよび中カッコ({})です。 ASCIIアポストロフィ('、U+0027)を使用して、これらの文字をメッセージに表示します。 1つのASCIIアポストロフィをメッセージに表示する必要があるが、その直後にポンド文字または中カッコが続く場合にのみ、二重ASCIIアポストロフィを使用します。 実アポストロフィ(一重引用符)文字(U+2019)を判読可能なテキストに使用し、ASCIIアポストロフィ' (U+0027)をエスケープ文字としてのみ使用することをお薦めします。 これにより、二重ASCIIアポストロフィを使用する必要がなくなります。

メッセージの特定の部分を翻訳しない場合は、テキスト・プレースホルダのパラメータとして指定します。

プレースホルダ・トークンの書式設定

プレースホルダ・トークンは、値を翻訳済メッセージに置換することで、動的コンテンツ生成を可能にします。

テキスト

テキスト・プレースホルダを使用して単純なトークン置換を実行します。 数値プレースホルダ名(01など)は使用しないでください。

"input_message_error": "Error: {MESSAGE}"

前述の例では、MESSAGEは、変換された文字列に挿入する名前付きパラメータを識別します。

複数形

複数プレースホルダは、特定の数値のグループまたは正確な数値を対象とした翻訳済メッセージを定義します。 これらはswitch文と考えることができます。

"shopping_card_items": "You have {item_count, plural, offset:0
    =0 {no items}
    one {# item}
    other {# items}
} in your cart"

前述の例を理解するには、次の点を考慮してください。

  • item_countは、メッセージの一致に数値が使用されるパラメータの名前です。
  • pluralはプレースホルダのタイプです。
  • =0は0と完全に一致します。
  • oneは、パラメータの値のカテゴリと照合される言語固有のカテゴリの1つです。 たとえば、121および31はすべて、ウクライナ語のカテゴリoneになります。 その他のカテゴリは、zerotwofewmanyです。 正確な数値(=1)の一致の優先順位は、カテゴリベースの一致よりも高いことに注意してください。
  • offsetは、カテゴリ・ベースのルールが適用される前にパラメータの値から減算される数値です。 デフォルトは0です。 offsetは完全一致(=1など)では使用されないことに注意してください。
  • otherは、switch文のdefaultラベルと同様です。 つまり、すべてのカテゴリをキャッチします。 otherは、すべての複数プレースホルダで必要であることに注意してください。
  • #は、オフセットが減算された後、実際の数値で置換されます。

選択

プレースホルダを選択して、文字列パラメータの値に基づいて選択される翻訳済メッセージを指定します。 複数形セレクタと同様に、これらをswitch文と考えることができます。

"response_message": "{gender, select,
    male {He}
    female {She}
    other {They}
    } will respond shortly."

前述の例では、genderは、変換されたメッセージと一致する文字列値を使用するパラメータの名前です。 使用可能なパラメータ値は、maleおよびfemaleです。 複数プレースホルダと同様に、otherは、switch文のdefaultラベルのように動作する特別なカテゴリです。 すべての選択プレースホルダでotherが必要であることに注意してください。

選択および複数プレースホルダのネスト

"invite_message": "{gender_of_host, select,
  female {
    {num_guests, plural, offset:1
      =0 {{host} does not give a party.}
      =1 {{host} invites {guest} to her party.}
      =2 {{host} invites {guest} and one other person to her party.}
      other {{host} invites {guest} and # other people to her party.}}}
  male {
    {num_guests, plural, offset:1
      =0 {{host} does not give a party.}
      =1 {{host} invites {guest} to his party.}
      =2 {{host} invites {guest} and one other person to his party.}
      other {{host} invites {guest} and # other people to his party.}}}
  other {
    {num_guests, plural, offset:1
      =0 {{host} does not give a party.}
      =1 {{host} invites {guest} to their party.}
      =2 {{host} invites {guest} and one other person to their party.}
      other {{host} invites {guest} and # other people to their party.}}}}"

前述の例では、複数プレースホルダが選択プレースホルダにネストされています。 その結果、性別固有のメッセージ・グループごとに複数ルールが適用されます。 複数ルールでのオフセットの値は、ゲスト数の出力にのみ使用され、guestパラメータで識別されるメイン・ゲストはカウントされません(#の置換)。

ICU翻訳バンドルを作成するためのOracle JET仮想DOMアプリケーションの設定

Oracle JET仮想DOMアプリケーションを設定してICU翻訳バンドルを作成および使用するには、add translationコマンドを実行し、このコマンドがアプリケーションのoraclejetconfig.jsonファイルに挿入するプロパティを編集します。

ICU翻訳バンドルをサポートするようにOracle JETアプリケーションを設定するには:

  1. アプリの最上位ディレクトリに移動し、ターミナル・ウィンドウを開き、次のコマンドを入力します。

    npx ojet add translation

  2. エディタで、oraclejetconfig.jsonファイルを開いて、add translationコマンドが挿入したプロパティを確認および編集します。

    translationセクションのtypeプロパティの値は、icuで変更しないままにします。 現在サポートされている値はこれのみです。 同様に、実行時ICU変換バンドルを生成しないかぎり、buildICUTranslationsBundleプロパティはtrueで変更しないままにします。 その場合は、falseに設定します。

    次の表に、optionsの子プロパティを示します。

    プロパティ 説明
    rootDir ICU変換バンドルのルート・ディレクトリ。 add translationコマンドで挿入される値は"./src/resources/nls"です。
    bundleName ojet buildまたはserveコマンドの実行時にランタイムICU変換バンドルを生成するバンドル名。 add translationコマンドで挿入される値は"translationBundle.json"です。
    locale ルート・ディレクトリ内のICU翻訳バンドルのロケール。 add translationコマンドで挿入される値は"en-US"です。
    outDir ランタイムICU翻訳バンドルの出力ディレクトリ。 add translationコマンドで挿入される値は"./src/resources/nls"です。 これを選択した値に変更します。 たとえば、"./src/resources-dist"です。
    supportedLocales ビルドする追加のロケールのカンマ区切りリスト。 rootDirディレクトリの下のnlsディレクトリに対応するエントリがないロケールを指定した場合、実行時ICU変換バンドルは、rootDirディレクトリのICU変換バンドルから構築されます。 add translationコマンドで挿入される値は"de"です。
    componentBundleName

    コンポーネントに関連付けられたICU変換バンドルのネーミング・パターンを指定します。

    add translationコマンドは、値"${componentName}-strings.json"を挿入します。ここで、{componentName}はコンポーネントの名前を参照します。

    my-componentという名前のコンポーネントを作成する場合、ICU変換バンドルのファイル名はmy-component-strings.jsonである必要があります。

Oracle JET仮想DOMアプリケーションへのICU翻訳バンドルの追加

アプリケーションUIに必要なカスタム文字列とアプリケーションがサポートする翻訳を使用して、ICU翻訳バンドルをOracle JET仮想DOMアプリケーションに追加します。

ICU翻訳バンドルをOracle JETアプリケーションに追加するには:

  1. フラットなキー値構造を持つ.JSONファイルのUI文字列および翻訳を定義します。

    各キーは最上位レベルである必要があります。 グループ化のためにオブジェクトをネストしないでください。

    たとえば、add translationコマンドの実行時にデフォルトで生成される次のエントリは、グリーティング・メッセージを定義します。

    {
      "greeting": "Hello! How are you doing?"
    }

    @の接頭辞が付いたキーは、説明やパラメータ・タイプなどのオプションのメタデータを提供します。 このメタデータは、プレースホルダがわかりにくい場合や、特定のロケールにのみ必要な場合に役立ちます。

  2. 各定義を、nlsという名前のディレクトリにある.JSONファイルに含めます。
    たとえば、前のステップの翻訳は、appRootDir/src/resources/nlsディレクトリのtranslationBundle.jsonという名前のファイルに配置されます。 サポートされている翻訳は、ロケールの名前を使用する子サブディレクトリのtranslationBundle.jsonという名前のファイルにあります。
    appRootDir/src/resources/nls
    |   translationBundle.json
    |
    /---de
        translationBundle.json

ランタイムICU翻訳バンドルの生成

Oracle JET仮想DOMアプリケーションがサポートするロケールのUI文字列を含む.JSONファイルを作成したら、実行時にアプリケーションがUI文字列を取得する場所にランタイムICU翻訳バンドルを構築する必要があります。

アプリの最上位ディレクトリに移動し、ターミナル・ウィンドウを開き、次のコマンドを入力します。

npx ojet build

これにより、ランタイムICU翻訳バンドル()が作成されます。TSファイル)および supportedLocales.tsファイル。oraclejetconfig.jsonファイルの outDirプロパティーで指定されたディレクトリにあります。 デフォルトでは、./src/resources/nlsです。

appRootDir/src/resources
\---nls
    |   supportedLocales.ts
    |   translationBundle.json
    |   translationBundle.ts
    |
    +---de
    |       translationBundle.json
    |       translationBundle.ts

ランタイムICU翻訳バンドルを生成したので、Oracle JET仮想DOMアプリケーションおよびVComponentsに含まれるUI文字列を使用できます。

Oracle JET仮想DOMアプリケーション・コンポーネントでのICU翻訳バンドルの使用

ランタイムICU翻訳バンドルを生成したら、生成されたバンドルのUI文字列を使用します。

ランタイムICU翻訳バンドルは、指定されたメッセージ・キーのUI文字列値を取得する関数を含むオブジェクトを定義します。 この関数は、メッセージをフォーマットするためのパラメータを使用できます。 複数形の場合、パラメータは常に数値です。

ノート:

同じメッセージに複数のパラメータを指定できます。 たとえば、複数ルールの数値、テキスト・プレースホルダの文字列です。 TypeScriptは、パラメータ・タイプを示します。

アプリケーションは、他のTypescriptモジュールと同様に翻訳バンドルを動的にロードします。 最初に、アプリが使用するロケールを決定します。 目的のロケールがわかると、./appRootDir/src/resources-dist/supportedLocales.tsファイルによってリストされるサポートされているロケールから最も一致するものが見つかります。 たとえば、add translationコマンドは、デフォルトでdeのサポートを追加します。

export default ["de"];

次のコード・サンプルは、仮想DOMアプリケーションのappRootDir/src/components/content/index.tsxファイルで、oj-input-text要素のlabel-hint属性値のUI文字列をロードする方法を示します。 最初にランタイムICU翻訳バンドルおよびサポートされているロケールをインポートします。 Preact useEffectフックは、インポートされたICU翻訳バンドルをロードし、優先ロケールと一致するものが見つかった場合は、それを使用します。 そうでない場合は、ドイツ語ロケール用のICU翻訳バンドルが使用されます(de)。

import { h } from "preact";
import { BundleType as ICUBundleType } from "../../resources/nls/translationBundle";
import supportedLocales from "../../resources/nls/supportedLocales";
import { useState, useEffect } from "preact/hooks";
import "ojs/ojlabel";
import "ojs/ojinputtext";
import "ojs/ojformlayout";

export function Content() {
  const [ICUBundle, setICUBundle] = useState<ICUBundleType>();
  // Load runtime ICU translation bundle in Preact's useEffect hook.
  useEffect(() => {
    _loadTranslationBundle().then((bundle) => setICUBundle(bundle));
  }, []);

  return (
    <div class="oj-web-applayout-max-width oj-web-applayout-content">
      <div id="icu-app">
      <p>
      Render UI string from the <code>./src/resources/nls/de</code> runtime
      ICU translation bundle.{" "}
      </p>
        {ICUBundle && (
          <oj-form-layout id="ofl1" max-columns="1" direction="column">
            <oj-input-text
              id="input"
              value=" "
              labelHint={ICUBundle.greeting()}
              labelEdge="inside"
            ></oj-input-text>
          </oj-form-layout>
        )}
      </div>
    </div>
  );
}

async function _loadTranslationBundle(): Promise<ICUBundleType> {
  const preferredLocale = navigator.languages[0];
  const localeToLoad = _matchTranslationBundle(
    preferredLocale,
    supportedLocales
  );

 // The ICU translation bundle name (translationBundle in this example) derives 
 // its name from the value that you specify for the bundleName property 
 // in the oraclejetconfig.json file. 
  const module = await import(
    `../../resources/nls/${localeToLoad}/translationBundle`
  );
  return module.default;
}

function _matchTranslationBundle(
  preferredLocale: string,
  supportedLocales: string[]
) {
  let match = null;
  const supportedLocaleSet = new Set(supportedLocales);

  if (supportedLocaleSet.has(preferredLocale)) {
    match = preferredLocale;
  } else {
    match = _findPartialMatch(preferredLocale, supportedLocaleSet);
  }
  // Normally, the fallback locale would be 'en-US', but for 
  // this example, we're using 'de'.
  return match || 'de';
}

function _findPartialMatch(locale: string, supportedLocales: Set<string>) {
  let match = null;
  const sep = "-";
  const parts = locale.split(sep);

  while (match === null && parts.length > 1) {
    parts.pop();
    const partial = parts.join(sep);
    if (supportedLocales.has(partial)) {
      match = partial;
    }
  }
  return match;
}

Oracle JET VComponentでのICU翻訳バンドルの使用

VComponentでICU翻訳バンドルを作成および使用するステップは、仮想DOMアプリケーションのものと同じです。 つまり、oraclejetconfig.jsonファイルのrootDirプロパティで指定されたパターンに一致するディレクトリの場所に、ICU翻訳バンドル・ソース・ファイル(.JSON)を作成します。 デフォルトでは、これは"./src/resources/nls"です。 VComponentを含むOracle JETプロジェクトをビルドすると、ランタイムICU翻訳バンドルがoutDirプロパティで指定された場所に生成されます。 この場合も、デフォルトは"./src/resources/nls"です。 oraclejetconfig.jsonファイル内の追加プロパティの1つは、componentBundleNameです。 これは、Oracle JETがランタイムICU翻訳バンドルを生成できるように、VComponentのICU翻訳バンドルのネーミング・パターンを指定します。 デフォルト値は"${componentName}-strings.json"です。

そのため、たとえば、Oracle JET仮想DOMアプリケーションにtranslation-icuという名前のVComponentを作成するとします。

npx ojet create component translation-icu --vcomponent

これにより、仮想DOMアプリケーションのプロジェクト・ファイル内にtranslation-icuディレクトリが作成され、このディレクトリには、次のようにresourcesおよびnlsサブディレクトリが含まれます。

appRootDir/src/components/translation-icu/resources/nls

nlsサブディレクトリ内に、サポートするロケールに応じてICU翻訳バンドルを作成します。

appRootDir/src/components/translation-icu/resources/nls
|   translation-icu-strings.json
+---de
|       translation-icu-strings.json

VComponentを含むOracle JET仮想DOMアプリケーションをビルドしてランタイムICU翻訳バンドルを生成したら、ランタイムICU翻訳バンドルのUI文字列をVComponentで使用できます。 次のコード・スニペットは、ICU翻訳バンドルからUI文字列をインポートおよび参照する方法を示しています。 簡潔にするために、仮想DOMアプリケーションの前の項のコード・サンプルから変更されていない完全なVComponentコードおよびヘルパー関数は省略されています。

. . .
import "css!./translation-icu-styles.css";
import { useState, useEffect } from "preact/hooks";
import "ojs/ojinputtext";
import "ojs/ojformlayout";
import { BundleType as ICUBundleType } from "./resources/nls/translation-icu-strings";
import supportedLocales from "./resources/nls/supportedLocales";

. . .

function TranslationIcuImpl() {

  const [ICUBundle, setICUBundle] = useState<ICUBundleType>();

  // Load ICU translation bundle in Preact's useEffect hook.
  useEffect(() => {
    _loadTranslationBundle().then((bundle) => setICUBundle(bundle));
  }, []);

  return (
    <div>
      <p>Render the string from the ICU translation bundle</p>
      {ICUBundle ? (
        <oj-form-layout id="ofl1" max-columns="1" direction="column">
          <oj-input-text
            id="input"
            value=" "
            labelHint={ICUBundle.greeting()}
            labelEdge="inside"
          ></oj-input-text>
        </oj-form-layout>
      ) : (
        <p>ICU translation bundle did not load</p>
      )}
    </div>
  );
}

// Helper functions for ICU translation bundle
async function _loadTranslationBundle(): Promise<ICUBundleType> {
  const preferredLocale = navigator.languages[0];
  const localeToLoad = _matchTranslationBundle(
    preferredLocale,
    supportedLocales
  );

// The component ICU translation bundle name 
// (translation-icu-strings in this example) derives 
// its name from the VComponent name and other values
// that you specify for the componentBundleName property 
// in the oraclejetconfig.json file.
  const module = await import(
    `./resources/nls/${localeToLoad}/translation-icu-strings`
  );
  return module.default;
}

function _matchTranslationBundle(
. . .
// Omitted for brevity. See previous section that includes full code
// for this function

function _findPartialMatch(locale: string, supportedLocales: Set<string>) {
. . .
// Omitted for brevity. See previous section that includes full code
// for this function

export const TranslationIcu: ComponentType<ExtendGlobalProps<
  ComponentProps<typeof TranslationIcuImpl>
  >> = registerCustomElement("translation-icu", TranslationIcuImpl);