Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement translation support for config options #579

Merged
merged 14 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_null_propagation = true:suggestion
indent_style = tab

[*.cs]
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_indent_labels = one_less_than_current
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"everytime",
"overridable",
"Untarget"
],
"githubPullRequests.ignoredPullRequestBranches": [
"master"
]
}
3 changes: 2 additions & 1 deletion OWML.sln
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OWML.ExampleAPI", "src\Samp
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C7F76E72-1CF2-4C0D-8A39-3D13EB868119}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
LICENSE = LICENSE
.github\workflows\main.yml = .github\workflows\main.yml
owmllogo.png = owmllogo.png
Expand Down Expand Up @@ -209,7 +210,7 @@ Global
{739D16FB-7848-4047-A173-500CE7C40399} = {C447A599-2700-44E1-BBFA-52880B7BFFBA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.2\lib\NET35;packages\Unity.2.1.505.0\lib\NET35
SolutionGuid = {0E767163-75F9-420A-80EB-320429543CAD}
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.2\lib\NET35;packages\Unity.2.1.505.0\lib\NET35
EndGlobalSection
EndGlobal
4 changes: 4 additions & 0 deletions schemas/manifest_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"dependencies": {
"type": "array",
"description": "The dependencies of the mod",
"default": [],
"items": {
"type": "string",
"description": "The uniqueName of the dependency",
Expand All @@ -91,6 +92,7 @@
"minGameVersion": {
"type": "string",
"description": "The minimum version of the game that this mod is compatible with",
"default": "",
"pattern": "^\\d+\\.\\d+\\.\\d+\\.\\d+$",
"examples": [
"1.0.0",
Expand All @@ -101,6 +103,7 @@
"maxGameVersion": {
"type": "string",
"description": "The maximum version of the game that this mod is compatible with",
"default": "",
"pattern": "^\\d+\\.\\d+\\.\\d+\\.\\d+$",
"examples": [
"1.0.0",
Expand All @@ -116,6 +119,7 @@
"incompatibleVendors": {
"type": "array",
"description": "The vendors this mod does not work on",
"default": [],
"items": {
"type": "string",
"enum": [
Expand Down
5 changes: 4 additions & 1 deletion src/OWML.Common/Interfaces/IModHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OWML.Common.Menus;
using OWML.Common.Interfaces;
using OWML.Common.Menus;
using System;

namespace OWML.Common
Expand Down Expand Up @@ -31,5 +32,7 @@ public interface IModHelper
IModInteraction Interaction { get; }

IMenuManager MenuHelper { get; }

IModTranslations MenuTranslations { get; }
}
}
7 changes: 7 additions & 0 deletions src/OWML.Common/Interfaces/IModTranslations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace OWML.Common.Interfaces
{
public interface IModTranslations
{
public string GetLocalizedString(string key);
}
}
24 changes: 17 additions & 7 deletions src/OWML.ModHelper.Menus/ModConfigMenuBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using OWML.Common;
using OWML.Common.Menus;
using OWML.Utils;
using OWML.Common.Interfaces;

namespace OWML.ModHelper.Menus
{
Expand All @@ -19,6 +20,8 @@ public abstract class ModConfigMenuBase : ModMenuWithSelectables, IModConfigMenu
private IModNumberInput _numberInputTemplate;
private IModSeparator _seperatorTemplate;

private IModTranslations _translations;

protected abstract void AddInputs();

public abstract void UpdateUIValues();
Expand All @@ -28,6 +31,8 @@ protected ModConfigMenuBase(IModManifest manifest, IModStorage storage, IModCons
{
Manifest = manifest;
Storage = storage;

_translations = new ModTranslations(manifest, console);
}

public void Initialize(Menu menu, IModToggleInput toggleTemplate, IModSliderInput sliderTemplate,
Expand Down Expand Up @@ -111,7 +116,7 @@ private void AddToggleInput(string key, int index, JObject obj = null)
{
var toggle = AddToggleInput(_toggleTemplate.Copy(key), index);
toggle.Element.name = key;
toggle.Title = (string)obj?["title"] ?? key;
SetupTitle(toggle, (string)obj?["title"], key);
SetupInputTooltip(toggle, (string)obj?["tooltip"]);
toggle.Show();
}
Expand All @@ -122,7 +127,7 @@ private void AddSliderInput(string key, int index, JObject obj)
slider.Min = (float)obj["min"];
slider.Max = (float)obj["max"];
slider.Element.name = key;
slider.Title = (string)obj["title"] ?? key;
SetupTitle(slider, (string)obj?["title"], key);
SetupInputTooltip(slider, (string)obj["tooltip"]);
slider.Show();
}
Expand All @@ -132,7 +137,7 @@ private void AddSelectorInput(string key, int index, JObject obj)
var options = obj["options"].ToObject<string[]>();
var selector = AddSelectorInput(_selectorTemplate.Copy(key), index);
selector.Element.name = key;
selector.Title = (string)obj["title"] ?? key;
SetupTitle(selector, (string)obj?["title"], key);
selector.Initialize((string)obj["value"], options);
SetupInputTooltip(selector, (string)obj["tooltip"]);
selector.Show();
Expand All @@ -142,7 +147,7 @@ private void AddTextInput(string key, int index, JObject obj = null)
{
var textInput = AddTextInput(_textInputTemplate.Copy(key), index);
textInput.Element.name = key;
textInput.Title = (string)obj?["title"] ?? key;
SetupTitle(textInput, (string)obj?["title"], key);
SetupInputTooltip(textInput, (string)obj?["tooltip"]);
textInput.Show();
}
Expand All @@ -151,7 +156,7 @@ private void AddNumberInput(string key, int index, JObject obj = null)
{
var numberInput = AddNumberInput(_numberInputTemplate.Copy(key), index);
numberInput.Element.name = key;
numberInput.Title = (string)obj?["title"] ?? key;
SetupTitle(numberInput, (string)obj?["title"], key);
SetupInputTooltip(numberInput, (string)obj?["tooltip"]);
numberInput.Show();
}
Expand All @@ -160,15 +165,20 @@ private void AddSeparator(string key, int index, JObject obj)
{
var separator = AddSeparator(_seperatorTemplate.Copy("Inputs"), index);
separator.Element.name = key;
separator.Title = (string)obj?["title"] ?? key;
SetupTitle(separator, (string)obj?["title"], key);
separator.Show();
}

internal void SetupInputTooltip<T>(IModInput<T> input, string tooltip)
{
var menuOption = input.Element.GetComponent<MenuOption>();
menuOption.SetValue("_tooltipTextType", UITextType.None);
menuOption.SetValue("_overrideTooltipText", tooltip?? "");
menuOption.SetValue("_overrideTooltipText", _translations.GetLocalizedString(tooltip) ?? "");
}

internal void SetupTitle(IModInputBase input, string title, string key)
{
input.Title = title == null ? key : _translations.GetLocalizedString(title);
}
}
}
92 changes: 92 additions & 0 deletions src/OWML.ModHelper.Menus/ModTranslations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Newtonsoft.Json.Linq;
using OWML.Common;
using OWML.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;

namespace OWML.ModHelper.Menus
{
public class ModTranslations : IModTranslations
{
private Dictionary<TextTranslation.Language, Dictionary<string, string>> _translationTable = new();

private IModManifest _manifest;
private IModConsole _console;

// Menu translations are stored under UIDictionary
// This means OWML config translations follow the New Horizons format
public static readonly string UIDictionary = nameof(UIDictionary);

private bool _initialized;

public ModTranslations(IModManifest manifest, IModConsole console)
{
_manifest = manifest;
_console = console;
}

private void Init()
{
try
{
var translationsFolder = Path.Combine(_manifest.ModFolderPath, "translations");
foreach (TextTranslation.Language translation in Enum.GetValues(typeof(TextTranslation.Language)))
{
var filename = Path.Combine(translationsFolder, $"{translation}.json");
if (File.Exists(filename))
{
var dict = JObject.Parse(File.ReadAllText(filename)).ToObject<Dictionary<string, object>>();
if (dict.ContainsKey(UIDictionary))
{
_translationTable[translation] = (Dictionary<string, string>)(dict[nameof(UIDictionary)] as JObject).ToObject(typeof(Dictionary<string, string>));
}
}
}
_initialized = true;
}
catch (Exception ex)
{
_console.WriteLine($"Failed to initialize mod option translations {ex}", MessageType.Error);
}
}

public string GetLocalizedString(string key)
{
if (!_initialized)
{
Init();
}

try
{
if (key == null) return null;
if (key == string.Empty) return string.Empty;

if (!_translationTable.TryGetValue(TextTranslation.Get().m_language, out var dict))
{
// Default to English
if (!_translationTable.TryGetValue(TextTranslation.Language.ENGLISH, out dict))
{
// Default to key
return key;
}
}

if (dict.TryGetValue(key, out var value))
{
return value;
}
else
{
return key;
}
}
catch (Exception ex)
{
_console.WriteLine($"Failed to load options translation: {ex}", MessageType.Error);
return key;
}
}
}
}
6 changes: 3 additions & 3 deletions src/OWML.ModHelper.Menus/NewMenuSystem/MenuManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void SaveConfig()
foreach (var (name, setting) in mod.ModHelper.Config.Settings)
{
var settingType = GetSettingType(setting);
var label = name;
var label = mod.ModHelper.MenuTranslations.GetLocalizedString(name);
var tooltip = "";

var settingObject = setting as JObject;
Expand All @@ -233,12 +233,12 @@ void SaveConfig()
{
if (settingObject["title"] != null)
{
label = settingObject["title"].ToString();
label = mod.ModHelper.MenuTranslations.GetLocalizedString(settingObject["title"].ToString());
}

if (settingObject["tooltip"] != null)
{
tooltip = settingObject["tooltip"].ToString();
tooltip = mod.ModHelper.MenuTranslations.GetLocalizedString(settingObject["tooltip"].ToString());
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/OWML.ModHelper/ModHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OWML.Common;
using OWML.Common.Interfaces;
using OWML.Common.Menus;

namespace OWML.ModHelper
Expand Down Expand Up @@ -31,6 +32,8 @@ public class ModHelper : IModHelper

public IMenuManager MenuHelper { get; }

public IModTranslations MenuTranslations { get; }

public ModHelper(
IModLogger logger,
IModConsole console,
Expand All @@ -44,7 +47,8 @@ public ModHelper(
IModDefaultConfig defaultConfig,
IOwmlConfig owmlConfig,
IModInteraction interaction,
IMenuManager menuHelper)
IMenuManager menuHelper,
IModTranslations menuTranslations)
{
Logger = logger;
Console = console;
Expand All @@ -59,6 +63,7 @@ public ModHelper(
OwmlConfig = owmlConfig;
Interaction = interaction;
MenuHelper = menuHelper;
MenuTranslations = menuTranslations;
}
}
}
Loading
Loading