UnityEngineのデフォルト言語 `default (SystemLanguage)` は アフリカン

# 前提
- unity 2018.4.9f1

# 困ったこと、やりたいこと
- `default (SystemLanguage)`は、`SystemLanguage.Afrikaans`で、`SystemLanguage.Unknown`ではありません。
- `Unknown`を含む全ての`SystemLanguage`値と区別可能な「`Undef`」を`default`値に持つ、`SystemLanguage`の拡張型が欲しいです。

# 試したこと
#### 継承して拡張する
- `SystemLanguage`は列挙型なので継承できませんでした。

#### Null許容型を流用する
- `SystemLanguage?`を使って、`null`を未定義とみなすようにしてみました。
- 諸々の不具合が生じて断念しました。

#### 新しい型を作る
- 上記の`Nullable<T>`での失敗から、`Booleanable<Language>`という概念を考えてみました。

# 解決策
- 論理値も持つ`Language`構造体を作ってみたところ、取り敢えず使えそうです。

### 特徴
- デフォルト値は`Language.Undef`です。
    - `Language lang = default;`とすると、`lang`には`Language.Undef`が入ります。
    - 構造体なので、未初期化のままで使用することはできません。
- `Language.Undef`は、`SystemLanguage`に対して`SystemLanguage.Unknown`として振る舞います。
    - `SystemLanguage syslang = lang;`とすると、`syslang`には`SystemLanguage.Unknown`が入ります。
- `SystemLanguage`と互換性があり、相互に代入や比較(暗黙の型変換)が可能です。
    - `Language lang = SystemLanguage.Japanese;`ができます。
    - `SystemLanguage syslang = lang;`ができます。
    - `if (lang == SystemLanguage.Japanese) { }`ができます。
- 論理値を持ちます。
    - `if (lang) { }`のような論理値コンテキストでは、`Language.Undef`以外なら真になります。
    - `lang = Language.Undef;`と、`lang = false;`は同じ結果です。
- 文字列型と相互に変換が可能です。
    - `ToString ()`ができます。
    - `Enum`準拠で、`Language.Parse ()`, `Language.TryParse ()`ができます。
        - `SystemLanguage`に存在しない名前は、全て`Language.Undef`になります。
- `Nullable<T>`に似た実装です。(たぶん)
    - `GetValueOrDefault ()`ができます。

### コード

```cs:BooleanableSystemLanguage.cs
using System;
using UnityEngine;

public struct Language : IEquatable<Language> {
	private bool hasValue;
	private SystemLanguage language;
	public static readonly Language Undef = new Language (false);
	public Language (bool _hasValue) {
		hasValue = _hasValue;
		language = _hasValue ? Application.systemLanguage : SystemLanguage.Unknown;
	}
	public Language (SystemLanguage _language) {
		hasValue = _language != SystemLanguage.Unknown;
		language = _language;
	}
	public static bool TryParse (string name, bool ignoreCase, out Language language) {
		if (string.IsNullOrEmpty (name) || !Enum.TryParse (name, ignoreCase, out SystemLanguage syslang)) {
			language = Undef;
			return false;
		}
		language = syslang;
		return true;
	}
	public static bool TryParse (string name, out Language language) => TryParse (name, false, out language);
	public static Language Parse (string name, bool ignoreCase) { TryParse (name, ignoreCase, out var language); return language; }
	public static Language Parse (string name) { TryParse (name, false, out var language); return language; }
	public SystemLanguage GetValueOrDefault () => hasValue ? language : SystemLanguage.Unknown;
	public SystemLanguage GetValueOrDefault (Language l) => hasValue ? language : (SystemLanguage) l;
	public static implicit operator bool (Language l) => l.hasValue;
	public static implicit operator SystemLanguage (Language l) => l.hasValue ? l.language : SystemLanguage.Unknown;
	public static implicit operator Language (bool b) => new Language (b);
	public static implicit operator Language (SystemLanguage l) => new Language (l);
	public override string ToString () => hasValue ? language.ToString () : "Undef";
	public static bool operator == (Language a, Language b) => (a.hasValue == b.hasValue) && (!a.hasValue || a.language == b.language);
	public static bool operator != (Language a, Language b) => !(a == b);
	public bool Equals (Language other) => (hasValue == other.hasValue) && (language == other.language);
	public override bool Equals (object obj) => (obj == null || GetType () != obj.GetType ()) ? false : Equals ((Language) obj);
	public override int GetHashCode () => hasValue ? language.GetHashCode () : int.MinValue;
}
```