mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Fluent Localization (#1584)
This commit is contained in:
@@ -574,29 +574,22 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: NGetText
|
||||
- name: Fluent.Net
|
||||
license: |
|
||||
The MIT License (MIT)
|
||||
blushingpenguin and Contributors
|
||||
|
||||
Copyright (c) 2012 Vitaly Zilnik
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
|
||||
- name: NetSerializer
|
||||
license: |
|
||||
|
||||
11
Robust.Shared/Enums/Gender.cs
Normal file
11
Robust.Shared/Enums/Gender.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
namespace Robust.Shared.Enums
|
||||
{
|
||||
public enum Gender : byte
|
||||
{
|
||||
Epicene,
|
||||
Female,
|
||||
Male,
|
||||
Neuter,
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the necessary information to generate text related to the entity.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class GrammarComponent: Component, IProperNamable
|
||||
{
|
||||
public sealed override string Name => "Grammar";
|
||||
|
||||
public sealed override uint? NetID => NetIDs.GRAMMAR;
|
||||
|
||||
private bool _proper;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Proper
|
||||
{
|
||||
get => _proper;
|
||||
set
|
||||
{
|
||||
_proper = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Proper, "proper", false);
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new GrammarComponentState(Proper);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (!(curState is GrammarComponentState cast))
|
||||
return;
|
||||
|
||||
Proper = cast.Proper;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class GrammarComponentState : ComponentState
|
||||
{
|
||||
public GrammarComponentState(bool proper) : base(NetIDs.GRAMMAR)
|
||||
{
|
||||
Proper = proper;
|
||||
}
|
||||
|
||||
public bool Proper { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public static class NetIDs
|
||||
{
|
||||
@@ -14,7 +14,6 @@
|
||||
public const uint USERINTERFACE = 24;
|
||||
public const uint CONTAINER_MANAGER = 25;
|
||||
public const uint OCCLUDER = 26;
|
||||
public const uint GRAMMAR = 27;
|
||||
public const uint EYE = 28;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using NGettext;
|
||||
using NGettext.Loaders;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
{
|
||||
/// <summary>
|
||||
/// Catalog with custom IFormatProvider
|
||||
/// </summary>
|
||||
public class CustomFormatCatalog : Catalog
|
||||
{
|
||||
public IFormatProvider? CustomFormatProvider;
|
||||
|
||||
public CustomFormatCatalog()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(CultureInfo cultureInfo)
|
||||
: base(cultureInfo)
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(ILoader loader)
|
||||
: base(loader)
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(Stream moStream)
|
||||
: base(moStream)
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(ILoader loader, CultureInfo cultureInfo)
|
||||
: base(loader, cultureInfo)
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(Stream moStream, CultureInfo cultureInfo)
|
||||
: base(moStream, cultureInfo)
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(string domain, string localeDir)
|
||||
: base(domain, localeDir)
|
||||
{ }
|
||||
|
||||
public CustomFormatCatalog(string domain, string localeDir, CultureInfo cultureInfo)
|
||||
: base(domain, localeDir, cultureInfo)
|
||||
{ }
|
||||
|
||||
public override string GetString(string text, params object[] args)
|
||||
{
|
||||
return string.Format(GetFormatProviderOrDefault(), GetStringDefault(text, text), args);
|
||||
}
|
||||
|
||||
public override string GetPluralString(string text, string pluralText, long n, params object[] args)
|
||||
{
|
||||
return string.Format(GetFormatProviderOrDefault(), GetPluralStringDefault(text, text, pluralText, n), args);
|
||||
}
|
||||
|
||||
public override string GetParticularString(string context, string text, params object[] args)
|
||||
{
|
||||
return string.Format(GetFormatProviderOrDefault(), GetStringDefault(context + CONTEXT_GLUE + text, text), args);
|
||||
}
|
||||
|
||||
public override string GetParticularPluralString(string context, string text, string pluralText, long n, params object[] args)
|
||||
{
|
||||
return string.Format(GetFormatProviderOrDefault(), GetPluralStringDefault(context + CONTEXT_GLUE + text, text, pluralText, n), args);
|
||||
}
|
||||
|
||||
private IFormatProvider GetFormatProviderOrDefault()
|
||||
{
|
||||
return CustomFormatProvider ?? CultureInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
{
|
||||
// ReSharper disable once CommentTypo
|
||||
/// <summary>
|
||||
/// Provides facilities to automatically translate in-game text.
|
||||
/// Provides facilities to obtain language appropriate in-game text.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The translation API is similar to GNU gettext.
|
||||
/// You pass a string through it (most often the English version),
|
||||
/// and when the game is ran in another language with adequate translation, the translation is returned instead.
|
||||
/// Translation is handled using Project Fluent (https://www.projectfluent.org/)
|
||||
/// You pass a Fluent 'identifier' as a string and the localization manager will fetch the message
|
||||
/// matching that identifier from the currently loaded language's Fluent files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="Loc"/>
|
||||
@@ -22,40 +23,18 @@ namespace Robust.Shared.Localization
|
||||
public interface ILocalizationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a string translated for the current culture.
|
||||
/// Gets a language approrpiate string represented by the supplied messageId.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to get translated.</param>
|
||||
/// <param name="messageId">Unique Identifier for a translated message.</param>
|
||||
/// <returns>
|
||||
/// The translated string if a translation is available, otherwise the string is returned.
|
||||
/// The language appropriate message if available, otherwise the messageId is returned.
|
||||
/// </returns>
|
||||
string GetString(string text);
|
||||
string GetString(string messageId);
|
||||
|
||||
/// <summary>
|
||||
/// Version of <see cref="GetString(string)"/> that also runs string formatting.
|
||||
/// Version of <see cref="GetString(string)"/> that supports arguments.
|
||||
/// </summary>
|
||||
[StringFormatMethod("text")]
|
||||
string GetString(string text, params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string inside a context or category.
|
||||
/// </summary>
|
||||
string GetParticularString(string context, string text);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string inside a context or category with formatting.
|
||||
/// </summary>
|
||||
[StringFormatMethod("text")]
|
||||
string GetParticularString(string context, string text, params object[] args);
|
||||
|
||||
string GetPluralString(string text, string pluralText, long n);
|
||||
|
||||
[StringFormatMethod("pluralText")]
|
||||
string GetPluralString(string text, string pluralText, long n, params object[] args);
|
||||
|
||||
string GetParticularPluralString(string context, string text, string pluralText, long n);
|
||||
|
||||
[StringFormatMethod("pluralText")]
|
||||
string GetParticularPluralString(string context, string text, string pluralText, long n, params object[] args);
|
||||
string GetString(string messageId, params (string, object)[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Default culture used by other methods when no culture is explicitly specified.
|
||||
@@ -67,9 +46,19 @@ namespace Robust.Shared.Localization
|
||||
/// Load data for a culture.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager"></param>
|
||||
/// <param name="textMacroFactory"></param>
|
||||
/// <param name="culture"></param>
|
||||
void LoadCulture(IResourceManager resourceManager, ITextMacroFactory textMacroFactory, CultureInfo culture);
|
||||
void LoadCulture(IResourceManager resourceManager, CultureInfo culture);
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remnants of the old Localization system.
|
||||
/// It exists to prevent source errors and allow existing game text to *mostly* work
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[StringFormatMethod("text")]
|
||||
string GetString(string text, params object[] args);
|
||||
}
|
||||
|
||||
internal interface ILocalizationManagerInternal : ILocalizationManager
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
{
|
||||
@@ -24,66 +25,23 @@ namespace Robust.Shared.Localization
|
||||
private static ILocalizationManager LocalizationManager => IoCManager.Resolve<ILocalizationManager>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string translated for the current culture.
|
||||
/// Gets a language approrpiate string represented by the supplied messageId.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to get translated.</param>
|
||||
/// <param name="messageId">Unique Identifier for a translated message.</param>
|
||||
/// <returns>
|
||||
/// The translated string if a translation is available, otherwise the string is returned.
|
||||
/// The language appropriate message if available, otherwise the messageId is returned.
|
||||
/// </returns>
|
||||
public static string GetString(string text)
|
||||
public static string GetString(string messageId)
|
||||
{
|
||||
return LocalizationManager.GetString(text);
|
||||
return LocalizationManager.GetString(messageId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Version of <see cref="GetString(string)"/> that also runs string formatting.
|
||||
/// Version of <see cref="GetString(string)"/> that supports arguments.
|
||||
/// </summary>
|
||||
[StringFormatMethod("text")]
|
||||
public static string GetString(string text, params object[] args)
|
||||
public static string GetString(string messageId, params (string,object)[] args)
|
||||
{
|
||||
return LocalizationManager.GetString(text, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string inside a context or category.
|
||||
/// </summary>
|
||||
public static string GetParticularString(string context, string text)
|
||||
{
|
||||
return LocalizationManager.GetParticularString(context, text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string inside a context or category with formatting.
|
||||
/// </summary>
|
||||
[StringFormatMethod("text")]
|
||||
public static string GetParticularString(string context, string text, params object[] args)
|
||||
{
|
||||
return LocalizationManager.GetParticularString(context, text, args);
|
||||
|
||||
}
|
||||
|
||||
public static string GetPluralString(string text, string pluralText, long n)
|
||||
{
|
||||
return LocalizationManager.GetPluralString(text, pluralText, n);
|
||||
|
||||
}
|
||||
|
||||
[StringFormatMethod("pluralText")]
|
||||
public static string GetPluralString(string text, string pluralText, long n, params object[] args)
|
||||
{
|
||||
return LocalizationManager.GetPluralString(text, pluralText, n, args);
|
||||
}
|
||||
|
||||
public static string GetParticularPluralString(string context, string text, string pluralText, long n)
|
||||
{
|
||||
return LocalizationManager.GetParticularString(context, text, pluralText, n);
|
||||
}
|
||||
|
||||
[StringFormatMethod("pluralText")]
|
||||
public static string GetParticularPluralString(string context, string text, string pluralText, long n,
|
||||
params object[] args)
|
||||
{
|
||||
return LocalizationManager.GetParticularString(context, text, pluralText, n, args);
|
||||
return LocalizationManager.GetString(messageId, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,9 +50,21 @@ namespace Robust.Shared.Localization
|
||||
/// <param name="resourceManager"></param>
|
||||
/// <param name="macroFactory"></param>
|
||||
/// <param name="culture"></param>
|
||||
public static void LoadCulture(IResourceManager resourceManager, ITextMacroFactory macroFactory, CultureInfo culture)
|
||||
public static void LoadCulture(IResourceManager resourceManager, CultureInfo culture)
|
||||
{
|
||||
LocalizationManager.LoadCulture(resourceManager, macroFactory, culture);
|
||||
LocalizationManager.LoadCulture(resourceManager, culture);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remnants of the old Localization system.
|
||||
/// It exists to prevent source errors and allow existing game text to *mostly* work
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[StringFormatMethod("text")]
|
||||
public static string GetString(string text, params object[] args)
|
||||
{
|
||||
return LocalizationManager.GetString(text, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,99 +2,71 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NGettext;
|
||||
using Fluent.Net;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
{
|
||||
internal sealed class LocalizationManager : ILocalizationManagerInternal
|
||||
{
|
||||
private readonly Dictionary<CultureInfo, Catalog> _catalogs = new();
|
||||
private readonly Dictionary<CultureInfo, MessageContext> _contexts = new();
|
||||
|
||||
private CultureInfo? _defaultCulture;
|
||||
|
||||
public string GetString(string text)
|
||||
public string GetString(string messageId)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return text;
|
||||
return messageId;
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetString(text);
|
||||
var context = _contexts[_defaultCulture];
|
||||
var message = context.GetMessage(messageId);
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
Logger.WarningS("Loc", $"Unknown messageId ({_defaultCulture.IetfLanguageTag}): {messageId}");
|
||||
return messageId;
|
||||
}
|
||||
|
||||
return context.Format(message, null, null);
|
||||
}
|
||||
|
||||
public string GetString(string messageId, params (string, object)[] args0)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return messageId;
|
||||
}
|
||||
var context = _contexts[_defaultCulture];
|
||||
var message = context.GetMessage(messageId);
|
||||
var args = new Dictionary<string, object>();
|
||||
foreach (var vari in args0)
|
||||
{
|
||||
args.Add(vari.Item1, vari.Item2);
|
||||
}
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
Logger.WarningS("Loc", $"Unknown messageId ({_defaultCulture.IetfLanguageTag}): {messageId}");
|
||||
return messageId;
|
||||
}
|
||||
|
||||
return context.Format(message, args, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remnants of the old Localization system.
|
||||
/// It exists to prevent source errors and allow existing game text to *mostly* work
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[StringFormatMethod("text")]
|
||||
public string GetString(string text, params object[] args)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return string.Format(text, args);
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetString(text, args);
|
||||
}
|
||||
|
||||
public string GetParticularString(string context, string text)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetParticularString(context, text);
|
||||
}
|
||||
|
||||
public string GetParticularString(string context, string text, params object[] args)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return string.Format(text, args);
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetParticularString(context, text, args);
|
||||
}
|
||||
|
||||
public string GetPluralString(string text, string pluralText, long n)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return n == 1 ? text : pluralText;
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetPluralString(text, pluralText, n);
|
||||
}
|
||||
|
||||
public string GetPluralString(string text, string pluralText, long n, params object[] args)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return string.Format(n == 1 ? text : pluralText, args);
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetPluralString(text, pluralText, n, args);
|
||||
}
|
||||
|
||||
public string GetParticularPluralString(string context, string text, string pluralText, long n)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return n == 1 ? text : pluralText;
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetParticularPluralString(context, text, pluralText, n, pluralText);
|
||||
}
|
||||
|
||||
public string GetParticularPluralString(string context, string text, string pluralText, long n, params object[] args)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
return string.Format(n == 1 ? text : pluralText, args);
|
||||
}
|
||||
var catalog = _catalogs[_defaultCulture];
|
||||
return catalog.GetParticularPluralString(context, text, pluralText, n, args);
|
||||
return string.Format(text, args);
|
||||
}
|
||||
|
||||
public CultureInfo? DefaultCulture
|
||||
@@ -107,7 +79,7 @@ namespace Robust.Shared.Localization
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (!_catalogs.ContainsKey(value))
|
||||
if (!_contexts.ContainsKey(value))
|
||||
{
|
||||
throw new ArgumentException("That culture is not yet loaded and cannot be used.", nameof(value));
|
||||
}
|
||||
@@ -118,13 +90,16 @@ namespace Robust.Shared.Localization
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCulture(IResourceManager resourceManager, ITextMacroFactory textMacroFactory, CultureInfo culture)
|
||||
public void LoadCulture(IResourceManager resourceManager, CultureInfo culture)
|
||||
{
|
||||
var catalog = new CustomFormatCatalog(culture);
|
||||
_catalogs.Add(culture, catalog);
|
||||
var context = new MessageContext(
|
||||
culture.Name,
|
||||
new MessageContextOptions { UseIsolating = false }
|
||||
);
|
||||
|
||||
_loadData(resourceManager, culture, catalog);
|
||||
_loadMacros(textMacroFactory, culture, catalog);
|
||||
_contexts.Add(culture, context);
|
||||
|
||||
_loadData(resourceManager, culture, context);
|
||||
if (DefaultCulture == null)
|
||||
{
|
||||
DefaultCulture = culture;
|
||||
@@ -133,13 +108,15 @@ namespace Robust.Shared.Localization
|
||||
|
||||
public void AddLoadedToStringSerializer(IRobustMappedStringSerializer serializer)
|
||||
{
|
||||
/*
|
||||
* TODO: need to expose Messages on MessageContext in Fluent.NET
|
||||
serializer.AddStrings(StringIterator());
|
||||
|
||||
IEnumerable<string> StringIterator()
|
||||
{
|
||||
foreach (var catalog in _catalogs.Values)
|
||||
foreach (var context in _contexts.Values)
|
||||
{
|
||||
foreach (var (key, translations) in catalog.Translations)
|
||||
foreach (var (key, translations) in _context)
|
||||
{
|
||||
yield return key;
|
||||
|
||||
@@ -150,65 +127,34 @@ namespace Robust.Shared.Localization
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private static void _loadData(IResourceManager resourceManager, CultureInfo culture, Catalog catalog)
|
||||
private static void _loadData(IResourceManager resourceManager, CultureInfo culture, MessageContext context)
|
||||
{
|
||||
// Load data from .yml files.
|
||||
// Load data from .ftl files.
|
||||
// Data is loaded from /Locale/<language-code>/*
|
||||
|
||||
var root = new ResourcePath($"/Locale/{culture.IetfLanguageTag}/");
|
||||
|
||||
foreach (var file in resourceManager.ContentFindFiles(root))
|
||||
{
|
||||
var yamlFile = root / file;
|
||||
_loadFromFile(resourceManager, yamlFile, catalog);
|
||||
var ftlFile = root / file;
|
||||
_loadFromFile(resourceManager, ftlFile, context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _loadFromFile(IResourceManager resourceManager, ResourcePath filePath, Catalog catalog)
|
||||
private static void _loadFromFile(IResourceManager resourceManager, ResourcePath filePath, MessageContext context)
|
||||
{
|
||||
var yamlStream = new YamlStream();
|
||||
using (var fileStream = resourceManager.ContentFileRead(filePath))
|
||||
using (var reader = new StreamReader(fileStream, EncodingHelpers.UTF8))
|
||||
{
|
||||
yamlStream.Load(reader);
|
||||
var errors = context.AddMessages(reader);
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Logger.WarningS("Loc", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in yamlStream.Documents
|
||||
.SelectMany(d => (YamlSequenceNode) d.RootNode)
|
||||
.Cast<YamlMappingNode>())
|
||||
{
|
||||
_readEntry(entry, catalog);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _readEntry(YamlMappingNode entry, Catalog catalog)
|
||||
{
|
||||
var id = entry.GetNode("msgid").AsString();
|
||||
var str = entry.GetNode("msgstr");
|
||||
string[] strings;
|
||||
if (str is YamlScalarNode scalar)
|
||||
{
|
||||
strings = new[] {scalar.AsString()};
|
||||
}
|
||||
else if (str is YamlSequenceNode sequence)
|
||||
{
|
||||
strings = sequence.Children.Select(c => c.AsString()).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Improve error reporting here.
|
||||
throw new Exception("Invalid format in translation file.");
|
||||
}
|
||||
|
||||
catalog.Translations.Add(id, strings);
|
||||
}
|
||||
|
||||
private static void _loadMacros(ITextMacroFactory textMacroFactory, CultureInfo culture, CustomFormatCatalog catalog)
|
||||
{
|
||||
var macros = textMacroFactory.GetMacrosForLanguage(culture.IetfLanguageTag);
|
||||
catalog.CustomFormatProvider = new MacroFormatProvider(new MacroFormatter(macros), culture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
/// <summary>
|
||||
/// Genders for grammatical usage only.
|
||||
/// </summary>
|
||||
public enum Gender : byte
|
||||
{
|
||||
Epicene,
|
||||
Female,
|
||||
Male,
|
||||
Neuter,
|
||||
}
|
||||
|
||||
public interface IGenderable
|
||||
{
|
||||
public Gender Gender => Gender.Epicene;
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get the gender of an object, or Epicene if the object is not IGenderable
|
||||
/// </summary>
|
||||
public static Gender GetGenderOrEpicene(object? argument)
|
||||
{
|
||||
// FIXME The Entity special case is not really good
|
||||
if (argument is IEntity entity)
|
||||
{
|
||||
// FIXME And this is not really better.
|
||||
return Enumerable.FirstOrDefault(entity.GetAllComponents<IGenderable>())?.Gender ?? Gender.Epicene;
|
||||
}
|
||||
else
|
||||
return (argument as IGenderable)?.Gender ?? Gender.Epicene;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IProperNamable
|
||||
{
|
||||
public bool Proper => false;
|
||||
|
||||
public static bool GetProperOrFalse(object? argument)
|
||||
{
|
||||
// FIXME The Entity special case is not really good
|
||||
if (argument is IEntity entity)
|
||||
{
|
||||
// FIXME And this is not really better.
|
||||
return entity.GetAllComponents<IProperNamable>().FirstOrDefault()?.Proper ?? false;
|
||||
}
|
||||
|
||||
return (argument as IProperNamable)?.Proper ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
namespace Robust.Shared.Localization.Macros.English
|
||||
{
|
||||
[RegisterTextMacro("they", "en")]
|
||||
public class They : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "she",
|
||||
Gender.Male => "he",
|
||||
Gender.Neuter => "it",
|
||||
_ => "they",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("their", "en")]
|
||||
public class Their : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "her",
|
||||
Gender.Male => "his",
|
||||
Gender.Neuter => "its",
|
||||
_ => "their",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("theirs", "en")]
|
||||
public class Theirs : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "hers",
|
||||
Gender.Male => "his",
|
||||
Gender.Neuter => "its",
|
||||
_ => "theirs",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("them", "en")]
|
||||
public class Them : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "her",
|
||||
Gender.Male => "him",
|
||||
Gender.Neuter => "it",
|
||||
_ => "them",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("themself", "en")]
|
||||
public class Themself : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "herself",
|
||||
Gender.Male => "himself",
|
||||
Gender.Neuter => "itself",
|
||||
_ => "themself",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("theyre", "en")]
|
||||
public class Theyre : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "she's",
|
||||
Gender.Male => "he's",
|
||||
Gender.Neuter => "it's",
|
||||
_ => "they're",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("theName", "en")]
|
||||
public class TheName : ITextMacro
|
||||
{
|
||||
private readonly NameMacro _nameMacro = new();
|
||||
|
||||
public string Format(object? argument)
|
||||
{
|
||||
var name = _nameMacro.Format(argument);
|
||||
return IProperNamable.GetProperOrFalse(argument)
|
||||
? name
|
||||
: "the " + name;
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("are", "en")]
|
||||
public class ToBe : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "is",
|
||||
Gender.Male => "is",
|
||||
Gender.Neuter => "is",
|
||||
_ => "are",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterTextMacro("have", "en")]
|
||||
public class Have : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
return IGenderable.GetGenderOrEpicene(argument) switch
|
||||
{
|
||||
Gender.Female => "has",
|
||||
Gender.Male => "has",
|
||||
Gender.Neuter => "has",
|
||||
_ => "have",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
[RegisterTextMacro("name")]
|
||||
public class NameMacro: ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
// TODO Make entity inherit "INameable" something?
|
||||
return (argument as IEntity)?.Name ?? argument?.ToString() ?? "<null>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
public interface ITextMacro
|
||||
{
|
||||
public string Format(object? argument);
|
||||
|
||||
public string CapitalizedFormat(object? arg)
|
||||
{
|
||||
string result = Format(arg);
|
||||
return char.ToUpper(result[0]) + result.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
public interface ITextMacroFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Get registered macros from
|
||||
/// </summary>
|
||||
/// <param name="languageTag">IEFT language tag. Might be composed of one or two subtags. for instance, "en" or "en-US".</param>
|
||||
/// <returns>A dictionnary of macros, indexed by lower-cased macro name.</returns>
|
||||
public IDictionary<string, ITextMacro> GetMacrosForLanguage(string languageTag);
|
||||
|
||||
/// <summary>
|
||||
/// Register a text macro for all languages.
|
||||
/// </summary>
|
||||
/// <param name="name">Macro name</param>
|
||||
/// <param name="macroType">The type to register</param>
|
||||
public void Register(string name, Type macroType);
|
||||
|
||||
/// <summary>
|
||||
/// Register a text macro.
|
||||
/// </summary>
|
||||
/// <param name="name">Macro name</param>
|
||||
/// <param name="languageTag">IEFT tag for the language the macro applies to.</param>
|
||||
/// <param name="macroType">The type to register</param>
|
||||
public void Register(string name, string languageTag, Type macroType);
|
||||
|
||||
void DoAutoRegistrations();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
public class MacroFormatProvider : IFormatProvider
|
||||
{
|
||||
public MacroFormatter Formatter;
|
||||
|
||||
public CultureInfo CultureInfo;
|
||||
|
||||
public MacroFormatProvider(MacroFormatter formatter, CultureInfo cultureInfo)
|
||||
{
|
||||
Formatter = formatter;
|
||||
CultureInfo = cultureInfo;
|
||||
}
|
||||
|
||||
public object? GetFormat(Type? formatType)
|
||||
{
|
||||
if (formatType == typeof(ICustomFormatter))
|
||||
return Formatter;
|
||||
else
|
||||
return CultureInfo.GetFormat(formatType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
public class MacroFormatter : ICustomFormatter
|
||||
{
|
||||
private readonly IDictionary<string, ITextMacro> Macros;
|
||||
|
||||
public MacroFormatter(IDictionary<string, ITextMacro> macros)
|
||||
{
|
||||
Macros = macros;
|
||||
}
|
||||
|
||||
public string Format(string? format, object? arg, IFormatProvider? formatProvider)
|
||||
{
|
||||
IFormatProvider? fallbackProvider = GetFallbackFormatProvider(formatProvider);
|
||||
|
||||
if (format == null || format == "")
|
||||
return string.Format(fallbackProvider, "{0}", arg);
|
||||
|
||||
bool capitalized = char.IsUpper(format[0]);
|
||||
string lowerCasedFunctionName = char.ToLower(format[0]) + format.Substring(1);
|
||||
|
||||
if (!Macros.TryGetValue(lowerCasedFunctionName, out var grammarFunction))
|
||||
return string.Format(fallbackProvider, "{0:" + format + '}', arg);
|
||||
|
||||
return capitalized
|
||||
? grammarFunction.CapitalizedFormat(arg)
|
||||
: grammarFunction.Format(arg);
|
||||
}
|
||||
|
||||
private static IFormatProvider? GetFallbackFormatProvider(IFormatProvider? formatProvider)
|
||||
{
|
||||
if (formatProvider is MacroFormatProvider macroFormatProvider)
|
||||
return macroFormatProvider.CultureInfo;
|
||||
else
|
||||
return formatProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
/// <summary>
|
||||
/// Register a text macro. The parameter must be the name of the macro,
|
||||
/// and a an IEFT language tag might be given as a second parameter so specify the language compatible with the macro.
|
||||
/// [RegisterTextMacro("they", "en")], [RegisterTextMacro("they", "en-US")].
|
||||
///
|
||||
/// Afterward, the macro can be use by its name.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[BaseTypeRequired(typeof(ITextMacro))]
|
||||
[MeansImplicitUse]
|
||||
public sealed class RegisterTextMacroAttribute : Attribute
|
||||
{
|
||||
public readonly string MacroName;
|
||||
|
||||
public readonly string? LanguageTag;
|
||||
|
||||
public RegisterTextMacroAttribute(string name)
|
||||
{
|
||||
MacroName = name;
|
||||
}
|
||||
|
||||
public RegisterTextMacroAttribute(string name, string languageTag)
|
||||
{
|
||||
MacroName = name;
|
||||
LanguageTag = languageTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Robust.Shared.Localization.Macros
|
||||
{
|
||||
public class TextMacroFactory : ITextMacroFactory
|
||||
{
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
||||
private struct TextMacroRegistration
|
||||
{
|
||||
public Type MacroType;
|
||||
public string MacroName;
|
||||
public string? LanguageTag;
|
||||
}
|
||||
|
||||
private IList<TextMacroRegistration> Macros = new List<TextMacroRegistration>();
|
||||
|
||||
public IDictionary<string, ITextMacro> GetMacrosForLanguage(string languageTag)
|
||||
{
|
||||
var languageMacros = new Dictionary<string, ITextMacro>();
|
||||
|
||||
foreach (var registeredMacro in Macros)
|
||||
{
|
||||
if (IsMacroForLanguage(languageTag, registeredMacro))
|
||||
{
|
||||
// TODO Handle duplicate macros?
|
||||
languageMacros.Add(registeredMacro.MacroName, _typeFactory.CreateInstanceUnchecked<ITextMacro>(registeredMacro.MacroType));
|
||||
}
|
||||
}
|
||||
|
||||
return languageMacros;
|
||||
}
|
||||
|
||||
private bool IsMacroForLanguage(string languageTag, TextMacroRegistration macro)
|
||||
{
|
||||
int dashIndex = languageTag.IndexOf('-');
|
||||
var firstSubTag = dashIndex != -1 ? languageTag.Substring(0, dashIndex) : languageTag;
|
||||
|
||||
return macro.LanguageTag == null || macro.LanguageTag == firstSubTag || macro.LanguageTag == languageTag;
|
||||
}
|
||||
|
||||
public void Register(string name, Type macroType)
|
||||
{
|
||||
Register(name, null, macroType);
|
||||
}
|
||||
|
||||
public void Register(string name, string? languageTag, Type macroType)
|
||||
{
|
||||
Macros.Add(new TextMacroRegistration
|
||||
{
|
||||
MacroType = macroType,
|
||||
MacroName = name,
|
||||
LanguageTag = languageTag,
|
||||
});
|
||||
}
|
||||
|
||||
public void DoAutoRegistrations()
|
||||
{
|
||||
var iComponent = typeof(ITextMacro);
|
||||
|
||||
foreach (var type in _reflectionManager.FindTypesWithAttribute<RegisterTextMacroAttribute>())
|
||||
{
|
||||
if (!iComponent.IsAssignableFrom(type))
|
||||
{
|
||||
Logger.Error("Type {0} has RegisterTextMacroAttribute but does not implement ITextMacro.", type);
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterTextMacroAttribute registerAttribute = (RegisterTextMacroAttribute)type.GetCustomAttributes(typeof(RegisterTextMacroAttribute), false)[0];
|
||||
Register(registerAttribute.MacroName, registerAttribute.LanguageTag, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
@@ -14,7 +14,7 @@
|
||||
<PackageReference Include="Nett" Version="0.15.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="nfluidsynth" Version="0.3.1" />
|
||||
<PackageReference Include="NGettext" Version="0.6.6" />
|
||||
<PackageReference Include="Fluent.Net" Version="1.0.50" />
|
||||
<PackageReference Include="Pidgin" Version="2.5.0" />
|
||||
<PackageReference Include="prometheus-net" Version="4.0.0" />
|
||||
<PackageReference Include="Robust.Shared.AuthLib" Version="0.1.2" />
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
@@ -45,7 +44,6 @@ namespace Robust.Shared
|
||||
IoCManager.Register<ITaskManager, TaskManager>();
|
||||
IoCManager.Register<ITimerManager, TimerManager>();
|
||||
IoCManager.Register<IRobustRandom, RobustRandom>();
|
||||
IoCManager.Register<ITextMacroFactory, TextMacroFactory>();
|
||||
IoCManager.Register<IRobustMappedStringSerializer, RobustMappedStringSerializer>();
|
||||
IoCManager.Register<IComponentDependencyManager, ComponentDependencyManager>();
|
||||
IoCManager.Register<ISandboxHelper, SandboxHelper>();
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Localization.Macros.English;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Localization.Macros
|
||||
{
|
||||
[TestFixture, Parallelizable]
|
||||
public class EnglishMacros_test
|
||||
{
|
||||
private struct Subject : IGenderable, IProperNamable
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public Gender Gender { get; set; }
|
||||
|
||||
public bool Proper { get; set; }
|
||||
|
||||
public Subject(string name, Gender gender, bool proper)
|
||||
{
|
||||
Name = name;
|
||||
Gender = gender;
|
||||
Proper = proper;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Subject female = new("Lisa", Gender.Female, true);
|
||||
private readonly Subject male = new("Bob", Gender.Male, true);
|
||||
private readonly Subject epicene = new("Michel", Gender.Epicene, true);
|
||||
private readonly Subject neuter = new("D.O.O.R.K.N.O.B.", Gender.Neuter, true);
|
||||
|
||||
public void TestThey()
|
||||
{
|
||||
ITextMacro sut = new They();
|
||||
Assert.That(sut.CapitalizedFormat(female), Is.EqualTo("She"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo("he"));
|
||||
Assert.That(sut.Format(epicene), Is.EqualTo("they"));
|
||||
Assert.That(sut.Format(neuter), Is.EqualTo("it"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTheir()
|
||||
{
|
||||
var sut = new Their();
|
||||
Assert.That(sut.Format(female), Is.EqualTo("her"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo("his"));
|
||||
Assert.That(sut.Format(epicene), Is.EqualTo("their"));
|
||||
Assert.That(sut.Format(neuter), Is.EqualTo("its"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTheirs()
|
||||
{
|
||||
var sut = new Theirs();
|
||||
Assert.That(sut.Format(female), Is.EqualTo("hers"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo("his"));
|
||||
Assert.That(sut.Format(epicene), Is.EqualTo("theirs"));
|
||||
Assert.That(sut.Format(neuter), Is.EqualTo("its"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestThem()
|
||||
{
|
||||
var sut = new Them();
|
||||
Assert.That(sut.Format(female), Is.EqualTo("her"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo("him"));
|
||||
Assert.That(sut.Format(epicene), Is.EqualTo("them"));
|
||||
Assert.That(sut.Format(neuter), Is.EqualTo("it"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestThemself()
|
||||
{
|
||||
var sut = new Themself();
|
||||
Assert.That(sut.Format(female), Is.EqualTo("herself"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo("himself"));
|
||||
Assert.That(sut.Format(epicene), Is.EqualTo("themself"));
|
||||
Assert.That(sut.Format(neuter), Is.EqualTo("itself"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTheyre()
|
||||
{
|
||||
ITextMacro sut = new Theyre();
|
||||
Assert.That(sut.CapitalizedFormat(female), Is.EqualTo("She's"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo("he's"));
|
||||
Assert.That(sut.Format(epicene), Is.EqualTo("they're"));
|
||||
Assert.That(sut.Format(neuter), Is.EqualTo("it's"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTheName()
|
||||
{
|
||||
var cpu = new Subject("CPU", Gender.Neuter, false);
|
||||
ITextMacro sut = new TheName();
|
||||
Assert.That(sut.CapitalizedFormat(cpu), Is.EqualTo("The CPU"));
|
||||
Assert.That(sut.Format(cpu), Is.EqualTo("the CPU"));
|
||||
Assert.That(sut.Format(male), Is.EqualTo(male.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Localization.Macros.English;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Localization.Macros
|
||||
{
|
||||
[TestFixture, Parallelizable, TestOf(typeof(MacroFormatProvider))]
|
||||
internal class MacroFormatProvider_tests
|
||||
{
|
||||
private MacroFormatProvider sut = default!;
|
||||
|
||||
private class GenderedPerson : IGenderable
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public Gender Gender { get; set; }
|
||||
|
||||
public GenderedPerson(string name, Gender gender)
|
||||
{
|
||||
Name = name;
|
||||
Gender = gender;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly GenderedPerson female = new("Lisa", Gender.Female);
|
||||
private readonly GenderedPerson male = new("Bob", Gender.Male);
|
||||
private readonly GenderedPerson epicene = new("Michel", Gender.Epicene);
|
||||
private readonly GenderedPerson neuter = new("D.O.O.R.K.N.O.B.", Gender.Neuter);
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var macros = new Dictionary<string, ITextMacro>
|
||||
{
|
||||
{ "they", new They() },
|
||||
{ "their", new Their() },
|
||||
{ "theirs", new Theirs() },
|
||||
{ "them", new Them() },
|
||||
{ "themself", new Themself() },
|
||||
{ "theyre", new Theyre() }
|
||||
};
|
||||
sut = new MacroFormatProvider(new MacroFormatter(macros), CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanFormatNormally()
|
||||
{
|
||||
AssertFormatNormally("Hello {0}", "world");
|
||||
AssertFormatNormally("PI is roughly {0}", 3.1415);
|
||||
AssertFormatNormally("Scientific notation: {0:#.##E+0}", 1234.5678);
|
||||
}
|
||||
|
||||
private void AssertFormatNormally(string format, params object[] args)
|
||||
{
|
||||
Assert.That(string.Format(sut, format, args), Is.EqualTo(string.Format(format, args)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertThey()
|
||||
{
|
||||
Assert.That(string.Format(sut, "{0:They} protects", female), Is.EqualTo("She protects"));
|
||||
Assert.That(string.Format(sut, "{0:They} attacks", male), Is.EqualTo("He attacks"));
|
||||
Assert.That(string.Format(sut, "{0:They} plasmaflood", neuter), Is.EqualTo("It plasmaflood"));
|
||||
Assert.That(string.Format(sut, "But most importantly, {0:they} do grammar right", epicene), Is.EqualTo("But most importantly, they do grammar right"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertTheir()
|
||||
{
|
||||
Assert.That(string.Format(sut, "{0:Their} toolbox", female), Is.EqualTo("Her toolbox"));
|
||||
Assert.That(string.Format(sut, "{0:Their} toolbox", male), Is.EqualTo("His toolbox"));
|
||||
Assert.That(string.Format(sut, "{0:Their} toolbox", neuter), Is.EqualTo("Its toolbox"));
|
||||
Assert.That(string.Format(sut, "Grab {0:their} toolbox", epicene), Is.EqualTo("Grab their toolbox"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertTheirs()
|
||||
{
|
||||
Assert.That(string.Format(sut, "{0:Theirs} toolboxs", female), Is.EqualTo("Hers toolboxs"));
|
||||
Assert.That(string.Format(sut, "{0:Theirs} toolboxs", male), Is.EqualTo("His toolboxs"));
|
||||
Assert.That(string.Format(sut, "{0:Theirs} toolboxs", neuter), Is.EqualTo("Its toolboxs"));
|
||||
Assert.That(string.Format(sut, "Grab {0:theirs} toolboxs", epicene), Is.EqualTo("Grab theirs toolboxs"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertThem()
|
||||
{
|
||||
Assert.That(string.Format(sut, "Robust {0:them}", female), Is.EqualTo("Robust her"));
|
||||
Assert.That(string.Format(sut, "Robust {0:them}", male), Is.EqualTo("Robust him"));
|
||||
Assert.That(string.Format(sut, "Robust {0:them}", neuter), Is.EqualTo("Robust it"));
|
||||
Assert.That(string.Format(sut, "Robust {0:them}", epicene), Is.EqualTo("Robust them"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertThemself()
|
||||
{
|
||||
Assert.That(string.Format(sut, "Robust {0:themself}", female), Is.EqualTo("Robust herself"));
|
||||
Assert.That(string.Format(sut, "Robust {0:themself}", male), Is.EqualTo("Robust himself"));
|
||||
Assert.That(string.Format(sut, "Robust {0:themself}", neuter), Is.EqualTo("Robust itself"));
|
||||
Assert.That(string.Format(sut, "Robust {0:themself}", epicene), Is.EqualTo("Robust themself"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertTheyre()
|
||||
{
|
||||
Assert.That(string.Format(sut, "{0:Theyre} robust", female), Is.EqualTo("She's robust"));
|
||||
Assert.That(string.Format(sut, "{0:Theyre} robust", male), Is.EqualTo("He's robust"));
|
||||
Assert.That(string.Format(sut, "{0:Theyre} robust", neuter), Is.EqualTo("It's robust"));
|
||||
Assert.That(string.Format(sut, "{0:Theyre} robust", epicene), Is.EqualTo("They're robust"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUseToString()
|
||||
{
|
||||
Assert.That(string.Format(sut, "{0} uses {0:their} toolbox", male), Is.EqualTo("Bob uses his toolbox"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Localization.Macros
|
||||
{
|
||||
[TestFixture, Parallelizable, TestOf(typeof(TextMacroFactory))]
|
||||
internal class TextMacroFactory_tests : RobustUnitTest
|
||||
{
|
||||
private TextMacroFactory sut = default!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
sut = new TextMacroFactory();
|
||||
IoCManager.InjectDependencies(sut);
|
||||
}
|
||||
|
||||
[RegisterTextMacro("mock_macro", "test-TE")]
|
||||
private class MockTextMacro : ITextMacro
|
||||
{
|
||||
public string Format(object? argument)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResolveLanguageMacro()
|
||||
{
|
||||
sut.Register("my_macro", typeof(MockTextMacro));
|
||||
sut.Register("my_macro_en", "en", typeof(MockTextMacro));
|
||||
sut.Register("my_macro_us", "en-US", typeof(MockTextMacro));
|
||||
sut.Register("my_macro_gb", "en-GB", typeof(MockTextMacro));
|
||||
sut.Register("my_macro_t", "ent", typeof(MockTextMacro));
|
||||
|
||||
var macros = sut.GetMacrosForLanguage("en-US");
|
||||
|
||||
Assert.IsTrue(macros.ContainsKey("my_macro"));
|
||||
Assert.IsTrue(macros.ContainsKey("my_macro_en"));
|
||||
Assert.IsTrue(macros.ContainsKey("my_macro_us"));
|
||||
Assert.IsFalse(macros.ContainsKey("my_macro_gb"));
|
||||
Assert.IsFalse(macros.ContainsKey("my_macro_t"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoRegistrations()
|
||||
{
|
||||
sut.DoAutoRegistrations();
|
||||
|
||||
var macros = sut.GetMacrosForLanguage("test-TE");
|
||||
|
||||
Assert.IsTrue(macros.ContainsKey("mock_macro"));
|
||||
Assert.IsInstanceOf<MockTextMacro>(macros["mock_macro"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user