11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C#のリソースの仕組みを使って多言語対応させようぜ☆(^~^)

Last updated at Posted at 2019-02-15

Visual Studio 2017 を使って C# のプログラムを多言語対応させようぜ

日本だけで使うプログラムなら、多言語対応の書き方は 一切 目にすることもないと思う。
見なくていいものは目に触れさせない、
そこらへん、Visual Studio / C#、 はたまた オブジェクト指向プログラミング というやつ は
よく出来てある……。

状況設定

ここは東京。 フランス人と インド人と 日本人が 働いているとするぜ。

私の書いたプログラムが 運悪く ファイル書き込み時に ファイルのロックに引っかかって 何度リトライしてもダメ、
諦めて タイムアウトさせて 異常終了することにするとしよう。
SendMail の機能を使って フランスチーム、インドチーム、日本チームに 異常通知メールを飛ばすという状況を想像してくれだぜ。

### 疑似コード

### 件名と本文。グーグル翻訳でいいだろ……、情報量の少ない日本語の直訳は、きっとおかしなことになっている。
SendMail("ツール障害", "ファイルのロックに引っかかった。")
SendMail("Défaillance de l'outil", "J'ai attrapé un verrou de fichier.")
SendMail("Tool failure", "I caught on a file lock.")

こんな感じで ベタ書き すれば その場の用は足りるが、何と言ったらいいのだろう。
ソースコードが 1文字 でも増えるのは労力だ。
定時に帰りたいマンは こんなことはしない。
というのも、テキスト は人類にとって便利だが、 テキストを扱う のはきっと人類が得意なものではない……。

そこそこ解決しよう

例えば、以下のようにする。

### 疑似コード

foreach culture in ["ja-JP(日本)", "fr-FR(フランス)", "en-US(インド)"]
    Resources.Culture = culture
    SendMail(Resources.ToolFailureSubject, Resources.ToolFailureBody)

文化を配列に入れておいてループで回し、
ループの中では 文化を切り替えて使う。翻訳の文章は、別のところに表を持っておく。

# コーヒーブレイク - 東京弁←→大阪弁に切り替えできるか?
# 
# en は英語、US はアメリカ方言で、合わせて en-US になる。英国方言なら en-EN。
# これは 言語コード という。
# 日本は ja-JP しかない。 大阪弁を追加しようと ja-OS(日本(大阪)) を作ろうとしたら
# 言語コードは OS(コンピューター)への登録制になっていて、管理者権限 が求められたので止めた。
# だったら fr-FR(フランス) に大阪弁を詰め込めば 東京弁/大阪弁 切り替えも実現可能……だが、
# こういう本来意図しない使い方をすると 定時に誰も帰れないくそコード になってしまう。

別の表は、以下のような形になっているとしよう。

Resources.ja-JP.resx

Key Value
ToolFailureSubject ツール障害
ToolFailureBody ファイルのロックに引っかかった。

Resources.fr-FR.resx

Key Value
ToolFailureSubject Défaillance de l'outil
ToolFailureBody J'ai attrapé un verrou de fichier.

Resources.resx

Key Value
ToolFailureSubject Tool failure
ToolFailureBody I caught on a file lock.

ここで、
Resources.ja-JP.resx には日本向けの、
Resources.fr-FR.resx にはフランス向けの、
Resources.resx にはアメリカ向けの言葉が入っているとするぜ。

インド人はどこに行ったのかというと、英語でいいらしい。

とりあえず、 英語(米国) を共通言語としておこう。 Key 列は 英語(米国) で書く。
あとは ja-JP を指定すれば 日本語テーブルの Value 列が、
fr-FR を指定すれば フランス語テーブルの Value 列が出てくる仕組みと考えれば すっきりしてるだろ。

en-US 、これは英語(米国)だが、を指定すれば デフォルトのテーブルが出てくると思えだぜ。

で、あとから言語を増やしたり減らしたりしたければ、 Resources.zh-CN.resx ファイルを追加したり、
Resources.fr-FR.resx ファイルを消したりすれば、文化を書いていた配列を修正する程度の手間で
対応でき……るという夢を ひとまず見ておこう。

