Lang/Loc.cs
using System;
using System.Collections.Generic;
namespace Science;
/// <summary>One UI language: its code, its own name for the language list, the font that has its glyphs, and its phrases.</summary>
public sealed class LangInfo
{
public string Code;
/// <summary>Written in the language itself, so a player can always find their own.</summary>
public string Name;
/// <summary>HUD class that switches the font. Roboto Condensed covers Latin and Cyrillic; Japanese needs Mochiy Pop One.</summary>
public string FontClass;
public Func<Dictionary<string, string>> Table;
}
/// <summary>
/// UI text in every supported language. English is the master table and the fallback for anything another table
/// lacks. Phrases use {0}-style placeholders and get their numbers already formatted. Tools/check_loc.py checks
/// every table for missing keys, placeholders, glyphs and label widths.
/// </summary>
public static partial class Loc
{
public static readonly LangInfo[] Languages =
{
new() { Code = "en", Name = "English", FontClass = "font-main", Table = English },
new() { Code = "es", Name = "Español", FontClass = "font-main", Table = Spanish },
new() { Code = "fr", Name = "Français", FontClass = "font-main", Table = French },
new() { Code = "de", Name = "Deutsch", FontClass = "font-main", Table = German },
new() { Code = "it", Name = "Italiano", FontClass = "font-main", Table = Italian },
new() { Code = "pt", Name = "Português (Brasil)", FontClass = "font-main", Table = Portuguese },
new() { Code = "pl", Name = "Polski", FontClass = "font-main", Table = Polish },
new() { Code = "tr", Name = "Türkçe", FontClass = "font-main", Table = Turkish },
new() { Code = "ru", Name = "Русский", FontClass = "font-main", Table = Russian },
new() { Code = "ja", Name = "日本語", FontClass = "font-mochiy", Table = Japanese },
};
static Dictionary<string, string> _english;
static Dictionary<string, string> _current;
public static LangInfo Current { get; private set; } = Languages[0];
public static string Code => Current.Code;
/// <summary>Bumped whenever the language changes, so the HUD knows to redraw.</summary>
public static int Version { get; private set; }
static Dictionary<string, string> EnglishTable => _english ??= English();
public static LangInfo Find( string code )
{
if ( string.IsNullOrEmpty( code ) ) return null;
code = code.Trim().ToLowerInvariant().Replace( '_', '-' );
foreach ( var l in Languages ) if ( l.Code == code ) return l;
// "pt-br", "es-419", "en-gb" and the like match on the language alone
int dash = code.IndexOf( '-' );
if ( dash > 0 )
{
var root = code.Substring( 0, dash );
foreach ( var l in Languages ) if ( l.Code == root ) return l;
}
return null;
}
/// <summary>Switch language. Unknown codes fall back to English.</summary>
public static void Set( string code )
{
var lang = Find( code ) ?? Languages[0];
if ( lang == Current && _current != null ) return;
Current = lang;
_current = lang.Code == "en" ? EnglishTable : lang.Table();
Version++;
}
/// <summary>The phrase for key in the current language, else English, else the key itself.</summary>
public static string T( string key )
{
if ( key == null ) return "";
_current ??= EnglishTable;
if ( _current.TryGetValue( key, out var s ) && !string.IsNullOrEmpty( s ) ) return s;
if ( EnglishTable.TryGetValue( key, out var e ) ) return e;
return key;
}
public static string T( string key, params object[] args )
{
var phrase = T( key );
try
{
return string.Format( phrase, args );
}
catch ( FormatException )
{
// a broken translation must never take the HUD down: show the English phrase instead
try { return string.Format( EnglishTable.TryGetValue( key, out var e ) ? e : key, args ); }
catch ( FormatException ) { return phrase; }
}
}
/// <summary>Key form of an internal English id: "Salt dissolve" becomes "salt_dissolve".</summary>
public static string Slug( string id ) => (id ?? "").Trim().ToLowerInvariant().Replace( ' ', '_' );
/// <summary>Upper case that respects Turkish dotted/dotless i, for headings.</summary>
public static string Upper( string s )
{
if ( string.IsNullOrEmpty( s ) ) return s ?? "";
if ( Code == "tr" ) s = s.Replace( 'i', 'İ' );
return s.ToUpperInvariant();
}
}