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

Multi language support #282

Closed
Closed
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
43 changes: 42 additions & 1 deletion SharedClasses/ConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using CitizenFX.Core;

using Newtonsoft.Json;
using System.Collections;

using static CitizenFX.Core.Native.API;

Expand Down Expand Up @@ -238,9 +239,49 @@ public LocationBlip(string name, Vector3 coordinates, int spriteID, int color)
}
}
#endregion
}

#region Get all the languages from the appropriate json file

/// <summary>
/// Gets and stores the languages from the multiple .json's.
/// </summary>
/// <returns></returns>
public static Dictionary<string, Dictionary<string, string>> GetLanguages()
{
Dictionary<string, Dictionary<string, string>> data = new Dictionary<string, Dictionary<string, string>>();

var metaData = GetResourceMetadata(GetCurrentResourceName(), "languages", GetNumResourceMetadata(GetCurrentResourceName(), "languages") - 1).Replace(" ", "");
if (!string.IsNullOrEmpty(metaData))
{
var languages = metaData.Split(',');
foreach (var lang in languages)
{
try
{
string jsonFile = LoadResourceFile(GetCurrentResourceName(), $"config/languages/{lang}.json");
if (!string.IsNullOrEmpty(jsonFile))
{
data.Add(lang, JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonFile));
}
else
{
#if CLIENT
vMenuClient.Notify.Error($"Unable to load {lang}.json.");
#endif
}
}
catch
{
#if CLIENT
vMenuClient.Notify.Error($"Unable to load {lang}.json.");
#endif
}
}
}

return data;
}

#endregion
}
}
1 change: 1 addition & 0 deletions SharedClasses/PermissionsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public enum Permission
DontBanMe,
NoClip,
Staff,
DumpLang,
#endregion

// Online Players
Expand Down
45 changes: 23 additions & 22 deletions vMenu/EntitySpawner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using CitizenFX.Core;
Expand All @@ -22,16 +23,16 @@ public class EntitySpawner : BaseScript
public EntitySpawner()
{
#if DEBUG
RegisterCommand("testEntity", new Action<int, List<object>>((source, args) =>
{
string prop = (string)args[0];
SpawnEntity(prop, Game.PlayerPed.Position);
}), false);
RegisterCommand("endTest", new Action(() =>
{
FinishPlacement();
}), false);
RegisterCommand("testEntity", new Action<int, List<object>>((source, args) =>
{
var prop = (string)args[0];
SpawnEntity(prop, Game.PlayerPed.Position);
}), false);

RegisterCommand("endTest", new Action(() =>
{
FinishPlacement();
}), false);
#endif
}