エディターを見つけよう

Visual Studio 2017 という開発統合環境(IDE)があるが、機能拡張しなければ 便利ではない。
例えば ResXManager といった感じのフリーソフトをインストールするとしよう。すると、

Key en-US ja-JP fr-FR
ToolFailureSubject Tool failure ツール障害 Défaillance de l'outil
ToolFailureBody I caught on a file lock. ファイルのロックに引っかかった。 J'ai attrapé un verrou de fichier.

横一列に 文化 が並び、翻訳作業、あるいは 抜けチェック が楽になるかもしれない。
モデル/ビュー/コントローラー という言葉を聞いたことがあるなら、これは ビュー に当たる。
しかも、 [Translation] ボタンを押すと グーグル翻訳 と連携して 1列を埋めてくれるかもしれない。

「だろう」とか「しれない」というのは サーバーからアクセス制限を食らったりといった 実践問題 が
いろいろ あるから 期待を下げているわけだが、「ベタ書き」よりは はるかにマシになるだろう。

次に、ソースコードを改造している間に、ソースコードで使っていないリソース が出てきて、
不要だから Resources.resx ファイルから消したい、ということも出てくるかもしれない。
自動でやってくれれば……、と思うものの Visual Studio 2017 に期待しすぎてはいけない。
NuGet とか Marketplace とか、プラグインをダウンロードする仕組みを使って
欲しいものは自分で探して勝手に導入するのが 流行りだぜ。

例えば RESX-Unused-Finder を使って、使っていないリソースは消してしまおう。
ぼーっとしているときに ファイルパスを間違えて どこかで何かが消えているヒューマンミスも起こりえるが、
人気が出れば 誰かが GUI を改善してくれるだろう。

これで多言語対応は ばっちりだ……、と思うと そうでもない。フリーソフトは バグを避けながら使うものだぜ。
便利ではない Visual Studio 2017 にデフォルトで付いている「リソース」とかいうエディターを使う場面が出てくる。
それが Keyの変更 だぜ。

Resources.resx ファイルを フリーソフトでいじっても、C# のソースコードの方はリファクタリング(プロパティ名の変更)してくれない。
理屈は分かる。
.resx とかいう XML ファイルをいじるのは簡単でも、C# のソースコードのリファクタリングまでやってくれるかというと、
その作業は 全く別物なのかもしれない。(あるいは 欲しければ自分で作らなければならない)

ところで C# のソースを貼っておく。

using System;

// ↓この角括弧が何やってるか分けわかんなければ消してくれだぜ。
[assembly: CLSCompliant(true)]
namespace Grayscale.ResourcePracticeWithConsole
{
    using System.Diagnostics;
    using System.Globalization;
    using System.Threading;

    // ↓リソースファイルを使おうとしている。
    // リソースファイルの作り方は Qiita で別の人が書いてるだろ、勝手に調べろだぜ。
    using Grayscale.ResourcePracticeWithConsole.Properties;

    /// <summary>
    /// プログラム。
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// エントリーポイント。
        /// </summary>
        public static void Main()
        {
            // 使うリソースを 英語(米国)を使う。
            Resources.Culture = new CultureInfo("en-US");

            // ↓C#デフォルトのエラーメッセージや、画面に効いてくる。
            Thread.CurrentThread.CurrentCulture = Resources.Culture;
            Thread.CurrentThread.CurrentUICulture = Resources.Culture;

            // ↓英語(米国)テーブルの HelloWorld キーに対応した値が表示される。
            Trace.WriteLine(Resources.HelloWorld);

            // 使うリソースを 日本 に変える。
            Resources.Culture = new CultureInfo("ja-JP");

            // ↓日本テーブルの HelloWorld キーに対応した値が表示される。
            Trace.WriteLine(Resources.HelloWorld);

            // 15秒待つ。
            Thread.Sleep(15000);
        }
    }
}

だいたいの 基本 を紹介した。

変えたカルチャーを元に戻すために Old 変数に古いのを一時退避しておいて後で使う スワップ とか使いこなして、
ログは英語、通知メールは自分の言語に切り替えるとか、そういうのやり始めるのは 応用 なんで説明しない。

