Skip to content

Instantly share code, notes, and snippets.

@Draknek
Created October 20, 2022 20:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Draknek/a1b8b9dcfd056ba345740532aabc83fd to your computer and use it in GitHub Desktop.
Save Draknek/a1b8b9dcfd056ba345740532aabc83fd to your computer and use it in GitHub Desktop.
Unity localisation scripts - pulling data from Google Sheets
StringsInspector.cs is the UI code (goes in an Assets/Editor folder so it only gets loaded in the editor)
Strings.Import is the function that takes the spreadsheet data and puts it into the ScriptableObject
Language.cs/Languages.cs might not be relevant but I see they're referenced here so I'm sharing them too
using System;
using System.Linq;
using UnityEngine;
namespace Localization
{
[System.Serializable]
public class Language
{
[SerializeField] string _id;
public string id => _id;
[SerializeField] SystemLanguage _systemLanguage;
public SystemLanguage systemLanguage => _systemLanguage;
[SerializeField] string _cultureid;
/// <summary>
/// Returns the culture string stored in the language. Unless English is selected in which case it checks for AU/US/GB and builds it's own string.
/// </summary>
public string cultureid
{
get
{
/*if (_systemLanguage == SystemLanguage.English)
{
string[] allowedRegions = {"AU","US","GB"};
string regionString = System.Globalization.RegionInfo.CurrentRegion?.ToString().ToUpper();
if ( string.IsNullOrEmpty(regionString) == false && allowedRegions.Contains(regionString) )
{
return "en-" + regionString;
}
}*/
return _cultureid;
}
}
[SerializeField] LanguageStyles _styleSheet;
public LanguageStyles styleSheet => _styleSheet;
[SerializeField] private bool _useImageToDisplayInDropdown = true;
public bool useImageToDisplayInDropdown => _useImageToDisplayInDropdown;
[SerializeField] private string _specificDateFormatString = null;
public string specificDateFormatString => _specificDateFormatString;
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Localization
{
//[CreateAssetMenu]
public class Languages : ScriptableObject
{
[SerializeField] List<Language> languages = null;
[SerializeField] int defaultLanguageIndex = 0;
public Language defaultLanguage => languages[defaultLanguageIndex];
public List<Language> All()
{
return languages;
}
public string Auto()
{
//Force through different English regions
if (Application.systemLanguage == SystemLanguage.English)
{
string regionString = System.Globalization.RegionInfo.CurrentRegion?.ToString().ToUpper();
if (regionString == "GB")
{
return "en-GB";
}
if (regionString == "AU")
{
return "en-AU";
}
//US
return "en";
}
#if UNITY_SWITCH
//System language is not as accurate with Application.systemLanguage so lets do a custom string check first
string switchLanguageID = GetSwitchSystemLanguage();
if (String.IsNullOrEmpty(switchLanguageID) == false)
{
foreach (var language in languages)
{
if (language.id == switchLanguageID)
{
return language.id;
}
}
}
#endif
foreach (var language in languages)
{
if (language.systemLanguage == Application.systemLanguage)
{
return language.id;
}
}
return defaultLanguage.id;
}
public Language AutoLanguage()
{
foreach (var language in languages)
{
if (language.systemLanguage == Application.systemLanguage)
{
return language;
}
}
return defaultLanguage;
}
public Language GetByName(string _language)
{
if(_language == "auto")
{
_language = Auto();
}
foreach (var language in languages)
{
if (language.id == _language)
{
return language;
}
}
return defaultLanguage;
}
#if UNITY_EDITOR
/// <summary>
/// This just makes sure the number of styles is correct and they have the right name
/// </summary>
void OnValidate()
{
if (Application.isPlaying)
return;
foreach (var language in languages)
{
if (language.styleSheet.styleTags.Length < (int) StyleType.Count)
{
Array.Resize(ref language.styleSheet.styleTags, (int) StyleType.Count);
}
for (int i = 0; i < language.styleSheet.styleTags.Length; i++)
{
language.styleSheet.styleTags[i].setNameByStyle((StyleType)i);
}
}
}
#endif
#if UNITY_SWITCH
public static string GetSwitchSystemLanguage()
{
string systemLanguage = nn.oe.Language.GetDesired();
if (string.IsNullOrEmpty(systemLanguage)) return "en";
systemLanguage = systemLanguage.Trim();
// Do some extra careful trimming here because the Switch SDK might be doing some extra padding.
// This was read about here: https://developer.nintendo.com/group/development/g1kr9vj6/forums/english/-/gts_message_boards/thread/274502428
if (systemLanguage.Contains("\0"))
systemLanguage = systemLanguage.Substring(0, systemLanguage.IndexOf('\0'));
// Languages supported by the Switch can be found here: https://developer.nintendo.com/html/online-docs/nx-en/g1kr9vj6-en/Packages/SDK/NintendoSDK/Documents/Package/contents/Pages/Page_100911958.html
/*if (systemLanguage == "en-US") CachedSystemLanguageString = "en"; // United States English
if (systemLanguage == "es-419") CachedSystemLanguageString = "es";// Latin American Spanish
if (systemLanguage == "pt") CachedSystemLanguageString = "pt-BR"; // Portuguese
if (systemLanguage == "zh-CN") CachedSystemLanguageString = "zh-Hans"; // Chinese
if (systemLanguage == "zh-TW") CachedSystemLanguageString = "zh-Hant"; // Taiwanese
*/
return systemLanguage;
}
#endif
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Localization
{
//[CreateAssetMenu]
public class Strings : ScriptableObject
{
public string uiSheetURI;
[SerializeField] Languages languages = null;
[SerializeField] List<StringsByLanguageWithId> strings = null;
public string Get(string id, string language = null)
{
if (language == null || language == "auto")
{
//language can equal "auto", but languageData always contains the current in use
language = Config.languageData.id;
}
#if PRERELEASE
if (language == "debug")
{
return id;
}
#endif
var stringByLanguageWithId = strings.Find(x => x.id == id);
if (stringByLanguageWithId == null)
{
return id;
}
var stringWithLanguage = stringByLanguageWithId.languages.Find(x => x.language == language);
if (stringWithLanguage == null)
{
return id;
}
if (string.IsNullOrWhiteSpace(stringWithLanguage.value))
{
Debug.LogWarning($"String '{id}' is blank in {language}, defaulting to en-GB");
stringWithLanguage = stringByLanguageWithId.languages.Find(x => x.language == "en-GB");
if (stringWithLanguage == null)
{
return id;
}
}
#if LANGUAGE_TEST
return "{"+stringWithLanguage.value+"}";
#endif
return stringWithLanguage.value;
}
public void Set(string id, List<(string language, string value)> stringsByLanguage)
{
strings.RemoveAll(x => x.id == id);
var languages = new List<StringWithLanguage>();
foreach (var (language, value) in stringsByLanguage)
{
if (language == "ar")
{
var lines = value.Split('\n');
Array.Reverse( lines );
string newArabicString = ArabicSupport.ArabicFixer.Fix(string.Join("\n", lines));
//string newArabicString = value;
char[] reversedStringArray = newArabicString.ToCharArray();
Array.Reverse(reversedStringArray);
languages.Add(new StringWithLanguage
{
language = language,
value = new string(reversedStringArray)
});
}
else
{
languages.Add(new StringWithLanguage
{
language = language,
value = value
});
}
}
strings.Add(new StringsByLanguageWithId
{
id = id,
languages = languages
});
}
public void Import(string tsv)
{
var values = tsv.Split('\n');
if (values.Length < 2)
return;
string[] languageHeaders = values[0].Split('\t');
for (int i = 0; i < languageHeaders.Length; i++)
{
languageHeaders[i] = languageHeaders[i].Trim('\r', '\n');
}
List<(string language, string value)> stringsByLanguage = new List<(string language, string value)>();
for (var valueIndex = 0; valueIndex < values.Length; valueIndex++)
{
stringsByLanguage.Clear();
var value = values[valueIndex].Split('\t');
// If headers have been copied, skip first line
if (valueIndex == 0 && value[0] == "Line ID")
{
continue;
}
// If line too short, skip
if (value.Length < 3)
{
continue;
}
var lineId = value[0];
for (int i = 2; i < value.Length; i++)
{
stringsByLanguage.Add( ( languageHeaders[i],
value[i].Trim('\r', '\n').Replace('¬','\n') ) );
}
Set(lineId, stringsByLanguage);
}
}
/// <summary>
/// Looks for platform strings and swaps them for the original
/// </summary>
public void InitializeForPlatform()
{
List<int> stringsToRemove = new List<int>();
#if UNITY_SWITCH
for (int i = strings.Count; --i >= 0;)
{
if (strings[i].id.EndsWith("_Switch"))
{
string newID = strings[i].id.Substring(0, strings[i].id.Length - 7);
int indexToRemove = strings.FindIndex(0, x => x.id == newID);
#if !UNITY_EDITOR
if(indexToRemove>=0)
stringsToRemove.Add(indexToRemove);
strings[i].id = newID;
#else
Debug.Log("strings Found on device I would take" + strings[i].id + " and copy over " + newID + " at index " + indexToRemove);
#endif
}
}
#endif
if (stringsToRemove.Count > 0)
{
stringsToRemove.Sort();
}
for (int i = stringsToRemove.Count; --i >= 0;)
{
strings.RemoveAt(stringsToRemove[i]);
}
}
[System.Serializable]
class StringsByLanguageWithId
{
public string id;
public List<StringWithLanguage> languages;
}
[System.Serializable]
class StringWithLanguage
{
public string language;
public string value;
}
}
}
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Net;
[CustomEditor(typeof(Localization.Strings), true)]
public class StringsInspector : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
if (GUILayout.Button("Import UI Strings"))
{
var strings = target as Localization.Strings;
var tsv = HTTPGet(strings.uiSheetURI);
if (!string.IsNullOrEmpty(tsv))
{
strings.Import(tsv);
EditorUtility.SetDirty(strings);
}
}
}
static string HTTPGet(string uri)
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment