.NET グローバリゼーションと ICU.NET globalization and ICU

.NET グローバリゼーション API は、これまで、プラットフォームごとに別の基になるライブラリを使用していました。In the past, the .NET globalization APIs used different underlying libraries on different platforms. この API は、Unix では、ICU (International Components for Unicode) を使用し、Windows では、各国語サポート (NLS) を使用していました。On Unix, the APIs used International Components for Unicode (ICU), and on Windows, they used National Language Support (NLS). これにより、アプリケーションを実行するとき、いくつかのグローバリゼーション API の動作がプラットフォームによって違うことがありました。This resulted in some behavioral differences in a handful of globalization APIs when running applications on different platforms. 次の領域で、動作が違うことがわかっています。Behavior differences were evident in these areas:

  • カルチャとカルチャ データCultures and culture data
  • 文字の大文字小文字String casing
  • 文字の並べ替えと検索String sorting and searching
  • 並べ替えキーSort keys
  • 文字の正規化String normalization
  • 国際化ドメイン名 (IDN) のサポートInternationalized Domain Names (IDN) support
  • Linux のタイム ゾーンの表示名Time zone display name on Linux

.NET 5.0 以降では、プラットフォームによってアプリケーションに違いがでることがないよう、開発者が基になるライブラリをより制御できるようになりました。Starting with .NET 5.0, developers have more control over which underlying library is used, enabling applications to avoid differences across platforms.

Windows 上の ICUICU on Windows

Windows 10 の 2019 年 5 月更新プログラム以降では、OS の一部として icu.dll が含まれるようになり、.NET 5.0 以降のバージョンでは、既定で ICU が使用されるようになりました。Windows 10 May 2019 Update and later versions include icu.dll as part of the OS, and .NET 5.0 and later versions use ICU by default. .NET 5.0 以降のバージョンを Windows で実行する場合、icu.dll の読み込みが試行され、存在する場合、グローバリゼーションの実装で使用されます。When running on Windows, .NET 5.0 and later versions try to load icu.dll and, if it's available, use it for the globalization implementation. Windows の古いバージョンを実行している場合などで、ICU ライブラリを見つけたり、読み込んだりすることができない場合、.NET 5.0 以降のバージョンは NLS ベースの実装に戻ります。If the ICU library can't be found or loaded, such as when running on older versions of Windows, .NET 5.0 and later versions fall back to the NLS-based implementation.

注意

ICU が使用されている場合でも、Windows オペレーティング システム API では、ユーザーが設定している場合は、CurrentCultureCurrentUICulture、および CurrentRegion のメンバーが使用されます。Even when using ICU, the CurrentCulture, CurrentUICulture, and CurrentRegion members still use Windows operating system APIs to honor user settings.

動作の違いBehavioral differences

.NET 5 を対象にするようにアプリをアップグレードすると、グローバリゼーション機能を使用していることを認識していない場合でも、アプリでの変更に気付くことがあります。If you upgrade your app to target .NET 5, you might see changes in your app even if you don't realize you're using globalization facilities. ここでは、気付く可能性のある動作の変更を 1 つ示しますが、他にもあります。This section lists one of the behavioral changes you might see, but there are others too.

String.IndexOfString.IndexOf

文字列内の改行文字のインデックスを調べるために String.IndexOf(String) を呼び出す次のコードについて考えます。Consider the following code that calls String.IndexOf(String) to find the index of the newline character in a string.

string s = "Hello\r\nworld!";
int idx = s.IndexOf("\n");
Console.WriteLine(idx);
  • Windows 上の以前のバージョンの .NET では、スニペットにより 6 と出力されます。In previous versions of .NET on Windows, the snippet prints 6.
  • Windows 10 May 2019 Update 以降のバージョン上の .NET 5.0 以降のバージョンでは、スニペットにより -1 と出力されます。In .NET 5.0 and later versions on Windows 10 May 2019 Update and later versions, the snippet prints -1.

