Merge pull request #3929 from ScottNZ/translation
Add translation support
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
#region Copyright & License Information
|
#region Copyright & License Information
|
||||||
/*
|
/*
|
||||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
||||||
* This file is part of OpenRA, which is free software. It is made
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
* available to you under the terms of the GNU General Public License
|
* available to you under the terms of the GNU General Public License
|
||||||
* as published by the Free Software Foundation. For more information,
|
* as published by the Free Software Foundation. For more information,
|
||||||
@@ -14,6 +14,7 @@ using System.Drawing;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace OpenRA.FileFormats
|
namespace OpenRA.FileFormats
|
||||||
{
|
{
|
||||||
@@ -38,31 +39,31 @@ namespace OpenRA.FileFormats
|
|||||||
object val;
|
object val;
|
||||||
if (kv.Value != null)
|
if (kv.Value != null)
|
||||||
val = kv.Value(kv.Key.Name, kv.Key.FieldType, my);
|
val = kv.Value(kv.Key.Name, kv.Key.FieldType, my);
|
||||||
else if (!TryGetValueFromYaml(kv.Key.Name, kv.Key.FieldType, my, out val))
|
else if (!TryGetValueFromYaml(kv.Key, my, out val))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
kv.Key.SetValue(self, val);
|
kv.Key.SetValue(self, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TryGetValueFromYaml(string fieldName, Type fieldType, MiniYaml yaml, out object ret)
|
static bool TryGetValueFromYaml(FieldInfo field, MiniYaml yaml, out object ret)
|
||||||
{
|
{
|
||||||
ret = null;
|
ret = null;
|
||||||
var n = yaml.Nodes.Where(x => x.Key == fieldName).ToList();
|
var n = yaml.Nodes.Where(x => x.Key == field.Name).ToList();
|
||||||
if (n.Count == 0)
|
if (n.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
if (n.Count == 1 && n[0].Value.Nodes.Count == 0)
|
if (n.Count == 1 && n[0].Value.Nodes.Count == 0)
|
||||||
{
|
{
|
||||||
ret = GetValue(fieldName, fieldType, n[0].Value.Value);
|
ret = GetValue(field.Name, field.FieldType, n[0].Value.Value, field);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (n.Count > 1)
|
else if (n.Count > 1)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The field {0} has multiple definitions:\n{1}"
|
throw new InvalidOperationException("The field {0} has multiple definitions:\n{1}"
|
||||||
.F(fieldName, n.Select(m => "\t- " + m.Location).JoinWith("\n")));
|
.F(field.Name, n.Select(m => "\t- " + m.Location).JoinWith("\n")));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("TryGetValueFromYaml: unable to load field {0} (of type {1})".F(fieldName, fieldType));
|
throw new InvalidOperationException("TryGetValueFromYaml: unable to load field {0} (of type {1})".F(field.Name, field.FieldType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Load<T>(MiniYaml y) where T : new()
|
public static T Load<T>(MiniYaml y) where T : new()
|
||||||
@@ -80,7 +81,7 @@ namespace OpenRA.FileFormats
|
|||||||
if (field != null)
|
if (field != null)
|
||||||
{
|
{
|
||||||
if (!field.HasAttribute<FieldFromYamlKeyAttribute>())
|
if (!field.HasAttribute<FieldFromYamlKeyAttribute>())
|
||||||
field.SetValue(self, GetValue(field.Name, field.FieldType, value));
|
field.SetValue(self, GetValue(field.Name, field.FieldType, value, field));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ namespace OpenRA.FileFormats
|
|||||||
if (prop != null)
|
if (prop != null)
|
||||||
{
|
{
|
||||||
if (!prop.HasAttribute<FieldFromYamlKeyAttribute>())
|
if (!prop.HasAttribute<FieldFromYamlKeyAttribute>())
|
||||||
prop.SetValue(self, GetValue(prop.Name, prop.PropertyType, value), NoIndexes);
|
prop.SetValue(self, GetValue(prop.Name, prop.PropertyType, value, prop), NoIndexes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,61 +99,70 @@ namespace OpenRA.FileFormats
|
|||||||
|
|
||||||
public static T GetValue<T>(string field, string value)
|
public static T GetValue<T>(string field, string value)
|
||||||
{
|
{
|
||||||
return (T)GetValue(field, typeof(T), value);
|
return (T)GetValue(field, typeof(T), value, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object GetValue(string field, Type fieldType, string x)
|
public static object GetValue(string fieldName, Type fieldType, string value)
|
||||||
{
|
{
|
||||||
if (x != null) x = x.Trim();
|
return GetValue(fieldName, fieldType, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object GetValue(string fieldName, Type fieldType, string value, MemberInfo field)
|
||||||
|
{
|
||||||
|
if (value != null) value = value.Trim();
|
||||||
|
|
||||||
if (fieldType == typeof(int))
|
if (fieldType == typeof(int))
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
if (int.TryParse(x, out res))
|
if (int.TryParse(value, out res))
|
||||||
return res;
|
return res;
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(ushort))
|
else if (fieldType == typeof(ushort))
|
||||||
{
|
{
|
||||||
ushort res;
|
ushort res;
|
||||||
if (ushort.TryParse(x, out res))
|
if (ushort.TryParse(value, out res))
|
||||||
return res;
|
return res;
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(float))
|
else if (fieldType == typeof(float))
|
||||||
{
|
{
|
||||||
float res;
|
float res;
|
||||||
if (float.TryParse(x.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
|
if (float.TryParse(value.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
|
||||||
return res * (x.Contains('%') ? 0.01f : 1f);
|
return res * (value.Contains('%') ? 0.01f : 1f);
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(decimal))
|
else if (fieldType == typeof(decimal))
|
||||||
{
|
{
|
||||||
decimal res;
|
decimal res;
|
||||||
if (decimal.TryParse(x.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
|
if (decimal.TryParse(value.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
|
||||||
return res * (x.Contains('%') ? 0.01m : 1m);
|
return res * (value.Contains('%') ? 0.01m : 1m);
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(string))
|
else if (fieldType == typeof(string))
|
||||||
return x;
|
{
|
||||||
|
if (field != null && field.HasAttribute<TranslateAttribute>())
|
||||||
|
return Regex.Replace(value, "@[^@]+@", m => Translate(m.Value.Substring(1, m.Value.Length - 2)), RegexOptions.Compiled);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(Color))
|
else if (fieldType == typeof(Color))
|
||||||
{
|
{
|
||||||
var parts = x.Split(',');
|
var parts = value.Split(',');
|
||||||
if (parts.Length == 3)
|
if (parts.Length == 3)
|
||||||
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255));
|
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255));
|
||||||
if (parts.Length == 4)
|
if (parts.Length == 4)
|
||||||
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255), int.Parse(parts[3]).Clamp(0, 255));
|
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255), int.Parse(parts[3]).Clamp(0, 255));
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(HSLColor))
|
else if (fieldType == typeof(HSLColor))
|
||||||
{
|
{
|
||||||
var parts = x.Split(',');
|
var parts = value.Split(',');
|
||||||
|
|
||||||
// Allow old ColorRamp format to be parsed as HSLColor
|
// Allow old ColorRamp format to be parsed as HSLColor
|
||||||
if (parts.Length == 3 || parts.Length == 4)
|
if (parts.Length == 3 || parts.Length == 4)
|
||||||
@@ -161,21 +171,21 @@ namespace OpenRA.FileFormats
|
|||||||
(byte)int.Parse(parts[1]).Clamp(0, 255),
|
(byte)int.Parse(parts[1]).Clamp(0, 255),
|
||||||
(byte)int.Parse(parts[2]).Clamp(0, 255));
|
(byte)int.Parse(parts[2]).Clamp(0, 255));
|
||||||
|
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(WRange))
|
else if (fieldType == typeof(WRange))
|
||||||
{
|
{
|
||||||
WRange res;
|
WRange res;
|
||||||
if (WRange.TryParse(x, out res))
|
if (WRange.TryParse(value, out res))
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(WVec))
|
else if (fieldType == typeof(WVec))
|
||||||
{
|
{
|
||||||
var parts = x.Split(',');
|
var parts = value.Split(',');
|
||||||
if (parts.Length == 3)
|
if (parts.Length == 3)
|
||||||
{
|
{
|
||||||
WRange rx, ry, rz;
|
WRange rx, ry, rz;
|
||||||
@@ -183,12 +193,12 @@ namespace OpenRA.FileFormats
|
|||||||
return new WVec(rx, ry, rz);
|
return new WVec(rx, ry, rz);
|
||||||
}
|
}
|
||||||
|
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(WPos))
|
else if (fieldType == typeof(WPos))
|
||||||
{
|
{
|
||||||
var parts = x.Split(',');
|
var parts = value.Split(',');
|
||||||
if (parts.Length == 3)
|
if (parts.Length == 3)
|
||||||
{
|
{
|
||||||
WRange rx, ry, rz;
|
WRange rx, ry, rz;
|
||||||
@@ -196,62 +206,62 @@ namespace OpenRA.FileFormats
|
|||||||
return new WPos(rx, ry, rz);
|
return new WPos(rx, ry, rz);
|
||||||
}
|
}
|
||||||
|
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(WAngle))
|
else if (fieldType == typeof(WAngle))
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
if (int.TryParse(x, out res))
|
if (int.TryParse(value, out res))
|
||||||
return new WAngle(res);
|
return new WAngle(res);
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(WRot))
|
else if (fieldType == typeof(WRot))
|
||||||
{
|
{
|
||||||
var parts = x.Split(',');
|
var parts = value.Split(',');
|
||||||
if (parts.Length == 3)
|
if (parts.Length == 3)
|
||||||
{
|
{
|
||||||
int rr, rp, ry;
|
int rr, rp, ry;
|
||||||
if (int.TryParse(x, out rr) && int.TryParse(x, out rp) && int.TryParse(x, out ry))
|
if (int.TryParse(value, out rr) && int.TryParse(value, out rp) && int.TryParse(value, out ry))
|
||||||
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
|
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
|
||||||
}
|
}
|
||||||
|
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType.IsEnum)
|
else if (fieldType.IsEnum)
|
||||||
{
|
{
|
||||||
if (!Enum.GetNames(fieldType).Select(a => a.ToLower()).Contains(x.ToLower()))
|
if (!Enum.GetNames(fieldType).Select(a => a.ToLower()).Contains(value.ToLower()))
|
||||||
return InvalidValueAction(x, fieldType, field);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
return Enum.Parse(fieldType, x, true);
|
return Enum.Parse(fieldType, value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(bool))
|
else if (fieldType == typeof(bool))
|
||||||
return ParseYesNo(x, fieldType, field);
|
return ParseYesNo(value, fieldType, fieldName);
|
||||||
|
|
||||||
else if (fieldType.IsArray)
|
else if (fieldType.IsArray)
|
||||||
{
|
{
|
||||||
if (x == null)
|
if (value == null)
|
||||||
return Array.CreateInstance(fieldType.GetElementType(), 0);
|
return Array.CreateInstance(fieldType.GetElementType(), 0);
|
||||||
|
|
||||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length);
|
var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length);
|
||||||
for (int i = 0; i < parts.Length; i++)
|
for (int i = 0; i < parts.Length; i++)
|
||||||
ret.SetValue(GetValue(field, fieldType.GetElementType(), parts[i].Trim()), i);
|
ret.SetValue(GetValue(fieldName, fieldType.GetElementType(), parts[i].Trim(), field), i);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(int2))
|
else if (fieldType == typeof(int2))
|
||||||
{
|
{
|
||||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
return new int2(int.Parse(parts[0]), int.Parse(parts[1]));
|
return new int2(int.Parse(parts[0]), int.Parse(parts[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType == typeof(float2))
|
else if (fieldType == typeof(float2))
|
||||||
{
|
{
|
||||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
float xx = 0;
|
float xx = 0;
|
||||||
float yy = 0;
|
float yy = 0;
|
||||||
float res;
|
float res;
|
||||||
@@ -264,13 +274,13 @@ namespace OpenRA.FileFormats
|
|||||||
|
|
||||||
else if (fieldType == typeof(Rectangle))
|
else if (fieldType == typeof(Rectangle))
|
||||||
{
|
{
|
||||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
return new Rectangle(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
|
return new Rectangle(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Bits<>))
|
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Bits<>))
|
||||||
{
|
{
|
||||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
var argTypes = new Type[] { typeof(string[]) };
|
var argTypes = new Type[] { typeof(string[]) };
|
||||||
var argValues = new object[] { parts };
|
var argValues = new object[] { parts };
|
||||||
return fieldType.GetConstructor(argTypes).Invoke(argValues);
|
return fieldType.GetConstructor(argTypes).Invoke(argValues);
|
||||||
@@ -279,11 +289,11 @@ namespace OpenRA.FileFormats
|
|||||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||||
{
|
{
|
||||||
var innerType = fieldType.GetGenericArguments().First();
|
var innerType = fieldType.GetGenericArguments().First();
|
||||||
var innerValue = GetValue("Nullable<T>", innerType, x);
|
var innerValue = GetValue("Nullable<T>", innerType, value, field);
|
||||||
return fieldType.GetConstructor(new []{ innerType }).Invoke(new []{ innerValue });
|
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
UnknownFieldAction("[Type] {0}".F(x), fieldType);
|
UnknownFieldAction("[Type] {0}".F(value), fieldType);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +322,7 @@ namespace OpenRA.FileFormats
|
|||||||
if (loadUsing.Length != 0)
|
if (loadUsing.Length != 0)
|
||||||
ret[field] = (_1, fieldType, yaml) => loadUsing[0].LoaderFunc(field)(yaml);
|
ret[field] = (_1, fieldType, yaml) => loadUsing[0].LoaderFunc(field)(yaml);
|
||||||
else if (fromYamlKey.Length != 0)
|
else if (fromYamlKey.Length != 0)
|
||||||
ret[field] = (f, ft, yaml) => GetValue(f, ft, yaml.Value);
|
ret[field] = (f, ft, yaml) => GetValue(f, ft, yaml.Value, field);
|
||||||
else if (ignore.Length == 0)
|
else if (ignore.Length == 0)
|
||||||
ret[field] = null;
|
ret[field] = null;
|
||||||
}
|
}
|
||||||
@@ -342,81 +352,24 @@ namespace OpenRA.FileFormats
|
|||||||
return loaderFuncCache;
|
return loaderFuncCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string Translate(string key)
|
||||||
|
{
|
||||||
|
if (Translations == null || string.IsNullOrEmpty(key))
|
||||||
|
return key;
|
||||||
|
|
||||||
|
string value;
|
||||||
|
if (!Translations.TryGetValue(key, out value))
|
||||||
|
return key;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, string> Translations = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FieldSaver
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||||
{
|
public class TranslateAttribute : Attribute { }
|
||||||
public static MiniYaml Save(object o)
|
|
||||||
{
|
|
||||||
var nodes = new List<MiniYamlNode>();
|
|
||||||
string root = null;
|
|
||||||
|
|
||||||
foreach (var f in o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
|
|
||||||
{
|
|
||||||
if (f.HasAttribute<FieldFromYamlKeyAttribute>())
|
|
||||||
root = FormatValue(o, f);
|
|
||||||
else
|
|
||||||
nodes.Add(new MiniYamlNode(f.Name, FormatValue(o, f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MiniYaml(root, nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MiniYaml SaveDifferences(object o, object from)
|
|
||||||
{
|
|
||||||
if (o.GetType() != from.GetType())
|
|
||||||
throw new InvalidOperationException("FieldLoader: can't diff objects of different types");
|
|
||||||
|
|
||||||
var fields = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
|
|
||||||
.Where(f => FormatValue(o, f) != FormatValue(from, f));
|
|
||||||
|
|
||||||
return new MiniYaml(null, fields.Select(f => new MiniYamlNode(f.Name, FormatValue(o, f))).ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MiniYamlNode SaveField(object o, string field)
|
|
||||||
{
|
|
||||||
return new MiniYamlNode(field, FieldSaver.FormatValue(o, o.GetType().GetField(field)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatValue(object v, Type t)
|
|
||||||
{
|
|
||||||
if (v == null)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
// Color.ToString() does the wrong thing; force it to format as an array
|
|
||||||
if (t == typeof(Color))
|
|
||||||
{
|
|
||||||
var c = (Color)v;
|
|
||||||
return "{0},{1},{2},{3}".F(((int)c.A).Clamp(0, 255),
|
|
||||||
((int)c.R).Clamp(0, 255),
|
|
||||||
((int)c.G).Clamp(0, 255),
|
|
||||||
((int)c.B).Clamp(0, 255));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't save floats in settings.yaml using country-specific decimal separators which can be misunderstood as group seperators.
|
|
||||||
if (t == typeof(float))
|
|
||||||
return ((float)v).ToString(CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
if (t == typeof(Rectangle))
|
|
||||||
{
|
|
||||||
var r = (Rectangle)v;
|
|
||||||
return "{0},{1},{2},{3}".F(r.X, r.Y, r.Width, r.Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.IsArray)
|
|
||||||
{
|
|
||||||
var elems = ((Array)v).OfType<object>();
|
|
||||||
return elems.JoinWith(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatValue(object o, FieldInfo f)
|
|
||||||
{
|
|
||||||
return FormatValue(f.GetValue(o), f.FieldType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FieldFromYamlKeyAttribute : Attribute { }
|
public class FieldFromYamlKeyAttribute : Attribute { }
|
||||||
|
|
||||||
|
|||||||
93
OpenRA.FileFormats/FieldSaver.cs
Normal file
93
OpenRA.FileFormats/FieldSaver.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation. For more information,
|
||||||
|
* see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace OpenRA.FileFormats
|
||||||
|
{
|
||||||
|
public static class FieldSaver
|
||||||
|
{
|
||||||
|
public static MiniYaml Save(object o)
|
||||||
|
{
|
||||||
|
var nodes = new List<MiniYamlNode>();
|
||||||
|
string root = null;
|
||||||
|
|
||||||
|
foreach (var f in o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
|
||||||
|
{
|
||||||
|
if (f.HasAttribute<FieldFromYamlKeyAttribute>())
|
||||||
|
root = FormatValue(o, f);
|
||||||
|
else
|
||||||
|
nodes.Add(new MiniYamlNode(f.Name, FormatValue(o, f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MiniYaml(root, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MiniYaml SaveDifferences(object o, object from)
|
||||||
|
{
|
||||||
|
if (o.GetType() != from.GetType())
|
||||||
|
throw new InvalidOperationException("FieldLoader: can't diff objects of different types");
|
||||||
|
|
||||||
|
var fields = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Where(f => FormatValue(o, f) != FormatValue(from, f));
|
||||||
|
|
||||||
|
return new MiniYaml(null, fields.Select(f => new MiniYamlNode(f.Name, FormatValue(o, f))).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MiniYamlNode SaveField(object o, string field)
|
||||||
|
{
|
||||||
|
return new MiniYamlNode(field, FieldSaver.FormatValue(o, o.GetType().GetField(field)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatValue(object v, Type t)
|
||||||
|
{
|
||||||
|
if (v == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
// Color.ToString() does the wrong thing; force it to format as an array
|
||||||
|
if (t == typeof(Color))
|
||||||
|
{
|
||||||
|
var c = (Color)v;
|
||||||
|
return "{0},{1},{2},{3}".F(((int)c.A).Clamp(0, 255),
|
||||||
|
((int)c.R).Clamp(0, 255),
|
||||||
|
((int)c.G).Clamp(0, 255),
|
||||||
|
((int)c.B).Clamp(0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't save floats in settings.yaml using country-specific decimal separators which can be misunderstood as group seperators.
|
||||||
|
if (t == typeof(float))
|
||||||
|
return ((float)v).ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
if (t == typeof(Rectangle))
|
||||||
|
{
|
||||||
|
var r = (Rectangle)v;
|
||||||
|
return "{0},{1},{2},{3}".F(r.X, r.Y, r.Width, r.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.IsArray)
|
||||||
|
{
|
||||||
|
var elems = ((Array)v).OfType<object>();
|
||||||
|
return elems.JoinWith(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatValue(object o, FieldInfo f)
|
||||||
|
{
|
||||||
|
return FormatValue(f.GetValue(o), f.FieldType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ namespace OpenRA.FileFormats
|
|||||||
public readonly string[]
|
public readonly string[]
|
||||||
Mods, Folders, Rules, ServerTraits,
|
Mods, Folders, Rules, ServerTraits,
|
||||||
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||||
Weapons, Voices, Notifications, Music, Movies, TileSets,
|
Weapons, Voices, Notifications, Music, Movies, Translations, TileSets,
|
||||||
ChromeMetrics, PackageContents;
|
ChromeMetrics, PackageContents;
|
||||||
|
|
||||||
public readonly Dictionary<string, string> Packages;
|
public readonly Dictionary<string, string> Packages;
|
||||||
@@ -53,6 +53,7 @@ namespace OpenRA.FileFormats
|
|||||||
Notifications = YamlList(yaml, "Notifications");
|
Notifications = YamlList(yaml, "Notifications");
|
||||||
Music = YamlList(yaml, "Music");
|
Music = YamlList(yaml, "Music");
|
||||||
Movies = YamlList(yaml, "Movies");
|
Movies = YamlList(yaml, "Movies");
|
||||||
|
Translations = YamlList(yaml, "Translations");
|
||||||
TileSets = YamlList(yaml, "TileSets");
|
TileSets = YamlList(yaml, "TileSets");
|
||||||
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
||||||
PackageContents = YamlList(yaml, "PackageContents");
|
PackageContents = YamlList(yaml, "PackageContents");
|
||||||
|
|||||||
@@ -80,6 +80,7 @@
|
|||||||
<Compile Include="Evaluator.cs" />
|
<Compile Include="Evaluator.cs" />
|
||||||
<Compile Include="Exts.cs" />
|
<Compile Include="Exts.cs" />
|
||||||
<Compile Include="FieldLoader.cs" />
|
<Compile Include="FieldLoader.cs" />
|
||||||
|
<Compile Include="FieldSaver.cs" />
|
||||||
<Compile Include="FileFormats\AudLoader.cs" />
|
<Compile Include="FileFormats\AudLoader.cs" />
|
||||||
<Compile Include="FileFormats\Blast.cs" />
|
<Compile Include="FileFormats\Blast.cs" />
|
||||||
<Compile Include="FileFormats\Blowfish.cs" />
|
<Compile Include="FileFormats\Blowfish.cs" />
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ namespace OpenRA.GameRules
|
|||||||
public int BatchSize = 8192;
|
public int BatchSize = 8192;
|
||||||
public int NumTempBuffers = 8;
|
public int NumTempBuffers = 8;
|
||||||
public int SheetSize = 2048;
|
public int SheetSize = 2048;
|
||||||
|
|
||||||
|
public string Language = "english";
|
||||||
|
public string DefaultLanguage = "english";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SoundSettings
|
public class SoundSettings
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ namespace OpenRA
|
|||||||
[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
|
[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
|
||||||
[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
|
[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
|
||||||
[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
|
[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
|
||||||
|
[FieldLoader.Ignore] public List<MiniYamlNode> Translations = new List<MiniYamlNode>();
|
||||||
|
|
||||||
// Binary map data
|
// Binary map data
|
||||||
[FieldLoader.Ignore] public byte TileFormat = 1;
|
[FieldLoader.Ignore] public byte TileFormat = 1;
|
||||||
@@ -191,6 +192,7 @@ namespace OpenRA
|
|||||||
Weapons = MiniYaml.NodesOrEmpty(yaml, "Weapons");
|
Weapons = MiniYaml.NodesOrEmpty(yaml, "Weapons");
|
||||||
Voices = MiniYaml.NodesOrEmpty(yaml, "Voices");
|
Voices = MiniYaml.NodesOrEmpty(yaml, "Voices");
|
||||||
Notifications = MiniYaml.NodesOrEmpty(yaml, "Notifications");
|
Notifications = MiniYaml.NodesOrEmpty(yaml, "Notifications");
|
||||||
|
Translations = MiniYaml.NodesOrEmpty(yaml, "Translations");
|
||||||
|
|
||||||
CustomTerrain = new string[MapSize.X, MapSize.Y];
|
CustomTerrain = new string[MapSize.X, MapSize.Y];
|
||||||
|
|
||||||
@@ -247,6 +249,7 @@ namespace OpenRA
|
|||||||
root.Add(new MiniYamlNode("Weapons", null, Weapons));
|
root.Add(new MiniYamlNode("Weapons", null, Weapons));
|
||||||
root.Add(new MiniYamlNode("Voices", null, Voices));
|
root.Add(new MiniYamlNode("Voices", null, Voices));
|
||||||
root.Add(new MiniYamlNode("Notifications", null, Notifications));
|
root.Add(new MiniYamlNode("Notifications", null, Notifications));
|
||||||
|
root.Add(new MiniYamlNode("Translations", null, Translations));
|
||||||
|
|
||||||
var entries = new Dictionary<string, byte[]>();
|
var entries = new Dictionary<string, byte[]>();
|
||||||
entries.Add("map.bin", SaveBinaryData());
|
entries.Add("map.bin", SaveBinaryData());
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
public ModData(params string[] mods)
|
public ModData(params string[] mods)
|
||||||
{
|
{
|
||||||
|
Languages = new string[0];
|
||||||
Manifest = new Manifest(mods);
|
Manifest = new Manifest(mods);
|
||||||
ObjectCreator = new ObjectCreator(Manifest);
|
ObjectCreator = new ObjectCreator(Manifest);
|
||||||
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
|
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
|
||||||
@@ -71,6 +72,47 @@ namespace OpenRA
|
|||||||
CursorProvider.Initialize(Manifest.Cursors);
|
CursorProvider.Initialize(Manifest.Cursors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> Languages { get; private set; }
|
||||||
|
|
||||||
|
void LoadTranslations(Map map)
|
||||||
|
{
|
||||||
|
var selectedTranslations = new Dictionary<string, string>();
|
||||||
|
var defaultTranslations = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
if (!Manifest.Translations.Any())
|
||||||
|
{
|
||||||
|
Languages = new string[0];
|
||||||
|
FieldLoader.Translations = new Dictionary<string, string>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var yaml = Manifest.Translations.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal);
|
||||||
|
Languages = yaml.Select(t => t.Key).ToArray();
|
||||||
|
|
||||||
|
yaml = MiniYaml.MergeLiberal(map.Translations, yaml);
|
||||||
|
|
||||||
|
foreach (var y in yaml)
|
||||||
|
{
|
||||||
|
if (y.Key == Game.Settings.Graphics.Language)
|
||||||
|
selectedTranslations = y.Value.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value ?? "");
|
||||||
|
if (y.Key == Game.Settings.Graphics.DefaultLanguage)
|
||||||
|
defaultTranslations = y.Value.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
var translations = new Dictionary<string, string>();
|
||||||
|
foreach (var tkv in defaultTranslations.Concat(selectedTranslations))
|
||||||
|
{
|
||||||
|
if (translations.ContainsKey(tkv.Key))
|
||||||
|
continue;
|
||||||
|
if (selectedTranslations.ContainsKey(tkv.Key))
|
||||||
|
translations.Add(tkv.Key, selectedTranslations[tkv.Key]);
|
||||||
|
else
|
||||||
|
translations.Add(tkv.Key, tkv.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldLoader.Translations = translations;
|
||||||
|
}
|
||||||
|
|
||||||
public Map PrepareMap(string uid)
|
public Map PrepareMap(string uid)
|
||||||
{
|
{
|
||||||
LoadScreen.Display();
|
LoadScreen.Display();
|
||||||
@@ -78,6 +120,8 @@ namespace OpenRA
|
|||||||
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
|
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
|
||||||
var map = new Map(AvailableMaps[uid].Path);
|
var map = new Map(AvailableMaps[uid].Path);
|
||||||
|
|
||||||
|
LoadTranslations(map);
|
||||||
|
|
||||||
// Reinit all our assets
|
// Reinit all our assets
|
||||||
InitializeLoaders();
|
InitializeLoaders();
|
||||||
FileSystem.LoadFromManifest(Manifest);
|
FileSystem.LoadFromManifest(Manifest);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace OpenRA.Widgets
|
|||||||
set { GetKey = _ => value; }
|
set { GetKey = _ => value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Text = "";
|
[Translate] public string Text = "";
|
||||||
public bool Depressed = false;
|
public bool Depressed = false;
|
||||||
public int VisualHeight = ChromeMetrics.Get<int>("ButtonDepth");
|
public int VisualHeight = ChromeMetrics.Get<int>("ButtonDepth");
|
||||||
public string Font = ChromeMetrics.Get<string>("ButtonFont");
|
public string Font = ChromeMetrics.Get<string>("ButtonFont");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
|
||||||
namespace OpenRA.Widgets
|
namespace OpenRA.Widgets
|
||||||
@@ -19,7 +20,7 @@ namespace OpenRA.Widgets
|
|||||||
|
|
||||||
public class LabelWidget : Widget
|
public class LabelWidget : Widget
|
||||||
{
|
{
|
||||||
public string Text = null;
|
[Translate] public string Text = null;
|
||||||
public TextAlign Align = TextAlign.Left;
|
public TextAlign Align = TextAlign.Left;
|
||||||
public TextVAlign VAlign = TextVAlign.Middle;
|
public TextVAlign VAlign = TextVAlign.Middle;
|
||||||
public string Font = "Regular";
|
public string Font = "Regular";
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
|||||||
var windowHeight = generalPane.Get<TextFieldWidget>("WINDOW_HEIGHT");
|
var windowHeight = generalPane.Get<TextFieldWidget>("WINDOW_HEIGHT");
|
||||||
windowHeight.Text = graphicsSettings.WindowedSize.Y.ToString();
|
windowHeight.Text = graphicsSettings.WindowedSize.Y.ToString();
|
||||||
|
|
||||||
|
var languageDropDownButton = generalPane.Get<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
|
||||||
|
languageDropDownButton.OnMouseDown = _ => SettingsMenuLogic.ShowLanguageDropdown(languageDropDownButton);
|
||||||
|
languageDropDownButton.GetText = () => FieldLoader.Translate(Game.Settings.Graphics.Language);
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
var soundSlider = generalPane.Get<SliderWidget>("SOUND_SLIDER");
|
var soundSlider = generalPane.Get<SliderWidget>("SOUND_SLIDER");
|
||||||
soundSlider.OnChange += x => { soundSettings.SoundVolume = x; Sound.SoundVolume = x; };
|
soundSlider.OnChange += x => { soundSettings.SoundVolume = x; Sound.SoundVolume = x; };
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
|||||||
nameLabel.GetText = () => tooltip.Name;
|
nameLabel.GetText = () => tooltip.Name;
|
||||||
|
|
||||||
var prereqs = buildable.Prerequisites.Select(a => ActorName(a));
|
var prereqs = buildable.Prerequisites.Select(a => ActorName(a));
|
||||||
var requiresString = prereqs.Any() ? "Requires {0}".F(prereqs.JoinWith(", ")) : "";
|
var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : "";
|
||||||
requiresLabel.GetText = () => requiresString;
|
requiresLabel.GetText = () => requiresString;
|
||||||
|
|
||||||
var power = bi != null ? bi.Power : 0;
|
var power = bi != null ? bi.Power : 0;
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ namespace OpenRA.Mods.Cnc.Widgets
|
|||||||
public readonly string TooltipContainer;
|
public readonly string TooltipContainer;
|
||||||
public readonly string TooltipTemplate = "PRODUCTION_TOOLTIP";
|
public readonly string TooltipTemplate = "PRODUCTION_TOOLTIP";
|
||||||
|
|
||||||
public readonly string ReadyText = "";
|
[Translate] public readonly string ReadyText = "";
|
||||||
public readonly string HoldText = "";
|
[Translate] public readonly string HoldText = "";
|
||||||
|
|
||||||
public string TooltipActor { get; private set; }
|
public string TooltipActor { get; private set; }
|
||||||
public readonly World World;
|
public readonly World World;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace OpenRA.Mods.RA
|
|||||||
[Desc("Shown in the build palette widget.")]
|
[Desc("Shown in the build palette widget.")]
|
||||||
public class TooltipInfo : ITraitInfo
|
public class TooltipInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
public readonly string Description = "";
|
[Translate] public readonly string Description = "";
|
||||||
public readonly string Name = "";
|
[Translate] public readonly string Name = "";
|
||||||
|
|
||||||
[Desc("Sequence of the actor that contains the cameo.")]
|
[Desc("Sequence of the actor that contains the cameo.")]
|
||||||
public readonly string Icon = "icon";
|
public readonly string Icon = "icon";
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ namespace OpenRA.Mods.RA.Widgets
|
|||||||
public int Columns = 3;
|
public int Columns = 3;
|
||||||
public int Rows = 5;
|
public int Rows = 5;
|
||||||
|
|
||||||
public string ReadyText = "";
|
[Translate] public string ReadyText = "";
|
||||||
public string HoldText = "";
|
[Translate] public string HoldText = "";
|
||||||
public string RequiresText = "";
|
[Translate] public string RequiresText = "";
|
||||||
|
|
||||||
public int IconWidth = 64;
|
public int IconWidth = 64;
|
||||||
public int IconHeight = 48;
|
public int IconHeight = 48;
|
||||||
@@ -484,7 +484,7 @@ namespace OpenRA.Mods.RA.Widgets
|
|||||||
var prereqs = buildable.Prerequisites.Select(Description);
|
var prereqs = buildable.Prerequisites.Select(Description);
|
||||||
if (prereqs.Any())
|
if (prereqs.Any())
|
||||||
{
|
{
|
||||||
Game.Renderer.Fonts["Regular"].DrawText("{0} {1}".F(RequiresText, prereqs.JoinWith(", ")), p.ToInt2(), Color.White);
|
Game.Renderer.Fonts["Regular"].DrawText(RequiresText.F(prereqs.JoinWith(", ")), p.ToInt2(), Color.White);
|
||||||
|
|
||||||
p += new int2(0, 8);
|
p += new int2(0, 8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.FileFormats.Graphics;
|
using OpenRA.FileFormats.Graphics;
|
||||||
using OpenRA.GameRules;
|
using OpenRA.GameRules;
|
||||||
using OpenRA.Widgets;
|
using OpenRA.Widgets;
|
||||||
@@ -142,6 +143,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
var maxFrameRate = display.Get<TextFieldWidget>("MAX_FRAMERATE");
|
var maxFrameRate = display.Get<TextFieldWidget>("MAX_FRAMERATE");
|
||||||
maxFrameRate.Text = gs.MaxFramerate.ToString();
|
maxFrameRate.Text = gs.MaxFramerate.ToString();
|
||||||
|
|
||||||
|
var languageDropDownButton = display.Get<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
|
||||||
|
languageDropDownButton.OnMouseDown = _ => ShowLanguageDropdown(languageDropDownButton);
|
||||||
|
languageDropDownButton.GetText = () => FieldLoader.Translate(Game.Settings.Graphics.Language);
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
var keys = bg.Get("KEYS_PANE");
|
var keys = bg.Get("KEYS_PANE");
|
||||||
var keyConfig = Game.Settings.Keys;
|
var keyConfig = Game.Settings.Keys;
|
||||||
@@ -270,6 +275,20 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool ShowLanguageDropdown(DropDownButtonWidget dropdown)
|
||||||
|
{
|
||||||
|
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
|
||||||
|
{
|
||||||
|
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||||
|
() => Game.Settings.Graphics.Language == o,
|
||||||
|
() => Game.Settings.Graphics.Language = o);
|
||||||
|
item.Get<LabelWidget>("LABEL").GetText = () => FieldLoader.Translate(o);
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
|
||||||
|
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, Game.modData.Languages, setupItem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool ShowSoundTickDropdown(DropDownButtonWidget dropdown, SoundSettings audio)
|
public static bool ShowSoundTickDropdown(DropDownButtonWidget dropdown, SoundSettings audio)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -279,8 +279,8 @@ Container@PLAYER_WIDGETS:
|
|||||||
X:WINDOW_RIGHT - 204
|
X:WINDOW_RIGHT - 204
|
||||||
Y:287
|
Y:287
|
||||||
TooltipContainer:TOOLTIP_CONTAINER
|
TooltipContainer:TOOLTIP_CONTAINER
|
||||||
ReadyText:Ready
|
ReadyText:@ready@
|
||||||
HoldText:On Hold
|
HoldText:@on-hold@
|
||||||
Background@FMVPLAYER:
|
Background@FMVPLAYER:
|
||||||
Width:WINDOW_RIGHT
|
Width:WINDOW_RIGHT
|
||||||
Height:WINDOW_BOTTOM
|
Height:WINDOW_BOTTOM
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Container@SETTINGS_PANEL:
|
|||||||
X:(WINDOW_RIGHT - WIDTH)/2
|
X:(WINDOW_RIGHT - WIDTH)/2
|
||||||
Y:(WINDOW_BOTTOM - 250)/2
|
Y:(WINDOW_BOTTOM - 250)/2
|
||||||
Width:740
|
Width:740
|
||||||
Height:535
|
Height:565
|
||||||
Children:
|
Children:
|
||||||
ColorPreviewManager@COLOR_MANAGER:
|
ColorPreviewManager@COLOR_MANAGER:
|
||||||
Label@TITLE:
|
Label@TITLE:
|
||||||
@@ -15,7 +15,7 @@ Container@SETTINGS_PANEL:
|
|||||||
Text:Settings
|
Text:Settings
|
||||||
Background@GENERAL_CONTROLS:
|
Background@GENERAL_CONTROLS:
|
||||||
Width:740
|
Width:740
|
||||||
Height:290
|
Height:320
|
||||||
Background:panel-black
|
Background:panel-black
|
||||||
Children:
|
Children:
|
||||||
Label@TITLE:
|
Label@TITLE:
|
||||||
@@ -66,35 +66,35 @@ Container@SETTINGS_PANEL:
|
|||||||
Text:Shellmap Music
|
Text:Shellmap Music
|
||||||
Label@DEBUG_TITLE:
|
Label@DEBUG_TITLE:
|
||||||
X:15
|
X:15
|
||||||
Y:150
|
Y:180
|
||||||
Width:340
|
Width:340
|
||||||
Font:Bold
|
Font:Bold
|
||||||
Text:Debug
|
Text:Debug
|
||||||
Align:Center
|
Align:Center
|
||||||
Checkbox@PERFTEXT_CHECKBOX:
|
Checkbox@PERFTEXT_CHECKBOX:
|
||||||
X:15
|
X:15
|
||||||
Y:170
|
Y:200
|
||||||
Width:300
|
Width:300
|
||||||
Height:20
|
Height:20
|
||||||
Font:Regular
|
Font:Regular
|
||||||
Text:Show Performance Text
|
Text:Show Performance Text
|
||||||
Checkbox@PERFGRAPH_CHECKBOX:
|
Checkbox@PERFGRAPH_CHECKBOX:
|
||||||
X:15
|
X:15
|
||||||
Y:200
|
Y:230
|
||||||
Width:300
|
Width:300
|
||||||
Height:20
|
Height:20
|
||||||
Font:Regular
|
Font:Regular
|
||||||
Text:Show Performance Graph
|
Text:Show Performance Graph
|
||||||
Checkbox@CHECKUNSYNCED_CHECKBOX:
|
Checkbox@CHECKUNSYNCED_CHECKBOX:
|
||||||
X:15
|
X:15
|
||||||
Y:230
|
Y:260
|
||||||
Width:300
|
Width:300
|
||||||
Height:20
|
Height:20
|
||||||
Font:Regular
|
Font:Regular
|
||||||
Text:Check Sync around Unsynced Code
|
Text:Check Sync around Unsynced Code
|
||||||
Checkbox@SHOW_FATAL_ERROR_DIALOG_CHECKBOX:
|
Checkbox@SHOW_FATAL_ERROR_DIALOG_CHECKBOX:
|
||||||
X:15
|
X:15
|
||||||
Y:260
|
Y:290
|
||||||
Width:300
|
Width:300
|
||||||
Height:20
|
Height:20
|
||||||
Font:Regular
|
Font:Regular
|
||||||
@@ -149,70 +149,82 @@ Container@SETTINGS_PANEL:
|
|||||||
Width:45
|
Width:45
|
||||||
Height:25
|
Height:25
|
||||||
MaxLength:5
|
MaxLength:5
|
||||||
|
Label@LANGUAGE_LABEL:
|
||||||
|
X:375
|
||||||
|
Y:70
|
||||||
|
Width:75
|
||||||
|
Height:25
|
||||||
|
Align:Right
|
||||||
|
Text:Language:
|
||||||
|
DropDownButton@LANGUAGE_DROPDOWNBUTTON:
|
||||||
|
X:455
|
||||||
|
Y:70
|
||||||
|
Width:140
|
||||||
|
Height:25
|
||||||
Label@VIDEO_DESC:
|
Label@VIDEO_DESC:
|
||||||
X:375
|
X:375
|
||||||
Y:68
|
Y:100
|
||||||
Width:340
|
Width:340
|
||||||
Height:25
|
Height:25
|
||||||
Font:Tiny
|
Font:Tiny
|
||||||
Align:Center
|
Align:Center
|
||||||
Text:Mode/Resolution changes will be applied after the game is restarted
|
Text:Mode/Resolution/Language changes will be applied after the game is restarted
|
||||||
Checkbox@PIXELDOUBLE_CHECKBOX:
|
Checkbox@PIXELDOUBLE_CHECKBOX:
|
||||||
X:375
|
X:375
|
||||||
Y:110
|
Y:140
|
||||||
Width:200
|
Width:200
|
||||||
Height:20
|
Height:20
|
||||||
Font:Regular
|
Font:Regular
|
||||||
Text:Enable Pixel Doubling
|
Text:Enable Pixel Doubling
|
||||||
Label@AUDIO_TITLE:
|
Label@AUDIO_TITLE:
|
||||||
X:375
|
X:375
|
||||||
Y:150
|
Y:180
|
||||||
Width:340
|
Width:340
|
||||||
Font:Bold
|
Font:Bold
|
||||||
Text:Sound
|
Text:Sound
|
||||||
Align:Center
|
Align:Center
|
||||||
Label@SOUND_LABEL:
|
Label@SOUND_LABEL:
|
||||||
X:375
|
X:375
|
||||||
Y:164
|
Y:194
|
||||||
Width:95
|
Width:95
|
||||||
Height:25
|
Height:25
|
||||||
Align:Right
|
Align:Right
|
||||||
Text:Sound Volume:
|
Text:Sound Volume:
|
||||||
Slider@SOUND_SLIDER:
|
Slider@SOUND_SLIDER:
|
||||||
X:475
|
X:475
|
||||||
Y:170
|
Y:200
|
||||||
Width:240
|
Width:240
|
||||||
Height:20
|
Height:20
|
||||||
Ticks:5
|
Ticks:5
|
||||||
Label@MUSIC_LABEL:
|
Label@MUSIC_LABEL:
|
||||||
X:375
|
X:375
|
||||||
Y:194
|
Y:224
|
||||||
Width:95
|
Width:95
|
||||||
Height:25
|
Height:25
|
||||||
Align:Right
|
Align:Right
|
||||||
Text:Music Volume:
|
Text:Music Volume:
|
||||||
Slider@MUSIC_SLIDER:
|
Slider@MUSIC_SLIDER:
|
||||||
X:475
|
X:475
|
||||||
Y:200
|
Y:230
|
||||||
Width:240
|
Width:240
|
||||||
Height:20
|
Height:20
|
||||||
Ticks:5
|
Ticks:5
|
||||||
Label@AUDIO_DEVICE_LABEL:
|
Label@AUDIO_DEVICE_LABEL:
|
||||||
X:375
|
X:375
|
||||||
Y:229
|
Y:259
|
||||||
Width:75
|
Width:75
|
||||||
Height:20
|
Height:20
|
||||||
Text:Audio Device:
|
Text:Audio Device:
|
||||||
DropDownButton@AUDIO_DEVICE:
|
DropDownButton@AUDIO_DEVICE:
|
||||||
X:475
|
X:475
|
||||||
Y:230
|
Y:260
|
||||||
Width:240
|
Width:240
|
||||||
Height:25
|
Height:25
|
||||||
Font:Regular
|
Font:Regular
|
||||||
Text:Default Device
|
Text:Default Device
|
||||||
Label@AUDIO_DESC:
|
Label@AUDIO_DESC:
|
||||||
X:375
|
X:375
|
||||||
Y:258
|
Y:288
|
||||||
Width:340
|
Width:340
|
||||||
Height:25
|
Height:25
|
||||||
Font:Tiny
|
Font:Tiny
|
||||||
@@ -296,20 +308,20 @@ Container@SETTINGS_PANEL:
|
|||||||
Font:Regular
|
Font:Regular
|
||||||
Text:Shift-Enter Toggles Team Chat
|
Text:Shift-Enter Toggles Team Chat
|
||||||
Button@GENERAL_BUTTON:
|
Button@GENERAL_BUTTON:
|
||||||
Y:289
|
Y:319
|
||||||
Width:140
|
Width:140
|
||||||
Height:35
|
Height:35
|
||||||
Text:General
|
Text:General
|
||||||
Button@INPUT_BUTTON:
|
Button@INPUT_BUTTON:
|
||||||
X:150
|
X:150
|
||||||
Y:289
|
Y:319
|
||||||
Width:140
|
Width:140
|
||||||
Height:35
|
Height:35
|
||||||
Text:Input
|
Text:Input
|
||||||
Button@BACK_BUTTON:
|
Button@BACK_BUTTON:
|
||||||
Key:escape
|
Key:escape
|
||||||
X:600
|
X:600
|
||||||
Y:289
|
Y:319
|
||||||
Width:140
|
Width:140
|
||||||
Height:35
|
Height:35
|
||||||
Text:Back
|
Text:Back
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ Background@PRODUCTION_TOOLTIP:
|
|||||||
Y:19
|
Y:19
|
||||||
Height:23
|
Height:23
|
||||||
Font:TinyBold
|
Font:TinyBold
|
||||||
|
Text:@requires@
|
||||||
Label@DESC:
|
Label@DESC:
|
||||||
X:5
|
X:5
|
||||||
Y:39
|
Y:39
|
||||||
|
|||||||
6
mods/cnc/languages/english.yaml
Normal file
6
mods/cnc/languages/english.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
english:
|
||||||
|
english: English
|
||||||
|
|
||||||
|
ready: Ready
|
||||||
|
on-hold: On Hold
|
||||||
|
requires: Requires {0}
|
||||||
@@ -94,6 +94,9 @@ Movies:
|
|||||||
mods/cnc/movies-gdi.yaml
|
mods/cnc/movies-gdi.yaml
|
||||||
mods/cnc/movies-nod.yaml
|
mods/cnc/movies-nod.yaml
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
mods/cnc/languages/english.yaml
|
||||||
|
|
||||||
Voices:
|
Voices:
|
||||||
mods/cnc/voices.yaml
|
mods/cnc/voices.yaml
|
||||||
|
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ Container@PLAYER_WIDGETS:
|
|||||||
Y:280
|
Y:280
|
||||||
Width:238
|
Width:238
|
||||||
Height:500
|
Height:500
|
||||||
ReadyText: READY
|
ReadyText:@ready@
|
||||||
HoldText: ON HOLD
|
HoldText:@on-hold@
|
||||||
RequiresText: Requires
|
RequiresText:@requires@
|
||||||
IconWidth: 60
|
IconWidth: 60
|
||||||
IconHeight: 48
|
IconHeight: 48
|
||||||
Columns: 3
|
Columns: 3
|
||||||
|
|||||||
6
mods/d2k/languages/english.yaml
Normal file
6
mods/d2k/languages/english.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
english:
|
||||||
|
english: English
|
||||||
|
|
||||||
|
ready: READY
|
||||||
|
on-hold: ON HOLD
|
||||||
|
requires: Requires {0}
|
||||||
@@ -95,6 +95,9 @@ Music:
|
|||||||
|
|
||||||
Movies:
|
Movies:
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
mods/d2k/languages/english.yaml
|
||||||
|
|
||||||
LoadScreen: DefaultLoadScreen
|
LoadScreen: DefaultLoadScreen
|
||||||
Image: mods/d2k/uibits/loadscreen.png
|
Image: mods/d2k/uibits/loadscreen.png
|
||||||
InstallerMenuWidget: INSTALL_PANEL
|
InstallerMenuWidget: INSTALL_PANEL
|
||||||
|
|||||||
@@ -148,16 +148,16 @@ Container@PLAYER_WIDGETS:
|
|||||||
SupportPowerBin@INGAME_POWERS_BIN:
|
SupportPowerBin@INGAME_POWERS_BIN:
|
||||||
X:0
|
X:0
|
||||||
Y:25
|
Y:25
|
||||||
ReadyText: READY
|
ReadyText: @ready@
|
||||||
HoldText: ON HOLD
|
HoldText: @on-hold@
|
||||||
BuildPalette@INGAME_BUILD_PALETTE:
|
BuildPalette@INGAME_BUILD_PALETTE:
|
||||||
X:WINDOW_RIGHT - 250
|
X:WINDOW_RIGHT - 250
|
||||||
Y:280
|
Y:280
|
||||||
Width:250
|
Width:250
|
||||||
Height:500
|
Height:500
|
||||||
ReadyText: READY
|
ReadyText: @ready@
|
||||||
HoldText: ON HOLD
|
HoldText: @on-hold@
|
||||||
RequiresText: Requires
|
RequiresText: @requires@
|
||||||
|
|
||||||
Container@OBSERVER_WIDGETS:
|
Container@OBSERVER_WIDGETS:
|
||||||
Children:
|
Children:
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ Background@SETTINGS_MENU:
|
|||||||
Label@SOUND_VOLUME_LABEL:
|
Label@SOUND_VOLUME_LABEL:
|
||||||
X:0
|
X:0
|
||||||
Y:10
|
Y:10
|
||||||
Text: Sound Volume
|
Text: Sound Volume
|
||||||
Slider@SOUND_VOLUME:
|
Slider@SOUND_VOLUME:
|
||||||
X:100
|
X:100
|
||||||
Y:0
|
Y:0
|
||||||
@@ -255,7 +255,7 @@ Background@SETTINGS_MENU:
|
|||||||
Height:25
|
Height:25
|
||||||
Font:Tiny
|
Font:Tiny
|
||||||
Align:Center
|
Align:Center
|
||||||
Text:Renderer/Mode/Resolution changes will be applied after the game is restarted.
|
Text:Mode/Resolution changes will be applied after the game is restarted.
|
||||||
Checkbox@PIXELDOUBLE_CHECKBOX:
|
Checkbox@PIXELDOUBLE_CHECKBOX:
|
||||||
Y:60
|
Y:60
|
||||||
Width:200
|
Width:200
|
||||||
@@ -274,6 +274,24 @@ Background@SETTINGS_MENU:
|
|||||||
Width:45
|
Width:45
|
||||||
Height:25
|
Height:25
|
||||||
MaxLength:3
|
MaxLength:3
|
||||||
|
Label@LANGUAGE_LABEL:
|
||||||
|
X:0
|
||||||
|
Y:130
|
||||||
|
Width:75
|
||||||
|
Height:25
|
||||||
|
Text:Language:
|
||||||
|
DropDownButton@LANGUAGE_DROPDOWNBUTTON:
|
||||||
|
X:80
|
||||||
|
Y:130
|
||||||
|
Width:140
|
||||||
|
Height:25
|
||||||
|
Label@LANGUAGE_DESC:
|
||||||
|
Y:160
|
||||||
|
Width:PARENT_RIGHT
|
||||||
|
Height:25
|
||||||
|
Font:Tiny
|
||||||
|
Align:Center
|
||||||
|
Text:Language changes will be applied after the game is restarted.
|
||||||
Container@KEYS_PANE:
|
Container@KEYS_PANE:
|
||||||
X:37
|
X:37
|
||||||
Y:100
|
Y:100
|
||||||
|
|||||||
6
mods/ra/languages/english.yaml
Normal file
6
mods/ra/languages/english.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
english:
|
||||||
|
english: English
|
||||||
|
|
||||||
|
ready: READY
|
||||||
|
on-hold: ON HOLD
|
||||||
|
requires: Requires {0}
|
||||||
@@ -113,6 +113,9 @@ Movies:
|
|||||||
mods/ra/movies1.yaml
|
mods/ra/movies1.yaml
|
||||||
mods/ra/movies2.yaml
|
mods/ra/movies2.yaml
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
mods/ra/languages/english.yaml
|
||||||
|
|
||||||
LoadScreen: DefaultLoadScreen
|
LoadScreen: DefaultLoadScreen
|
||||||
Image: mods/ra/uibits/loadscreen.png
|
Image: mods/ra/uibits/loadscreen.png
|
||||||
InstallerMenuWidget: INSTALL_PANEL
|
InstallerMenuWidget: INSTALL_PANEL
|
||||||
|
|||||||
6
mods/ts/languages/english.yaml
Normal file
6
mods/ts/languages/english.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
english:
|
||||||
|
english: English
|
||||||
|
|
||||||
|
ready: READY
|
||||||
|
on-hold: ON HOLD
|
||||||
|
requires: Requires {0}
|
||||||
@@ -136,6 +136,9 @@ Music:
|
|||||||
|
|
||||||
Movies:
|
Movies:
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
mods/ts/languages/english.yaml
|
||||||
|
|
||||||
LoadScreen: DefaultLoadScreen
|
LoadScreen: DefaultLoadScreen
|
||||||
Image: mods/ts/uibits/loadscreen.png
|
Image: mods/ts/uibits/loadscreen.png
|
||||||
InstallerMenuWidget: INSTALL_PANEL
|
InstallerMenuWidget: INSTALL_PANEL
|
||||||
|
|||||||
Reference in New Issue
Block a user