asp.net core で resx ファイル文字列を取得するにはどうすればいいですか? mvc では ResxResourceReader を使用して文字列を取得します。 しかし、asp.net core では同じものを取得できません。
ベストアンサー1
.NET Core では、リソース ファイルの動作方法が標準以下でわかりにくいものに変更されました (理解するのに数日かかりました)。ただし、必要な操作は次のとおりです。
- Startup.cs に次のコードを追加します - 注: サポートする言語を変更すると、"Resources" の ResourcePath は、後で .resx ファイルを保存するフォルダーになります。
としてジャストマーティンコメントで言った:あなたがあなたの共有リソースファイル内リソースフォルダを作成し、その名前空間をResourcesで終わるように設定し、
o.ResourcesPath = "Resources"
ただ使うservices.AddLocalization()
そうしないと、存在しない Resources.Resources フォルダーの検索が開始されます。
services.AddLocalization(o => o.ResourcesPath = "Resources");
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("en-GB"),
new CultureInfo("de-DE")
};
options.DefaultRequestCulture = new RequestCulture("en-US", "en-US");
// You must explicitly state which cultures your application supports.
// These are the cultures the app supports for formatting
// numbers, dates, etc.
options.SupportedCultures = supportedCultures;
// These are the cultures the app supports for UI strings,
// i.e. we have localized resources for.
options.SupportedUICultures = supportedCultures;
});
resx ファイルを保存するプロジェクトにフォルダーを作成します。デフォルトでは、「Resources」という名前になります。
特定のカルチャと後で検索するファイル名を使用して新しい resx ファイルを作成します。共有ファイルがある場合は、SharedResource.en-US.resx のように実行できます。次に、自動コード生成は今は役に立たないのでオフにします。
resx ファイルと同じ場所に「SharedResource」というクラスを作成します。空でもかまいませんが、後で参照できるようにそこに存在する必要があります。
リソースを使用する場所にはどこでも、IoC が (この例では) IStringLocalizer< SharedResource > を "_localizer" などの名前で挿入します。
最後に、_localizer["My_Resource_Name"] を実行することで、リソース ファイル内のエントリを参照できます。
同じフォルダーに「SharedResource.de-DE.resx」などの名前の新しい resx ファイルを作成して、別の言語を追加します。
「リソース」フォルダーは、すべてのアセンブリを検索するためにすべてのアセンブリで使用されます。そのため、特にビュー固有のものをここに含め始めると、このフォルダーがかなり乱雑になる可能性があります。
開発者がここで何をしようとしていたかはわかりますが、そこに到達するまでに多くのことを犠牲にしました。開発者は実際に翻訳しなくても、コードを作成して翻訳を追加できます。開発者が最初から翻訳を念頭に置くことは簡単になりましたが、実際に翻訳を使用する開発者にとっては、結局はるかに多くの作業が必要になります。今では何も自動生成できません。翻訳にアクセスするには、翻訳への参照を IoC で注入する必要があります (ServiceLocater アンチパターンを使用する場合を除き、static は不要です)。すべての名前はハードコードされた文字列であるため、翻訳を間違えると、指定した文字列が返されるだけになり、翻訳の目的がそもそも失われます。つまり、どこでも定数に依存しないように、おそらくこれをラップする必要があります。
正直に言うと、これが良いアイデアだと思った人がいるなんて信じられません。そもそも、翻訳を気にしない開発者のために、なぜ全力を尽くすのでしょうか?
結局、このスタイルのラッパーを作成しました。これの良い点は、データベースからリソースを取得することに決めた場合、上記のコードを変更する必要がないことです。ただし、リソース エントリを追加し、それをインターフェイスに追加してから、それを再度取得するために実装する必要があります。nameof() を使用したので、定数を使用する必要はありませんでしたが、プロパティ名または resx ファイル名が変更されると、クラッシュすることなく翻訳が壊れるため、これはまだ脆弱です。送信した値と同じ値を取得しないようにするには、統合テストが必要になるでしょう。
public interface ICommonResource
{
string ErrorUnexpectedNumberOfRowsSaved { get; }
string ErrorNoRecordsSaved { get; }
string ErrorConcurrency { get; }
string ErrorGeneric { get; }
string RuleAlreadyInUse { get; }
string RuleDoesNotExist { get; }
string RuleInvalid { get; }
string RuleMaxLength { get; }
string RuleRequired { get; }
}
public class CommonResource : ICommonResource
{
private readonly IStringLocalizer<CommonResource> _localizer;
public CommonResource(IStringLocalizer<CommonResource> localizer) =>
_localizer = localizer;
public string ErrorUnexpectedNumberOfRowsSaved => GetString(nameof(ErrorUnexpectedNumberOfRowsSaved));
public string ErrorNoRecordsSaved => GetString(nameof(ErrorNoRecordsSaved));
public string ErrorConcurrency => GetString(nameof(ErrorConcurrency));
public string ErrorGeneric => GetString(nameof(ErrorGeneric));
public string RuleAlreadyInUse => GetString(nameof(RuleAlreadyInUse));
public string RuleDoesNotExist => GetString(nameof(RuleDoesNotExist));
public string RuleInvalid => GetString(nameof(RuleInvalid));
public string RuleMaxLength => GetString(nameof(RuleMaxLength));
public string RuleRequired => GetString(nameof(RuleRequired));
private string GetString(string name) =>
_localizer[name];
}