データベースに 通知メール受け取りユーザーや、そのユーザーの言語を覚えさせといて
ユーザーの追加に対応するとか 発展 なんで、要件を満たした後で 勝手に遊べだぜ。

じゃあ、

  • 「リソース」エディターの中では [Shift]+[Enter] キーで改行できるが、他のエディターではどうやって改行を入れるのか?
  • 「&#39;」って何よ?
  • 「&amp;gt;」って何よ?

など、いろいろな 実践問題が出てくるので 1つ1つ 解決していって欲しい。
多分、フリーソフトの人気が上がって 利用者が儲かれば コミュニティの誰かが直してくれると思うが……。

年月日時分

また、 12, FEB と書いてあっても わたしには 何かわからないし、 2019/02/12 と書いていても 何月か分からないし、
データベースに 年月日を 20190212 と 8桁で入れている日本企業があるとしても 何月か分からない。

日付の 書式 をハードコーディングしていては 定時には帰れないだろう。
そんなときにも CultureInfo を使う。

	// 日付。
	var dateValue = new DateTime(2019, 2, 16, 13, 57, 0);
	var cultures = new[]
	{
		new CultureInfo("ja-JP"),
		new CultureInfo("en-US"),
		new CultureInfo("fr-FR"),
		new CultureInfo("de-DE")
	};

	foreach (var culture in cultures)
	{
		Trace.WriteLine(string.Format(
			"{0}: {1}",
			culture.Name,
			dateValue.ToString(culture)));
	}

書式はハードコーディングせず、文化を指定すれば 勝手に変換されるように書いておく。

ja-JP: 2019/02/16 13:57:00
en-US: 2/16/2019 1:57:00 PM
fr-FR: 16/02/2019 13:57:00
de-DE: 16.02.2019 13:57:00

参考。

しかし、見た目だけ対応していても、日本はアメリカより9時間早い。
時差に対応する必要がある。 これも ハードコーディングしていては 定時には帰れないだろう。

そこで、日本時間を いったん世界標準時に戻して、そこから タイムゾーンを指定して ずらす。
夏時間とか、 アメリカといっても東部と西部のどっちなのかとか、いっさい考えない。

	var universalDate = DateTimeOffset.Now.ToUniversalTime();

	// System.ValueTuple
	(CultureInfo CultureInfo, string TimeZone)[] cultures =
	{
		(new CultureInfo("ja-JP"), "Tokyo Standard Time"),
		(new CultureInfo("en-US"), "GMT Standard Time"),
		(new CultureInfo("fr-FR"), "Romance Standard Time"),
		(new CultureInfo("de-DE"), "Romance Standard Time"),
	};

	foreach (var culture in cultures)
	{
		Trace.WriteLine(string.Format(
			"{0}: {1} ({2})",
			culture.CultureInfo.Name,
			TimeZoneInfo.ConvertTime(
				universalDate,
				TimeZoneInfo.FindSystemTimeZoneById(culture.TimeZone)).ToString(culture.CultureInfo),
				culture.TimeZone));
	}

その結果、

ja-JP: 2019/02/16 14:48:15 +09:00 (Tokyo Standard Time)
en-US: 2/16/2019 5:48:15 AM +00:00 (GMT Standard Time)
fr-FR: 16/02/2019 06:48:15 +01:00 (Romance Standard Time)
de-DE: 16.02.2019 06:48:15 +01:00 (Romance Standard Time)

と時差も考慮してくれる。
アメリカの東なのか西なのかは 聞いておくといいのかもしれないが、めんどくさいんで イギリスのタイムゾーンにしている。

+09:00 というのは、グリニッジ天文台の時計から+9時間ですよ、という意味なので、9を引けば グリニッジ標準時に戻せる。
日本で 朝8時にメールが届く、ということは グリニッジから見れば 昨日の23時 にメールが届いている。
フランスから見れば 0時に届いていると分かる。 これで時差は だいたい配慮できた。

// ヒマがあれば もっと書く。

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?