カルチャ依存検索ではなく序数検索を実行してこのコードを修正するには、IndexOf(String, StringComparison) のオーバーロードを呼び出し、StringComparison.Ordinal を引数として渡します。To fix this code by conducting an ordinal search instead of a culture-sensitive search, call the IndexOf(String, StringComparison) overload and pass in StringComparison.Ordinal as an argument.

コード分析規則 CA1307: 明確化のために StringComparison を指定するおよび CA1309: 順序に基づく StringComparison を使用するを実行して、コード内でのこれらの呼び出しサイトを検索できます。You can run code analysis rules CA1307: Specify StringComparison for clarity and CA1309: Use ordinal StringComparison to find these call sites in your code.

詳細については、「.NET 5 以降で文字列を比較するときの動作の変更」を参照してください。For more information, see Behavior changes when comparing strings on .NET 5+.

ICU の代わりに NLS を使用するUse NLS instead of ICU

NLS の代わりに ICU を使用すると、一部のグローバリゼーション関連の操作で動作が違ってしまうことがあります。Using ICU instead of NLS may result in behavioral differences with some globalization-related operations. 開発者は、NLS を使用するように、ICU の実装を戻すことを選択することができます。To revert back to using NLS, a developer can opt out of the ICU implementation. アプリケーションでは、次のいずれかの方法で NLS モードを有効にできます。Applications can enable NLS mode in any of the following ways:

  • プロジェクト ファイルで次を実行します。In the project file:

    <ItemGroup>
      <RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />
    </ItemGroup>
    
  • runtimeconfig.json ファイルで次の操作を行います。In the runtimeconfig.json file:

    {
      "runtimeOptions": {
         "configProperties": {
           "System.Globalization.UseNls": true
          }
      }
    }
    
  • 環境変数 DOTNET_SYSTEM_GLOBALIZATION_USENLS の値を true または 1 に設定します。By setting the environment variable DOTNET_SYSTEM_GLOBALIZATION_USENLS to the value true or 1.

注意

プロジェクトまたは runtimeconfig.json に設定された値が環境変数に優先されます。A value set in the project or in the runtimeconfig.json file takes precedence over the environment variable.

詳細については、ランタイムの構成設定に関するページを参照してください。For more information, see Run-time config settings.

アプリローカル ICUApp-local ICU

ICU の各リリースには、バグ修正と、世界の言語が記述された更新された共通ロケール データ リポジトリ (CLDR) データが含まれる場合があります。Each release of ICU may bring with it bug fixes as well as updated Common Locale Data Repository (CLDR) data that describes the world's languages. ICU のバージョンを変えると、グローバリゼーション関連の操作でアプリの動作がわずかに影響を受ける場合があります。Moving between versions of ICU can subtly impact app behavior when it comes to globalization-related operations. .NET 5.0 以降のバージョンでは、独自の ICU のコピーが Windows と Unix の両方上のアプリに含まれ使用されになっており、アプリケーション開発者が、配置したすべてのアプリで整合性を保てるようになっています。To help application developers ensure consistency across all deployments, .NET 5.0 and later versions enable apps on both Windows and Unix to carry and use their own copy of ICU.

アプリケーションでは、次のいずれかの方法で、アプリローカル ICU が実装されるモードをオプトインできます。Applications can opt in to an app-local ICU implementation mode in any of the following ways:

  • プロジェクト ファイルで次の操作を行います。In the project file:

    <ItemGroup>
      <RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="<suffix>:<version> or <version>" />
    </ItemGroup>
    
  • runtimeconfig.json ファイルで次の操作を行います。In the runtimeconfig.json file:

    {
      "runtimeOptions": {
         "configProperties": {
           "System.Globalization.AppLocalIcu": "<suffix>:<version> or <version>"
         }
      }
    }
    
  • 環境変数 DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU の値を <suffix>:<version> または <version> に設定します。By setting the environment variable DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU to the value <suffix>:<version> or <version>.

    <suffix>: 公開されている ICU パッケージ規則に従った、長さが 36 文字未満の省略可能なサフィックス。<suffix>: Optional suffix of fewer than 36 characters in length, following the public ICU packaging conventions. ICU をカスタマイズして構築する場合、libicuucmyapp のように、ライブラリ名とエクスポートされたシンボル名にサフィックスが含まれるようにカスタマイズできます。ここでは、myapp がサフィックスです。When building a custom ICU, you can customize it to produce the lib names and exported symbol names to contain a suffix, for example, libicuucmyapp, where myapp is the suffix.

    <version>:67.1 などの有効な ICU のバージョン。<version>: A valid ICU version, for example, 67.1. このバージョンは、バイナリを読み込み、エクスポートされたシンボルを取得するために使用されます。This version is used to load the binaries and to get the exported symbols.

