へっぽこプログラマーの備忘録
プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。
menu
keyboard_arrow_up
Top
search
close
home
ホーム
computer
PC一般
construction
開発環境・ツール
code
プログラミング
home
ホーム
computer
PC一般
construction
開発環境・ツール
code
プログラミング
Home
›
ASP.NET Core
›
ASP.NET Core の多言語対応用リソースファイルのテスト
2019/01/08
ASP.NET Core の多言語対応用リソースファイルのテスト
update
event_note
label
ASP.NET Core
ASP.NET Core のおいて、多言語対応のためのリソースファイルが正しいかどうかをユニットテストで確認したいと思い、試行錯誤した結果です。
ASP.NET Core の多言語対応の機能を使っていることが前提です。 ## 環境 - Visual Studio 2017 - ASP.NET Core 2.1 - xUnit 2.4.1 ## やりたいこと **`Localizer` に指定されたキーが、リソースファイルにあるかどうか** - キーがリソースファイルに存在せず、キーの内容がそのまま出力されている場合にテスト失敗としたい - 例えば、あるキーが日本語のリソースファイルにはあるけど中国語のリソースファイルにはなく、言語が中国語の場合にはキーがそのまま出力されてしまっている、という場合にテスト失敗にしたい これにより、リソースファイルへのキーの追加忘れや、ソースファイルの変更に伴うリソースファイルの修正忘れをチェックできます。 **未使用のキーがないか** - リソースファイルにキーが定義されているが、実際にソースコード上で使われていないキーがある場合にはテスト失敗としたい これにより、リソースファイル内の不要なキーをチェックできます。 ## 手法と概要 いろいろ悩んだ結果、以下のやり方しか思いつきませんでした。 - ユニットテストを実行する前にテスト対象プロジェクト内のソースコードを全検索し、`localizer` に指定されているキーを正規表現により抽出する - ソースコードから抽出したキーとリソースファイル内のキーを比較 - これを全カルチャーのリソースファイルに対してチェックする ## 準備 ### テスト対象プロジェクト内のソースコードからキーを取得 テスト対象プロジェクト内のソースコードを全検索し、`localizer` に指定されているキーを正規表現により抽出します。 対象とするソースファイルの拡張子は `.cs` と `.cshtml` です。 まずはソースファイルの一覧を以下のようにして取得します。 ```cs var files = GetFiles("テスト対象プロジェクトへのパス", ".cs", ".cshtml"); ``` ```cs IEnumerable
GetFiles(string path, params string[] extensions) { return Directory .GetFiles(path, "*.*", SearchOption.AllDirectories) .Where(c => extensions.Any(extension => c.EndsWith(extension))) .ToArray(); } ``` ソースファイルの一覧を取得したら、各ソースファイル内から以下のようにしてキーを取得します。 ```cs // 各ソースファイル内の localizer のキーを取得 var allKeys = new List
(); foreach (var file in files) { allKeys.AddRange(GetAllKeys(file)); } // 重複を除外 SourceKeys = allKeys.Distinct(); ``` ```cs IEnumerable
GetAllKeys(string path) { var keys = new List
(); // 正規表現で localizer[] に指定されたキーを抽出する var rgx = new Regex("localizer\\[\"(.*)\"\\]", RegexOptions.IgnoreCase); using (var file = new StreamReader(path)) { var line = ""; while ((line = file.ReadLine()) != null) { var match = rgx.Match(line); if (match.Success) { keys.Add(match.Groups[1].Value); } } } return keys; } ``` ### リソースファイル内のキーを取得するため、DI の準備を行う リソースファイル内のキーを取得するために、きちんと DI サービスの設定を行って Localizer を取得します。 やっていることは ASP.NET Core プロジェクトの `Startup.cs` で行う DI サービスの設定と全く同じです。 ```cs IServiceCollection serviceCollection = new ServiceCollection(); // DI の準備を行う // 何故か ILoggerFactory を DI しないと Localizer も DI できなかった ILoggerFactory loggerFactory = new LoggerFactory().AddConsole(); services.AddSingleton(loggerFactory); services.AddLogging(); // ローカリゼーションを行うために必要なサービスをコンテナに登録 services.AddLocalization(options => options.ResourcesPath = "Resources"); // 参考: https://github.com/aspnet/Entropy/blob/master/samples/Localization.StarterWeb/Startup.cs const string enCulture = "en"; services.Configure
(options => { var supportedCultures = new[] { new CultureInfo(enCulture), new CultureInfo("ja"), }; options.DefaultRequestCulture = new RequestCulture(culture: enCulture, uiCulture: enCulture); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; }); // DI サービスのビルド serviceProvider = serviceCollection.BuildServiceProvider(); // サービスの取得 Localizer = serviceProvider.GetService
>(); ``` ## テストコード 以上の準備が終わったうえで、例えば以下のようにテストします。 ### ソースファイル内のキーがリソースファイルに定義されているかどうか ```cs [Theory, InlineData("ja"), InlineData("")] public void KeyDefined(string culture) { bool error = false; // 指定したカルチャーのリソースファイルのキーを取得 var resourceKeys = Localizer.WithCulture(new CultureInfo(culture)).GetAllStrings(false).Select(x => x.Name); // ソースファイル内のキーがリソースファイルに定義されているかどうかチェック foreach (var key in SourceKeys) { try { Assert.True(resourceKeys.Contains(key)); } catch { // 都度テスト失敗にしていたら、キーをリソースファイルの追加していくのが大変なので、 // 足りないキーを一括で表示したうえで、最後にテスト失敗にする output.WriteLine($"culture: {culture}\nkey: {key}\n-----"); error = true; } } Assert.False(error); } ``` ### リソースファイルに未使用のキーがあるかどうか ```cs [Theory, InlineData("ja"), InlineData("")] public void UnusedKey(string culture) { bool error = false; // 指定したカルチャーのリソースファイルのキーを取得 var resourceKeys = Localizer.WithCulture(new CultureInfo(culture)).GetAllStrings(false).Select(x => x.Name); // リソースファイル内のキーがソースファイル上にあるかどうかチェック foreach (var key in resourceKeys) { try { Assert.True(SourceKeys.Contains(key)); } catch { // 都度テスト失敗にしていたら、キーをリソースファイルから削除していくのが大変なので、 // 未使用キーを一括で表示したうえで、最後にテスト失敗にする output.WriteLine($"culture: {culture}\nkey: {key}\n----------"); error = true; } } Assert.False(error); } ``` ## チェックできないこと 以上で一応やりたいことはできました。 しかし、これは ASP.NET Core の多言語対応の書き方を正しく行っていることが前提となります。 つまり、`Localizer["key"]` と書かなければならないところを、間違って直接 `"key"` と書いてしまっているような場合はチェックできません。
## 参考 URL - https://forums.asp.net/t/2139161.aspx?how+to+use+other+language+option+in+localizer
tweet
facebook
Pocket
B!
はてブ
LINE
chevron_left
chevron_right
Translate
Popular Posts
TortoiseGit でコミットメッセージを変更する
image
NO IMAGE
smbclient で session setup failed: NT_STATUS_LOGON_FAILURE が表示される
Docker for Windows の設定
image
NO IMAGE
マージ元ブランチとマージ先ブランチ
image
NO IMAGE
TortoiseSVN ロック状態のチェック
TortoiseGit でブランチ間の差分を見る
image
NO IMAGE
Visual Studio で文字がにじむ(ぼやける)
image
NO IMAGE
AsciidocFX をビルドする
image
NO IMAGE
PowerShellでブレークポイントが設定できない場合
[Python] 文字列の判定で、None と空文字を同時に判定する
Labels
.NET Core
31
.NET Framework
17
.NET Standard
2
AdminLTE
1
AI
1
Apache
3
AppVeyor
2
AsciiDoc
7
ASP.NET Core
55
Atom
4
AWS
5
AWS Cloud9
4
blockdiag
1
Blogger
13
Bootstrap
3
C/C++
6
C#
106
CentOS
3
Chrome
1
Chronograf
3
chrony
1
Codecov
1
CSS
1
Docker
80
DokuWiki
4
Doxygen
1
draw.io
1
EasyTag
1
Electron
1
Electron.NET
2
Entity Framework Core
9
Excel
2
FFmpeg
3
Firefox
6
Flask
1
Git
19
GitBook
4
GitBucket
7
GitHub
7
GitLab
39
Go
1
Google
1
Google Cloud Platform
1
Grafana
13
GStreamer
2
HTML
5
IIS
8
InfluxDB
14
JavaScript
15
Jekyll
2
Jenkins
7
Linux
34
Log4View
1
MahApps.Metro
3
MaterialDesignInXamlToolkit
1
MkDocs
2
MongoDB
5
MVC
1
MVVM
6
nginx
3
NLog
3
Node.js
8
npm
1
NVIDIA
3
onvif
1
OpenAPI
2
OpenCV
4
OpenSSL
3
OpenVINO
2
ownCloud
2
pandas
1
Pine Script
1
PlantUML
5
Portainer
3
PowerShell
8
Prism
2
PySide
19
Python
88
PyTorch
1
RabbitVCS
1
Razor
3
redis
1
Redmine
33
Redoc
1
remark.js
2
rocketchat
10
Ruby
3
scikit-learn
1
shotcut
1
SignalR
1
Slack
1
Socket.IO
1
SonarQube
5
Sphinx
10
SQL Server
5
SQLite
1
StableDiffusion
1
Subversion
2
Swagger
1
Swarmpit
1
Syslog
3
Telegraf
6
Tesseract
3
TestLink
2
Tomcat
2
TortoiseGit
11
TortoiseSVN
2
Trading View
1
Traefik
3
Travis CI
1
Ubuntu
31
Visual Studio
39
Visual Studio Code
10
VSCode
8
Vue.js
8
Windows
62
Windows 10
5
Windows ADK
1
Windows API
2
Windows Embedded
4
wkhtmltopdf
2
Word
3
WPF
12
WSL
5
WSL2
5
Xamarin
1
xUnit
5
yaml
1
yolo
1
アプリケーション
1
デザインパターン
1
テスト
1
バッチファイル
2
プログラミング
4
ライセンス
1
暗号資産(仮想通貨)
1
英語
2
確定申告
1
機械学習
1
強化学習
1
雑記
1
書籍
1
数学
1
正規表現
1
動画編集
1
Blog Archive
►
2024
(18)
►
9月
(5)
►
8月
(1)
►
7月
(2)
►
6月
(1)
►
4月
(2)
►
3月
(1)
►
2月
(5)
►
1月
(1)
►
2023
(30)
►
12月
(3)
►
11月
(5)
►
10月
(2)
►
9月
(1)
►
8月
(2)
►
7月
(4)
►
6月
(2)
►
5月
(3)
►
4月
(2)
►
3月
(2)
►
2月
(3)
►
1月
(1)
►
2022
(106)
►
12月
(5)
►
11月
(1)
►
10月
(3)
►
9月
(6)
►
8月
(7)
►
7月
(6)
►
6月
(13)
►
5月
(9)
►
4月
(15)
►
3月
(11)
►
2月
(14)
►
1月
(16)
►
2021
(85)
►
12月
(11)
►
11月
(6)
►
10月
(4)
►
9月
(10)
►
8月
(8)
►
7月
(4)
►
6月
(18)
►
5月
(7)
►
4月
(8)
►
3月
(2)
►
2月
(2)
►
1月
(5)
►
2020
(56)
►
12月
(1)
►
11月
(3)
►
10月
(3)
►
9月
(3)
►
8月
(3)
►
7月
(7)
►
6月
(7)
►
5月
(2)
►
4月
(6)
►
3月
(6)
►
2月
(3)
►
1月
(12)
▼
2019
(92)
►
12月
(13)
►
11月
(9)
►
10月
(3)
►
9月
(2)
►
8月
(3)
►
7月
(5)
►
6月
(11)
►
5月
(6)
►
4月
(17)
►
3月
(9)
►
2月
(6)
▼
1月
(8)
AWS Cloud9 に .NET Core SDK をインストールする
ASP.NET Core のクエリ文字列の渡し方
AddScoped で DI した場合のインスタンスの扱い
[C#] 数値をカンマ付きで出力する
GitHub でプライベートリポジトリが作成できるようになった
バッチファイルで文字列を置換
ASP.NET Core の多言語対応用リソースファイルのテスト
xUnit でテストエクスプローラーにログを出力する
►
2018
(100)
►
12月
(1)
►
11月
(11)
►
10月
(8)
►
9月
(6)
►
8月
(10)
►
7月
(10)
►
6月
(8)
►
5月
(9)
►
4月
(8)
►
3月
(14)
►
2月
(4)
►
1月
(11)
►
2017
(117)
►
12月
(14)
►
11月
(20)
►
10月
(17)
►
9月
(19)
►
8月
(10)
►
7月
(8)
►
6月
(3)
►
5月
(6)
►
4月
(5)
►
3月
(2)
►
2月
(8)
►
1月
(5)
►
2016
(91)
►
12月
(5)
►
11月
(9)
►
10月
(11)
►
9月
(9)
►
8月
(6)
►
7月
(14)
►
6月
(14)
►
5月
(11)
►
4月
(10)
►
3月
(2)
►
2015
(23)
►
12月
(4)
►
11月
(2)
►
10月
(8)
►
9月
(8)
►
7月
(1)
►
2013
(3)
►
11月
(1)
►
9月
(1)
►
7月
(1)
►
2012
(2)
►
7月
(1)
►
6月
(1)
►
2011
(1)
►
9月
(1)
►
2009
(1)
►
7月
(1)
►
2008
(2)
►
11月
(1)
►
7月
(1)
►
2007
(3)
►
10月
(3)