Expand Down Expand Up @@ -109,8 +110,8 @@ public static async void FinishPlacement(bool duplicate = false)
{
if (duplicate)
{
int hash = CurrentEntity.Model.Hash;
Vector3 position = CurrentEntity.Position;
var hash = CurrentEntity.Model.Hash;
var position = CurrentEntity.Position;
CurrentEntity = null;
await Delay(1); // Mandatory
SpawnEntity((uint)hash, position);
Expand Down Expand Up @@ -154,7 +155,7 @@ private void DrawButtons() //TODO: Right keys
/// <returns>Output direction vector</returns>
private Vector3 RotationToDirection(Vector3 rotation)
{
Vector3 adj = new Vector3(
var adj = new Vector3(
(float)Math.PI / 180f * rotation.X,
(float)Math.PI / 180f * rotation.Y,
(float)Math.PI / 180f * rotation.Z
Expand All @@ -173,20 +174,20 @@ private Vector3 RotationToDirection(Vector3 rotation)
/// <returns>destination if no hit was found and coords of hit if there was one</returns>
private Vector3 GetCoordsPlayerIsLookingAt()
{
Vector3 camRotation = GetGameplayCamRot(0);
Vector3 camCoords = GetGameplayCamCoord();
Vector3 camDirection = RotationToDirection(camRotation);
var camRotation = GetGameplayCamRot(0);
var camCoords = GetGameplayCamCoord();
var camDirection = RotationToDirection(camRotation);

Vector3 dest = new Vector3(
var dest = new Vector3(
camCoords.X + (camDirection.X * RayDistance),
camCoords.Y + (camDirection.Y * RayDistance),
camCoords.Z + (camDirection.Z * RayDistance)
);

RaycastResult res = World.Raycast(camCoords, dest, IntersectOptions.Everything, Game.PlayerPed);
var res = World.Raycast(camCoords, dest, IntersectOptions.Everything, Game.PlayerPed);

#if DEBUG
DrawLine(Game.PlayerPed.Position.X, Game.PlayerPed.Position.Y,Game.PlayerPed.Position.Z, dest.X, dest.Y, dest.Z, 255, 0, 0, 255);
DrawLine(Game.PlayerPed.Position.X, Game.PlayerPed.Position.Y, Game.PlayerPed.Position.Z, dest.X, dest.Y, dest.Z, 255, 0, 0, 255);
#endif

return res.DitHit ? res.HitPosition : dest;
Expand Down Expand Up @@ -219,7 +220,7 @@ private async Task MoveHandler()
}
}

float headingOffset = 0f;
var headingOffset = 0f;
while (Active)
{
if (CurrentEntity == null || !CurrentEntity.Exists())
Expand All @@ -228,7 +229,7 @@ private async Task MoveHandler()
CurrentEntity = null;
break;
}
int handle = CurrentEntity.Handle;
var handle = CurrentEntity.Handle;

DrawButtons();

Expand All @@ -238,7 +239,7 @@ private async Task MoveHandler()
SetEntityAlpha(handle, (int)(255 * 0.4), 0);
CurrentEntity.Heading = (GetGameplayCamRot(0).Z + headingOffset) % 360f;

Vector3 newPosition = GetCoordsPlayerIsLookingAt();
var newPosition = GetCoordsPlayerIsLookingAt();

CurrentEntity.Position = newPosition;
if (CurrentEntity.HeightAboveGround < 3.0f)
Expand Down
219 changes: 219 additions & 0 deletions vMenu/LanguageManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
using CitizenFX.Core;
using MenuAPI;
using System.Collections.Generic;
using System.Linq;

/*
* TODO:
* • Notifications
*/

namespace vMenuClient
{
class LanguageManager : BaseScript
{
#region Properties

/// <summary>
/// The list of languages from the server.
/// </summary>
public static Dictionary<string, Dictionary<string, string>> Languages { get; set; } = new Dictionary<string, Dictionary<string, string>>();

/// <summary>
/// The original language of the menus.
/// </summary>
private static Dictionary<string, string> OriginalLanguage { get; set; } = new Dictionary<string, string>();

#endregion

#region Fields

/// <summary>
/// The list of menus extracted by the GetMenu() method.
/// </summary>
private static readonly List<Menu> ExtractedListOfMenus = new List<Menu>();

/// <summary>
/// To indicate whether the original language has been stored yet.
/// </summary>
private static bool IsOriginalLanguageStored = false;

#endregion

#region Constructor

public LanguageManager()
{
TranslateMenus(true);
}

#endregion

#region Events

#region Dump a language template

[EventHandler("vMenu:DumpLanguageTamplate:Client")]
void DumpLanguageTemplate()
{
// Just to make sure
OriginalLanguage.Remove(Game.Player.Name);
OriginalLanguage.Remove("vMenu");

// Send to the server
TriggerServerEvent("vMenu:DumpLanguageTemplate:Server", Newtonsoft.Json.JsonConvert.SerializeObject(OriginalLanguage, Newtonsoft.Json.Formatting.Indented));
}

#endregion

#endregion

#region Tools

#region Translate menus

/// <summary>
/// Translate all of the menus' text and descriptions.
/// </summary>
public async static void TranslateMenus(bool slowDownLoop = false)
{
// Wait for it to be completed
while (!IsOriginalLanguageStored)
{
await Delay(500);
}

for (int i = 0; i < ExtractedListOfMenus.Count; i++)
{
Menu m = ExtractedListOfMenus[i];
List<MenuItem> list = m.GetMenuItems();

for (int i1 = 0; i1 < list.Count; i1++)
{
MenuItem item = list[i1];

if (!string.IsNullOrEmpty(item.ParentMenu.MenuTitle))
item.ParentMenu.MenuTitle = TranslateHelper(item.ParentMenu.MenuTitle);

if (!string.IsNullOrEmpty(item.ParentMenu.MenuSubtitle))
item.ParentMenu.MenuSubtitle = TranslateHelper(item.ParentMenu.MenuSubtitle);

if (!string.IsNullOrEmpty(item.Text))
item.Text = TranslateHelper(item.Text);

if (!string.IsNullOrEmpty(item.Description))
item.Description = TranslateHelper(item.Description);

// Solves resource time warning spiking so high when building the menu
if (slowDownLoop)
{
await Delay(10);
}
}
}
}

#endregion

#region Translate helper

private static string TranslateHelper(string text)
{
// Reset the text to the original language.
text = OriginalLanguage.ContainsValue(text) ? OriginalLanguage.First(x => x.Value == text).Key : text;

// Check if the languages dictionary contains the key matching the text.
if (Languages.ContainsKey(MiscSettings.CurrentLanguage) && Languages[MiscSettings.CurrentLanguage].ContainsKey(text))
{
// Update the original language value with the new language translation.
OriginalLanguage[text] = Languages[MiscSettings.CurrentLanguage][text];

// Return the new translation.
return Languages[MiscSettings.CurrentLanguage][text];
}
else
{
// These are the duplicate words that lost their way in the dictionary, we can recover them like this.
foreach (var lang in Languages)
{
if (lang.Value.ContainsValue(text))
{
// Default the text.
text = lang.Value.First(x => x.Value.Equals(text)).Key;

// Update it.
if (Languages.ContainsKey(MiscSettings.CurrentLanguage) && Languages[MiscSettings.CurrentLanguage].ContainsKey(text))
{
// Return the new translation.
return Languages[MiscSettings.CurrentLanguage][text];
}
}
}
}

return text;
}

#endregion

#region Update original langauge

public static void UpdateOriginalLanguage()
{
for (int i = 0; i < ExtractedListOfMenus.Count; i++)
{
Menu m = ExtractedListOfMenus[i];
List<MenuItem> list = m.GetMenuItems();

for (int i1 = 0; i1 < list.Count; i1++)
{
MenuItem item = list[i1];

if (!OriginalLanguage.ContainsKey(item.ParentMenu.MenuTitle))
{
if (!item.ParentMenu.MenuTitle.Equals(Game.Player.Name) && !item.ParentMenu.MenuTitle.Equals("vMenu"))
{
OriginalLanguage.Add(item.ParentMenu.MenuTitle, item.ParentMenu.MenuTitle);
}
}
if (!string.IsNullOrEmpty(item.ParentMenu.MenuSubtitle) && !OriginalLanguage.ContainsKey(item.ParentMenu.MenuSubtitle))
{
if (!item.ParentMenu.MenuSubtitle.Equals(Game.Player.Name))
{
OriginalLanguage.Add(item.ParentMenu.MenuSubtitle, item.ParentMenu.MenuSubtitle);
}
}
if (!OriginalLanguage.ContainsKey(item.Text))
{
OriginalLanguage.Add(item.Text, item.Text);
}
if (!string.IsNullOrEmpty(item.Description) && !OriginalLanguage.ContainsKey(item.Description))
{
OriginalLanguage.Add(item.Description, item.Description);
}
}
}

IsOriginalLanguageStored = true;
}

#endregion

#region Get menu

/// <summary>
/// Get the menu and store it to a list to be used to translate.
/// </summary>
/// <param name="menu"></param>
/// <returns></returns>
public Menu GetMenu(Menu menu)
{
ExtractedListOfMenus.Add(menu);
return menu;
}

#endregion

#endregion
}
}
Loading