.NET では、アプリローカルのスイッチが設定されている場合に ICU を読み込むために、複数のパスをプローブする NativeLibrary.TryLoad メソッドが使用されます。To load ICU when the app-local switch is set, .NET uses the NativeLibrary.TryLoad method, which probes multiple paths. このメソッドでは、まず NATIVE_DLL_SEARCH_DIRECTORIES プロパティからライブラリが検索されます。このプロパティは、アプリの deps.json ファイルに基づき dotnet ホストが作成します。The method first tries to find the library in the NATIVE_DLL_SEARCH_DIRECTORIES property, which is created by the dotnet host based on the deps.json file for the app. 詳細については、「既定のプローブ」を参照してください。For more information, see Default probing.

自己完結型アプリの場合は、ICU がアプリのディレクトリ内にあることを確認する以外に、ユーザーが特別に行う操作はありません (自己完結型アプリの場合、既定の作業ディレクトリは NATIVE_DLL_SEARCH_DIRECTORIES です)。For self-contained apps, no special action is required by the user, other than making sure ICU is in the app directory (for self-contained apps, the working directory defaults to NATIVE_DLL_SEARCH_DIRECTORIES).

NuGet パッケージの ICU を使用している場合は、フレームワークに依存するアプリケーションでこのようになります。If you're consuming ICU via a NuGet package, this works in framework-dependent applications. NuGet はネイティブ資産を解決し、deps.json ファイルと runtimes ディレクトリ下のアプリケーションの出力ディレクトリにそれを格納します。NuGet resolves the native assets and includes them in the deps.json file and in the output directory for the application under the runtimes directory. .NET はそこからこれを読み込みます。.NET loads it from there.

ローカル ビルドから ICU が使用されるフレームワーク依存のアプリの場合は、手順を追加で実行する必要があります。For framework-dependent apps (not self contained) where ICU is consumed from a local build, you must take additional steps. .NET SDK には、"ルース" ネイティブ バイナリを deps.json に組み込む機能がまだありません (この SDK の問題を参照してください)。The .NET SDK doesn't yet have a feature for "loose" native binaries to be incorporated into deps.json (see this SDK issue). 代わりに、アプリケーションのプロジェクト ファイルに情報を追加して有効にすることができます。Instead, you can enable this by adding additional information into the application's project file. 次に例を示します。For example:

<ItemGroup>
  <IcuAssemblies Include="icu\*.so*" />
  <RuntimeTargetsCopyLocalItems Include="@(IcuAssemblies)" AssetType="native" CopyLocal="true" DestinationSubDirectory="runtimes/linux-x64/native/" DestinationSubPath="%(FileName)%(Extension)" RuntimeIdentifier="linux-x64" NuGetPackageId="System.Private.Runtime.UnicodeData" />
</ItemGroup>

これは、サポートされているランタイムのすべての ICU バイナリに対して実行する必要があります。This must be done for all the ICU binaries for the supported runtimes. また、RuntimeTargetsCopyLocalItems 項目グループの NuGetPackageId メタデータが、プロジェクトが実際に参照する NuGet パッケージと一致している必要があります。Also, the NuGetPackageId metadata in the RuntimeTargetsCopyLocalItems item group needs to match a NuGet package that the project actually references.

macOS の動作macOS behavior

