💾 Archived View for d.moonfire.us › blog › 2015 › 03 › 04 › mfgames-culture-api-countries captured on 2022-04-29 at 12:26:26. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2022-04-28)
➡️ Next capture (2022-06-03)
-=-=-=-=-=-=-
Continuing my effort to document my new library, this is the second part of a short series on MfGames Culture CIL[1], a C# library for globalization written with the intent of supporting non-standard worlds.
1: https://github.com/dmoonifre/mfgames-culture-cil/
I haven't had much feedback on this library, but the one I did pointed out that I use a “semi” Singleton[2] pattern. Now, singletons have gotten a lot of bad press lately but I don't feel that a blanket statement of “singletons are bad” is the best answer either.
2: http://en.wikipedia.org/wiki/Singleton_pattern
Part of the reason I'm writing this is because `System.Globalization` is a singleton. There is no way to replace the functionality and I can't easily inject my own codes into the system. The need for that flexibility is one reason I've created this is to get around that limitation.
The biggest different between MfGames Culture CIL and `System.Globalization` is this:
var manager = CodeManager.Instance; CodeManager.Instance = new CodeManager(); CodeManager.Instance.Languages = new CustomLanguageCodeManager();
While CodeManager is a concrete type, all of the parameters inside the manager are interfaces. This means that the entire implementation can be replaced without breaking the rest of the system, assuming the interface contract is maintained.
I consider this an acceptable use of the Singleton pattern because it allows you to replace the singleton with a mock- or testing-specific implementation.
Also, working with instance members means that one could use Dependency Injection[3] (DI) to provide the code managers without using the `CodeManager` class entirely.
3: http://en.wikipedia.org/wiki/Dependency_injection
public void SomeProcess(ILanguageCodeManager languages) {}
While others could use this:
public void SomeProcess() { languageCode = CodeManager.Languages.GetIsoAlpha3("eng"); }
So, for those who use DI, they ignore the static instance and inject the code. For those who don't need that flexibility, they can use the static instance. I think this allows for either style of developing without declaring the One True Way™ which I'm not fond of.
All of the namespaces for this example are `MfGames.Culture`.
using MfGames.Culture;
You may have noticed that I introduced `CodeManager` while dropping the `Instance` properties of `LanguageCodeManager`, `CountryCodeManager`, and `ScriptCodeManager`. I had two reasons for this. The first is I was getting a lot more code managers into the system and it was getting overwhelming. The second is Single Responsibility Principle[4] (SRP). `CodeManager` is purely to handle singleton management of the code managers; this pulls that logic out of the individual managers and keeps their functions pure.
4: http://en.wikipedia.org/wiki/Single_responsibility_principle
Like the language codes, I needed country codes for some later functions. I'm also using standard codes, in this case ISO 3166[5] which defines standard two- and three-character abbreviations for countries. For example, the Republic of Turkey has a two-character code of `TR` and a three-character one of `TUR`. Country codes are typically uppercase.
5: http://en.wikipedia.org/wiki/ISO_3166
In addition, 3166 defines a numerical code for systems that don't have character-based codes.
Country codes are in the `CountryCode` class and work much like `LanguageCode` when it comes to case comparison.
CountryCode turkey1 = new CountryCode("TR", "TUR"); CountryCode turkey2 = new CountryCode("tr", "tur"); Assert.AreEqual(turkey1, turkey2);
The `ToString()` method prefers two-character codes over three.
CountryCode turkey1 = new CountryCode("TR", "TUR"); CountryCode turkey2 = new CountryCode("tr", "tur"); CountryCode turkey3 = new CountryCode(null, "tur"); Assert.AreEqual("TR", turkey1); Assert.AreEqual("TR", turkey2); Assert.AreEqual("TUR", turkey3);
The real power of the system comes from `CountryCodeManager` which provides a single place of access for the known country codes. You can get a `CountryCodeManager` via `CodeManager` or directly.
ICountryCodeManager countries1 = CodeManager.Instance.Countries; ICountryCodeManager countries2 = new CountryCodeManager();
A default set of countries (basically the known list) is embedded into the DLL as a resource. For the `CountryCodeManager` loaded in the default `CodeManager`, it is already loaded but for new instances, the `AddDefaults()` method needs to be called.
ICountryCodeManager countries2 = new CountryCodeManager(); countries2.AddDefaults();
Retrieving codes is pretty simple. The `Get` method tries all known codes while `GetIsoAlpha2` only checks the two-character codes. I did this to allow for a generic system (`Get`) or something being specific. If a code cannot be found, this returns null.
CountryCode turkey1 = countries.Get("TR"); CountryCode turkey2 = countries.Get("TUR"); CountryCode turkey3 = countries.Get("792"); CountryCode turkey4 = countries.Get(792); CountryCode turkey5 = countries.GetIsoAlpha2("TR"); CountryCode turkey6 = countries.GetIsoAlpha3("TUR"); CountryCode turkey7 = countries.GetIsoNumeric(792);
The following topics are coming up.
6: http://en.wikipedia.org/wiki/ISO_15924
7: http://en.wikipedia.org/wiki/IETF_language_tag
Categories:
Tags:
Below are various useful links within this site and to related sites (not all have been converted over to Gemini).
https://d.moonfire.us/blog/2015/03/04/mfgames-culture-api-countries/