Rozszerzając @ FrancescoLorenzetti84 odpowiedź, jeden sposób robiłem to w przeszłości, aby łatwiej utrzymać się zawinąć odzyskiwanie bazy danych w klasie ResourceString tak, że można zrobić coś takiego:
private static readonly ResourceString res = "The value";
, a następnie odnieść się do tego w kodzie. Za sceną klasa ResourceString wykonuje pracę. Oto przykład, że:
namespace ResString
{
public interface IResourceResolver
{
string Resolve(string key, string defaultValue);
}
public class ResourceString
{
public ResourceString(string value)
{
this.defaultValue = value;
GetOwner();
}
public string Value
{
get
{
if (!resolved)
Resolve();
return value;
}
}
public override string ToString()
{
return Value;
}
public static implicit operator string(ResourceString rhs)
{
return rhs.Value;
}
public static implicit operator ResourceString(string rhs)
{
return new ResourceString(rhs);
}
protected virtual void Resolve()
{
if (Resolver != null)
{
if (key == null)
key = GetKey();
value = Resolver.Resolve(key, defaultValue);
}
else
{
value = defaultValue;
}
resolved = true;
}
[MethodImpl(MethodImplOptions.NoInlining)]
protected virtual void GetOwner()
{
StackTrace trace = new StackTrace();
StackFrame frame = null;
int i = 1;
while (i < trace.FrameCount && (owner == null || typeof(ResourceString).IsAssignableFrom(owner)))
{
frame = trace.GetFrame(i);
MethodBase meth = frame.GetMethod();
owner = meth.DeclaringType;
i++;
}
}
protected virtual string GetKey()
{
string result = owner.FullName;
FieldInfo field = owner.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(f =>
typeof(ResourceString).IsAssignableFrom(f.FieldType) && f.GetValue(null) == this
).FirstOrDefault();
if (field != null)
result += "." + field.Name;
return result;
}
public static IResourceResolver Resolver { get; set; }
private string defaultValue;
private string value;
private bool resolved;
private string key;
private Type owner;
}
}
i przykładowy program:
namespace ResString
{
class Program
{
/// <summary>
/// Description for the first resource.
/// </summary>
private static readonly ResourceString firstRes = "First";
/// <summary>
/// Description for the second resource.
/// </summary>
private static readonly ResourceString secondRes = "Second";
/// <summary>
/// Description for the format string.
/// </summary>
private static readonly ResourceString format = "{0} {1}";
static void Main(string[] args)
{
ResourceString.Resolver = new French();
Console.WriteLine(String.Format(format, firstRes, secondRes));
}
private class French : IResourceResolver
{
public string Resolve(string key, string defaultValue)
{
switch (key)
{
case "ResString.Program.firstRes":
return "Premier";
case "ResString.Program.secondRes":
return "Deuxième";
case "ResString.Program.format":
return "{1} {0}";
}
return defaultValue;
}
}
}
}
Jeśli prowadzisz, że to będzie wyjście: Deuxième Premier
komentarz na przypisanie przelicznika i będziesz get: Pierwsza sekunda
Dowolne miejsce, w którym można użyć ciągu znaków w interfejsie użytkownika, należy użyć zadeklarowanej metody ResourceString.
Zmiana przelicznika po rozwiązaniu wartości łańcuchowych nie zmieni ich wartości, ponieważ wartości są pobierane tylko raz. Będziesz oczywiście musiał napisać prawdziwy resolver, który pobiera z bazy danych.
Potrzebny jest program narzędziowy do uruchamiania skompilowanych klas i wyciągania deklaracji ResourceString oraz umieszczania klucza i wartości domyślnych w bazie danych lub pliku tekstowym, aby można je było przetłumaczyć. Powinno to również przejść przez wygenerowane pliki XML pomocy dla każdego zestawu i wyciągnąć komentarz do deklaracji ResourceString, aby tłumacz mógł korzystać z kontekstu. Kluczowe deklaracje będą również zawierać kontekst, ponieważ możesz łatwo grupować zasoby według klasy interfejsu użytkownika.
Dodaj to do skryptu budowy, upewnij się, że jest regularnie aktualizowany.
Możesz użyć tego samego podejścia do obrazów i tym podobnych.
Najprawdopodobniej będziesz musiał zaimplementować własne rozwiązanie, ale jest to proste. Tylko niektóre tabele w bazie danych z identyfikatorem zasobu, kulturą, marką (zerową), wartością, z powrotem do wartości domyślnych (nie każdy zasób powinien być zlokalizowany dla każdej marki). I oczywiście pamięć podręczna, która po uruchomieniu aplikacji w pamięci. Przechowywanie takich rzeczy razem z bazą kodu nie jest dobrym pomysłem - mogą wymagać aktualizacji o wiele więcej \ mniej często niż kod. – Evk