macOSmatch-o ファイルで指定した読み込みコマンドから依存しているダイナミック ライブラリを解決する動作は、Linux ローダーの動作とは異なります。macOS has a different behavior for resolving dependent dynamic libraries from the load commands specified in the match-o file than the Linux loader. Linux ローダーでは、ICU 依存関係グラフに従い、.NET が libicudatalibicuuc、および libicui18n を (この順序で) 試行します。In the Linux loader, .NET can try libicudata, libicuuc, and libicui18n (in that order) to satisfy ICU dependency graph. ただし、これは macOS では機能しません。However, on macOS, this doesn't work. macOS で ICU を構築する場合、ユーザーは既定でこれらの読み込みコマンドで、libicuuc にダイナミック ライブラリを取得します。When building ICU on macOS, you, by default, get a dynamic library with these load commands in libicuuc. 次のスニペットに例を示します。The following snippet shows an example.

~/ % otool -L /Users/santifdezm/repos/icu-build/icu/install/lib/libicuuc.67.1.dylib
/Users/santifdezm/repos/icu-build/icu/install/lib/libicuuc.67.1.dylib:
 libicuuc.67.dylib (compatibility version 67.0.0, current version 67.1.0)
 libicudata.67.dylib (compatibility version 67.0.0, current version 67.1.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
 /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 902.1.0)

これらのコマンドでは、ICU の他のコンポーネントの依存ライブラリの名前のみを参照します。These commands just reference the name of the dependent libraries for the other components of ICU. ローダーは、dlopen 表記規則に従って検索を実行します。このとき、これらのライブラリはシステム ディレクトリに配置されているか、LD_LIBRARY_PATH 環境変数が設定されているか、アプリレベル ディレクトリに ICU がある必要があります。The loader performs the search following the dlopen conventions, which involves having these libraries in the system directories or setting the LD_LIBRARY_PATH env vars, or having ICU at the app-level directory. LD_LIBRARY_PATH を設定できない場合、または ICU バイナリがアプリレベルのディレクトリにない可能性がある場合は、作業を追加で行う必要があります。If you can't set LD_LIBRARY_PATH or ensure that ICU binaries are at the app-level directory, you will need to do some extra work.

ローダーには、その読み込みコマンドでバイナリと同じディレクトリから、その依存関係を検索するように指示する、@loader_path などのディレクティブがいくつかあります。There are some directives for the loader, like @loader_path, which tell the loader to search for that dependency in the same directory as the binary with that load command. これを実現する方法は 2 つあります。There are two ways to achieve this:

  • install_name_tool -change

    次のコマンドを実行します。Run the following commands:

    install_name_tool -change "libicudata.67.dylib" "@loader_path/libicudata.67.dylib" /path/to/libicuuc.67.1.dylib
    install_name_tool -change "libicudata.67.dylib" "@loader_path/libicudata.67.dylib" /path/to/libicui18n.67.1.dylib
    install_name_tool -change "libicuuc.67.dylib" "@loader_path/libicuuc.67.dylib" /path/to/libicui18n.67.1.dylib
    
  • @loader_path が使用されたインストール名が作成されるよう、ICU をパッチします。Patch ICU to produce the install names with @loader_path

    autoconf (./runConfigureICU) を実行する前に、これらの行を次のように変更します。Before running autoconf (./runConfigureICU), change these lines to:

    LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @loader_path/$(notdir $(MIDDLE_SO_TARGET))
    

WebAssembly での ICUICU on WebAssembly

WebAssembly ワークロード専用の ICU バージョンを利用できます。A version of ICU is available that's specifically for WebAssembly workloads. このバージョンでは、デスクトップ プロファイルとのグローバリゼーションの互換性が提供されます。This version provides globalization compatibility with desktop profiles. ICU データ ファイルのサイズを 24 MB から 1.4 MB (Brotli で圧縮する場合は約 0.3 MB) に減らすため、このワークロードにはいくつかの制限があります。To reduce the ICU data file size from 24 MB to 1.4 MB (or ~0.3 MB if compressed with Brotli), this workload has a handful of limitations.

次の API はサポートされていません。The following APIs are not supported:

次の API は、制限付きでサポートされています。The following APIs are supported with limitations:

また、サポートされているロケールの一覧については、dotnet/icu リポジトリを参照してください。In addition, a list of supported locales can be found on the dotnet/icu repo.