├── ProjectSettings ├── ProjectVersion.txt ├── TagManager.asset ├── TimeManager.asset ├── AudioManager.asset ├── EditorSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NavMeshLayers.asset ├── NetworkManager.asset ├── DynamicsManager.asset ├── GraphicsSettings.asset ├── ProjectSettings.asset ├── QualitySettings.asset ├── UnityAdsSettings.asset ├── ClusterInputManager.asset ├── EditorBuildSettings.asset ├── Physics2DSettings.asset ├── UnityAnalyticsManager.asset └── UnityConnectSettings.asset ├── docs ├── img │ ├── main-window-1.png │ ├── plural-form-1.png │ ├── root-window-1.png │ ├── watch-file-1.png │ ├── watch-file-2.png │ ├── watch-file-3.png │ ├── getting-started-1.png │ ├── getting-started-2.png │ ├── getting-started-3.png │ ├── getting-started-4.png │ ├── getting-started-5.png │ ├── getting-started-6.png │ ├── getting-started-7.png │ ├── getting-started-8.png │ ├── localize-fonts-1.png │ ├── localize-fonts-2.png │ ├── translate-window-1.png │ ├── microsoft-translator-1.jpeg │ ├── microsoft-translator-2.png │ ├── microsoft-translator-3.png │ ├── microsoft-translator-4.png │ ├── microsoft-translator-5.png │ ├── microsoft-translator-6.png │ ├── microsoft-translator-7.png │ ├── microsoft-translator-8.png │ └── microsoft-translator-9.png ├── setting-the-languagemanager-to-persist-between-scenes.md ├── translate-window.md ├── how-to-localize-fonts.md ├── root-window.md ├── main-window.md ├── how-to-load-device-language.md ├── getting-keys-and-values-from-editor.md ├── append-and-change-data-runtime.md ├── how-to-use-watch-file.md ├── how-to-use-microsoft-translator.md ├── getting-started.md └── plural-form-feature.md ├── Assets ├── SmartLocalization │ ├── Examples │ │ ├── LoadAllLanguages.unity │ │ └── LoadAllLanguages.cs │ ├── Scripts │ │ ├── Editor │ │ │ ├── ThirdParty │ │ │ │ ├── NPOI │ │ │ │ │ ├── NPOI.dll │ │ │ │ │ ├── NPOI.OOXML.dll │ │ │ │ │ ├── NPOI.OpenXml4Net.dll │ │ │ │ │ ├── NPOI.OpenXmlFormats.dll │ │ │ │ │ └── ICSharpCode.SharpZipLib.dll │ │ │ │ ├── CSVHelper │ │ │ │ │ └── CsvHelper.dll │ │ │ │ ├── ExcelLibrary │ │ │ │ │ └── ExcelLibrary.dll │ │ │ │ └── Rotorz │ │ │ │ │ └── Reorderable List Field │ │ │ │ │ ├── Support │ │ │ │ │ ├── Resources.zip │ │ │ │ │ ├── User Guide.pdf │ │ │ │ │ ├── API Reference.chm │ │ │ │ │ └── API Reference.chw │ │ │ │ │ ├── LICENSE.txt │ │ │ │ │ ├── Editor │ │ │ │ │ ├── Internal │ │ │ │ │ │ └── GUIHelper.cs │ │ │ │ │ ├── ReorderableListFlags.cs │ │ │ │ │ ├── IReorderableListAdaptor.cs │ │ │ │ │ ├── GenericListAdaptor.cs │ │ │ │ │ └── SerializedPropertyAdaptor.cs │ │ │ │ │ └── README.txt │ │ │ ├── EditorWindows │ │ │ │ ├── ListControls │ │ │ │ │ ├── SettingsMenuControl.cs │ │ │ │ │ ├── SettingsListAdaptor.cs │ │ │ │ │ ├── CreateLanguageListAdaptor.cs │ │ │ │ │ ├── SmartCultureInfoListAdaptor.cs │ │ │ │ │ ├── LocalizedObjectMenuControl.cs │ │ │ │ │ ├── CreateLanguageMenuControl.cs │ │ │ │ │ ├── SmartCultureInfoMenuControl.cs │ │ │ │ │ └── LocalizedObjectListAdaptor.cs │ │ │ │ ├── LanguageUpdateWindow.cs │ │ │ │ ├── LanguageExportWindow.cs │ │ │ │ ├── CreateLanguageWindow.cs │ │ │ │ ├── LanguageImportWindow.cs │ │ │ │ └── BulkUpdateWindow.cs │ │ │ ├── FileSystem │ │ │ │ ├── StringExtensions.cs │ │ │ │ ├── CSVExporter.cs │ │ │ │ ├── DirectoryUtility.cs │ │ │ │ ├── FileUtility.cs │ │ │ │ └── CSVParser.cs │ │ │ ├── InspectorScripts │ │ │ │ ├── LocalizedGUITextInspector.cs │ │ │ │ ├── LocalizedGUITextureInspector.cs │ │ │ │ └── LocalizedAudioSourceInspector.cs │ │ │ ├── Utility │ │ │ │ ├── SmartCultureInfoEx.cs │ │ │ │ ├── EditorThreadQueuer.cs │ │ │ │ ├── LocalizationWindowUtility.cs │ │ │ │ ├── LanguageDictionaryHelper.cs │ │ │ │ └── LocalizedKeySelector.cs │ │ │ ├── FileOperations │ │ │ │ └── CustomResxImporter.cs │ │ │ └── Translation │ │ │ │ └── IAutomaticTranslator.cs │ │ ├── ILocalizedAssetLoader.cs │ │ ├── StringExtensions.cs │ │ ├── uGUI │ │ │ ├── LocalizedText.cs │ │ │ └── Editor │ │ │ │ └── LocalizedTextInspector.cs │ │ ├── LocalizedGUIText.cs │ │ ├── LocalizedGUITexture.cs │ │ ├── NGUI │ │ │ ├── SL_NGUILabel.cs │ │ │ ├── SL_NGUISpriteName.cs │ │ │ ├── Editor │ │ │ │ ├── SL_NGUILabelInspector.cs │ │ │ │ ├── SL_NGUISpriteNameInspector.cs │ │ │ │ └── SL_NGUISpriteAtlasInspector.cs │ │ │ └── SL_NGUISpriteAtlas.cs │ │ ├── LocalizedAudioSource.cs │ │ ├── RuntimeLocalizedAssetLoader.cs │ │ ├── LocalizationSystem │ │ │ ├── SmartCultureInfoCollectionDeserializer.cs │ │ │ └── SmartCultureInfo.cs │ │ ├── LanguageRuntimeData.cs │ │ ├── LanguageParser.cs │ │ ├── ApplicationExtensions.cs │ │ └── LanguageDataHandler.cs │ └── Resources │ │ └── EmptyResourceHeader.txt └── Tests │ ├── EditorAssemblyInfo.cs │ └── Editor │ ├── ApplicationExtensionTests.cs │ ├── CSVExporterTests.cs │ ├── Mocks │ └── MockLocalizedAssetLoader.cs │ ├── SmartCultureInfoTests.cs │ ├── LanguageDictionaryHelperTests.cs │ ├── XLSExporterTests.cs │ └── CSVParserTests.cs ├── package.json ├── .gitignore ├── Gulpfile.js └── README.md /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2018.1.6f1 2 | -------------------------------------------------------------------------------- /docs/img/main-window-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/main-window-1.png -------------------------------------------------------------------------------- /docs/img/plural-form-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/plural-form-1.png -------------------------------------------------------------------------------- /docs/img/root-window-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/root-window-1.png -------------------------------------------------------------------------------- /docs/img/watch-file-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/watch-file-1.png -------------------------------------------------------------------------------- /docs/img/watch-file-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/watch-file-2.png -------------------------------------------------------------------------------- /docs/img/watch-file-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/watch-file-3.png -------------------------------------------------------------------------------- /docs/img/getting-started-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-1.png -------------------------------------------------------------------------------- /docs/img/getting-started-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-2.png -------------------------------------------------------------------------------- /docs/img/getting-started-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-3.png -------------------------------------------------------------------------------- /docs/img/getting-started-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-4.png -------------------------------------------------------------------------------- /docs/img/getting-started-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-5.png -------------------------------------------------------------------------------- /docs/img/getting-started-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-6.png -------------------------------------------------------------------------------- /docs/img/getting-started-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-7.png -------------------------------------------------------------------------------- /docs/img/getting-started-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/getting-started-8.png -------------------------------------------------------------------------------- /docs/img/localize-fonts-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/localize-fonts-1.png -------------------------------------------------------------------------------- /docs/img/localize-fonts-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/localize-fonts-2.png -------------------------------------------------------------------------------- /docs/img/translate-window-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/translate-window-1.png -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshLayers.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/NavMeshLayers.asset -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /docs/img/microsoft-translator-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-1.jpeg -------------------------------------------------------------------------------- /docs/img/microsoft-translator-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-2.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-3.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-4.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-5.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-6.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-7.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-8.png -------------------------------------------------------------------------------- /docs/img/microsoft-translator-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/docs/img/microsoft-translator-9.png -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityAnalyticsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/UnityAnalyticsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /Assets/SmartLocalization/Examples/LoadAllLanguages.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Examples/LoadAllLanguages.unity -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.OOXML.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.OOXML.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/CSVHelper/CsvHelper.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/CSVHelper/CsvHelper.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.OpenXml4Net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.OpenXml4Net.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/ExcelLibrary/ExcelLibrary.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/ExcelLibrary/ExcelLibrary.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.OpenXmlFormats.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/NPOI.OpenXmlFormats.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/ICSharpCode.SharpZipLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/NPOI/ICSharpCode.SharpZipLib.dll -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/Resources.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/Resources.zip -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/User Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/User Guide.pdf -------------------------------------------------------------------------------- /Assets/Tests/EditorAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | //Makes the internal members visible to to the editor scripts, useful for unit-testing internal classes 2 | #if UNITY_EDITOR 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: InternalsVisibleTo("Assembly-CSharp-Editor")] 6 | #endif -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/API Reference.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/API Reference.chm -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/API Reference.chw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NiklasBorglund/Smart-Localization-2/HEAD/Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Support/API Reference.chw -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/ILocalizedAssetLoader.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace SmartLocalization{ 5 | internal interface ILocalizedAssetLoader { 6 | T LoadAsset(string assetKey, string languageCode) where T : UnityEngine.Object; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/SettingsMenuControl.cs: -------------------------------------------------------------------------------- 1 | namespace SmartLocalization.Editor 2 | { 3 | using UnityEngine; 4 | using System.Collections; 5 | using SmartLocalization.ReorderableList; 6 | using UnityEditor; 7 | internal class SettingsMenuControl : ReorderableListControl 8 | { 9 | public SettingsMenuControl() : base(ReorderableListFlags.HideAddButton | ReorderableListFlags.DisableContextMenu){} 10 | } 11 | } -------------------------------------------------------------------------------- /docs/setting-the-languagemanager-to-persist-between-scenes.md: -------------------------------------------------------------------------------- 1 | # Setting the LanguageManager to persist between scenes 2 | 3 | The LanguageManager singleton instance does not by default persist between scenes. So you need to set this by yourself. 4 | 5 | To achieve this, you can use the static method in the LanguageManager class named SetDontDestroyOnLoad() 6 | ```csharp 7 | LanguageManager.SetDontDestroyOnLoad(); 8 | ``` 9 | The reason for this is mainly of legacy reasons, but that might change in the future. -------------------------------------------------------------------------------- /Assets/Tests/Editor/ApplicationExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using UnityEngine; 4 | 5 | namespace SmartLocalization.Editor 6 | { 7 | [TestFixture] 8 | public class ApplicationExtensionTests 9 | { 10 | [Test] 11 | public void TestGetSystemLanguage() 12 | { 13 | foreach (var systemLanguage in Enum.GetValues(typeof(SystemLanguage))) 14 | { 15 | string systemLanguageString = ApplicationExtensions.GetStringValueOfSystemLanguage((SystemLanguage)systemLanguage); 16 | Assert.AreEqual(systemLanguage.ToString(), systemLanguageString); 17 | } 18 | } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart-localization-2", 3 | "description": "A localization plugin for the Unity3D game engine", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1", 6 | "gulp": "node ./node_modules/gulp/bin/gulp.js" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/NiklasBorglund/SmartLocalization2.git" 11 | }, 12 | "author": "Niklas Borglund & Jakob Hillerström", 13 | "license": "MIT", 14 | "private": true, 15 | "devDependencies": { 16 | "files-exist": "^1.0.1", 17 | "gulp": "^3.9.1", 18 | "gulp-sync": "^0.1.4", 19 | "rimraf": "^2.5.4", 20 | "upath": "^0.2.0" 21 | } 22 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Mono Runtime Version: 4.0.30319.1 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | // ------------------------------------------------------------------------------ 10 | using System; 11 | using SmartLocalization; 12 | 13 | public static class StringExtensions 14 | { 15 | public static string Localize(this string key) 16 | { 17 | return LanguageManager.Instance.GetTextValue(key); 18 | } 19 | } -------------------------------------------------------------------------------- /docs/translate-window.md: -------------------------------------------------------------------------------- 1 | # The Translate Window 2 | 3 | The translate window is where you add the translations to your game. Whether it's text or an asset - it all goes in here. 4 | 5 | The translate window enables you to do the following: 6 | 7 | * Translate text values into the desired languages 8 | * Translate TextAssets, AudioClips, Textures & Prefabs 9 | * Translate fonts to choose the correct font with the supported glyphs 10 | * Automatically translate your text values with Microsoft Translator to another language 11 | * Search & Sort Translations 12 | * Override Asset Translations and use the same asset as another language. 13 | * Copy values from the root language file 14 | * Watch .csv or .xls file for changes and hot reload 15 | 16 | ![alt](img/translate-window-1.png) -------------------------------------------------------------------------------- /docs/how-to-localize-fonts.md: -------------------------------------------------------------------------------- 1 | # How to localize fonts in Smart Localization 2 | To start localizing fonts in Smart Localization, you first need to create a key with a FONT type in the root language file. Don't forget to press save after you've made your changes. 3 | 4 | ![alt](img/localize-fonts-1.png) 5 | After this, you need to set the fonts in your localized languages. You can accomplish this by enterting the Translation Window of the desired language. In this tutorial, I will add the Google Noto Sans font to Swedish. 6 | ![alt](img/localize-fonts-2.png) 7 | To get the font from code, you can use the GetFont() method in the LanguageManager. 8 | 9 | ```csharp 10 | void MyFontRetrievalMethod() 11 | { 12 | var myMainMenuFont = LanguageManager.Instance.GetFont("MainMenu.Font"); 13 | } 14 | ``` -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/FileSystem/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | // StringExtensions.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | using System; 6 | using System.Linq; 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | /// 11 | /// Contains extension methods for string 12 | /// 13 | public static class StringExtensions 14 | { 15 | /// 16 | /// Removes all the whitespaces in a string 17 | /// 18 | /// The string to remove whitespaces from 19 | /// The string with the removed whitespaces 20 | public static string RemoveWhitespace(this string input) 21 | { 22 | return new string(input.ToCharArray() 23 | .Where(c => !Char.IsWhiteSpace(c)) 24 | .ToArray()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/root-window.md: -------------------------------------------------------------------------------- 1 | # The Edit Root Language Window 2 | 3 | The Edit Root Language Window is where you add and change all your keys in your project. It manipulates something called the "root language file". 4 | 5 | You could say that the root language file is the law book of all the other translation files. Everything in this file is law, and all other languages and translation need to abide by their rules. It's the very heart of the Smart Localization plugin. 6 | 7 | In this window you can: 8 | 9 | Add new keys 10 | * Change existing keys 11 | * Set what specific type each key is. e.g. If you want to localize audio, prefabs, textures or fonts other than just text. 12 | * Search for a specific key 13 | 14 | If you change a key here, all the other language files will be instantly changed as well to reflect the new changes. 15 | 16 | 17 | ![alt](img/root-window-1.png) -------------------------------------------------------------------------------- /docs/main-window.md: -------------------------------------------------------------------------------- 1 | # The Main Project Window 2 | 3 | The main project window in Smart Localization is the main navigation hub of our plugin. You can find this window by navigating in your Unity Editor to Window -> Smart Localization. 4 | 5 | This is where you: 6 | 7 | * Create new languages to translate 8 | * Export/Update to and from .csv 9 | * Navigate to the edit root window 10 | * Navigate to the translate language window 11 | * Setup your Automatic Translator Account with Microsoft Translator 12 | * Generate a store presence for App Store & Google Play 13 | * Create any type of new culture that you want to support. Even made up ones! 14 | * Export/Update to and from .xls 15 | * Export/Update all languages to and from a single csv/xls file 16 | 17 | 18 | The picture below shows where you can find the different features and areas. 19 | ![alt](img/main-window-1.png) -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/SettingsListAdaptor.cs: -------------------------------------------------------------------------------- 1 | namespace SmartLocalization.Editor 2 | { 3 | using UnityEngine; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using SmartLocalization.ReorderableList; 7 | 8 | internal class SettingsListAdaptor : GenericListAdaptor 9 | { 10 | public SettingsListAdaptor(List list, ReorderableListControl.ItemDrawer itemDrawer, float itemHeight) 11 | : base(list, itemDrawer, itemHeight) 12 | { 13 | 14 | } 15 | 16 | public override void DrawItem(Rect position, int index) 17 | { 18 | base.DrawItem(position, index); 19 | } 20 | 21 | public override bool CanDrag(int index) 22 | { 23 | return false; 24 | } 25 | 26 | public override bool CanRemove(int index) 27 | { 28 | return false; 29 | } 30 | 31 | public override float GetItemHeight(int index) 32 | { 33 | return base.GetItemHeight(index); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Assets/Tests/Editor/CSVExporterTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using UnityEngine; 4 | 5 | namespace SmartLocalization.Editor 6 | { 7 | [TestFixture] 8 | public class CSVExporterTests 9 | { 10 | 11 | 12 | #pragma warning disable 618 13 | [Test] 14 | public void TestObsoleteGetDelimiter() 15 | { 16 | Assert.AreEqual(CSVExporter.GetDelimiter(CSVDelimiter.COMMA), CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA)); 17 | Assert.AreEqual(CSVExporter.GetDelimiter(CSVDelimiter.SEMI_COLON), CSVParser.GetDelimiter(CSVParser.Delimiter.SEMI_COLON)); 18 | Assert.AreEqual(CSVExporter.GetDelimiter(CSVDelimiter.TAB), CSVParser.GetDelimiter(CSVParser.Delimiter.TAB)); 19 | Assert.AreEqual(CSVExporter.GetDelimiter(CSVDelimiter.VERTICAL_BAR), CSVParser.GetDelimiter(CSVParser.Delimiter.VERTICAL_BAR)); 20 | Assert.AreEqual(CSVExporter.GetDelimiter(CSVDelimiter.CARET), CSVParser.GetDelimiter(CSVParser.Delimiter.CARET)); 21 | } 22 | #pragma warning restore 618 23 | } 24 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/CreateLanguageListAdaptor.cs: -------------------------------------------------------------------------------- 1 | namespace SmartLocalization.Editor 2 | { 3 | using UnityEngine; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using SmartLocalization.ReorderableList; 7 | 8 | 9 | internal class CreateLanguageListAdaptor : GenericListAdaptor 10 | { 11 | public CreateLanguageListAdaptor(List list, ReorderableListControl.ItemDrawer itemDrawer, float itemHeight) 12 | : base(list, itemDrawer, itemHeight) 13 | { 14 | 15 | } 16 | 17 | public override void DrawItem(Rect position, int index) 18 | { 19 | base.DrawItem(position, index); 20 | } 21 | 22 | public SmartCultureInfo GetCultureInfo(int itemIndex) 23 | { 24 | return this[itemIndex]; 25 | } 26 | 27 | public override bool CanDrag(int index) 28 | { 29 | return false; 30 | } 31 | 32 | public override bool CanRemove(int index) 33 | { 34 | return false; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/how-to-load-device-language.md: -------------------------------------------------------------------------------- 1 | # How to load the device language in Smart Localization 2 | This is a code snippet on how to set Smart Localization to load the system language in the start method of a script. 3 | 4 | ```csharp 5 | void Start(){ 6 | languageManager = LanguageManager.Instance; 7 | var deviceCulture = languageManager.GetDeviceCultureIfSupported(); 8 | if(deviceCulture != null){ 9 | languageManager.ChangeLanguage(deviceCulture); 10 | } 11 | else{ 12 | //The device language is not available in the current application or it had a region that 13 | //the Application.systemLanguage cannot handle 14 | } 15 | } 16 | ``` 17 | 18 | The built in functions to detect the device language in Smart Localization is entirely based on Application.systemLanguage. That is an enum that only returns the language in plain english and no regional data. So, if you need better control over the device language - writing a native plugin for that is probably the best way to go. -------------------------------------------------------------------------------- /Assets/Tests/Editor/Mocks/MockLocalizedAssetLoader.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | namespace SmartLocalization.Editor{ 6 | 7 | public class AssetSuccessfullyLoadedException : Exception 8 | { 9 | 10 | } 11 | 12 | public class MockLocalizedAssetLoader : ILocalizedAssetLoader 13 | { 14 | public enum Scenario 15 | { 16 | ReturnNull, 17 | ThrowSuccessfullyLoadedExceptions, 18 | } 19 | 20 | Scenario testScenario = Scenario.ReturnNull; 21 | 22 | public Scenario TestScenario { 23 | get { 24 | return testScenario; 25 | } 26 | set { 27 | testScenario = value; 28 | } 29 | } 30 | 31 | public T LoadAsset (string assetKey, string languageCode) where T : UnityEngine.Object 32 | { 33 | switch(testScenario) 34 | { 35 | case Scenario.ThrowSuccessfullyLoadedExceptions: 36 | { 37 | throw new AssetSuccessfullyLoadedException(); 38 | } 39 | case Scenario.ReturnNull: 40 | default: 41 | return null; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/uGUI/LocalizedText.cs: -------------------------------------------------------------------------------- 1 | namespace SmartLocalization.Editor 2 | { 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using System.Collections; 6 | 7 | [RequireComponent (typeof (Text))] 8 | public class LocalizedText : MonoBehaviour 9 | { 10 | public string localizedKey = "INSERT_KEY_HERE"; 11 | Text textObject; 12 | 13 | void Start () 14 | { 15 | textObject = this.GetComponent(); 16 | 17 | //Subscribe to the change language event 18 | LanguageManager languageManager = LanguageManager.Instance; 19 | languageManager.OnChangeLanguage += OnChangeLanguage; 20 | 21 | //Run the method one first time 22 | OnChangeLanguage(languageManager); 23 | } 24 | 25 | void OnDestroy() 26 | { 27 | if(LanguageManager.HasInstance) 28 | { 29 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 30 | } 31 | } 32 | 33 | void OnChangeLanguage(LanguageManager languageManager) 34 | { 35 | textObject.text = LanguageManager.Instance.GetTextValue(localizedKey); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /docs/getting-keys-and-values-from-editor.md: -------------------------------------------------------------------------------- 1 | # Editor Scripting in Smart Localization: Getting Language Values and Keys 2 | 3 | Getting your language values not just at runtime, but in the editor can be a valuable tool. Especially if you are creating an editor extension or another system that is based on Smart Localization and the values it produces. 4 | 5 | This can be done with a method found in the static class LanguageHandlerEditor named LoadParsedLanguageFile. 6 | 7 | If you want only the keys or the comments in the root language file - you can pass in null as the language code and true as the second parameter that indicates that this is the root file. 8 | ```csharp 9 | var rootValues = LanguageHandlerEditor.LoadParsedLanguageFile(null, true); 10 | ``` 11 | If you'd like to have the language values for a specific language - you pass in the language code of the language you want to load (i.e. "en" "sv" etc.") and false as the second parameter. 12 | ```csharp 13 | var languageValues = LanguageHandlerEditor.LoadParsedLanguageFile("en", false); 14 | ``` -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/uGUI/Editor/LocalizedTextInspector.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor{ 3 | using UnityEngine.UI; 4 | using UnityEngine; 5 | using UnityEditor; 6 | using System.Collections; 7 | 8 | [CustomEditor(typeof(LocalizedText))] 9 | public class LocalizedTextInspector : Editor 10 | { 11 | private string selectedKey = null; 12 | 13 | void Awake() 14 | { 15 | LocalizedText textObject = ((LocalizedText)target); 16 | if(textObject != null) 17 | { 18 | selectedKey = textObject.localizedKey; 19 | } 20 | } 21 | 22 | public override void OnInspectorGUI () 23 | { 24 | base.OnInspectorGUI (); 25 | 26 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.STRING); 27 | 28 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 29 | { 30 | LocalizedText textObject = ((LocalizedText)target); 31 | Undo.RecordObject(textObject, "Set Smart Localization text"); 32 | textObject.localizedKey = selectedKey; 33 | } 34 | } 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LocalizedGUIText.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedGUIText.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization 9 | { 10 | using UnityEngine; 11 | using System.Collections; 12 | 13 | public class LocalizedGUIText : MonoBehaviour 14 | { 15 | public string localizedKey = "INSERT_KEY_HERE"; 16 | 17 | void Start () 18 | { 19 | //Subscribe to the change language event 20 | LanguageManager languageManager = LanguageManager.Instance; 21 | languageManager.OnChangeLanguage += OnChangeLanguage; 22 | 23 | //Run the method one first time 24 | OnChangeLanguage(languageManager); 25 | } 26 | 27 | void OnDestroy() 28 | { 29 | if(LanguageManager.HasInstance) 30 | { 31 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 32 | } 33 | } 34 | 35 | void OnChangeLanguage(LanguageManager languageManager) 36 | { 37 | //Initialize all your language specific variables here 38 | GetComponent().text = LanguageManager.Instance.GetTextValue(localizedKey); 39 | } 40 | } 41 | }//namespace SmartLocalization 42 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LocalizedGUITexture.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedGUITexture.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization 9 | { 10 | using UnityEngine; 11 | using System.Collections; 12 | 13 | public class LocalizedGUITexture : MonoBehaviour 14 | { 15 | public string localizedKey = "INSERT_KEY_HERE"; 16 | 17 | void Start () 18 | { 19 | //Subscribe to the change language event 20 | LanguageManager languageManager = LanguageManager.Instance; 21 | languageManager.OnChangeLanguage += OnChangeLanguage; 22 | 23 | //Run the method one first time 24 | OnChangeLanguage(languageManager); 25 | } 26 | 27 | void OnDestroy() 28 | { 29 | if(LanguageManager.HasInstance) 30 | { 31 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 32 | } 33 | } 34 | 35 | void OnChangeLanguage(LanguageManager languageManager) 36 | { 37 | //Initialize all your language specific variables here 38 | GetComponent().texture = LanguageManager.Instance.GetTexture(localizedKey); 39 | } 40 | } 41 | }//namespace SmartLocalization -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/NGUI/SL_NGUILabel.cs: -------------------------------------------------------------------------------- 1 | // SL_NGUILabel.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | //#define SMARTLOC_NGUI //<--- UNCOMMENT THIS FOR NGUI CLASSES 7 | 8 | #if SMARTLOC_NGUI 9 | using UnityEngine; 10 | using System.Collections; 11 | using SmartLocalization; 12 | 13 | public class SL_NGUILabel : MonoBehaviour 14 | { 15 | public string localizedKey = "INSERT_KEY_HERE"; 16 | UILabel uiLabel = null; 17 | 18 | void Awake() 19 | { 20 | uiLabel = GetComponent(); 21 | } 22 | 23 | void Start () 24 | { 25 | //Subscribe to the change language event 26 | LanguageManager languageManager = LanguageManager.Instance; 27 | languageManager.OnChangeLanguage += OnChangeLanguage; 28 | 29 | OnChangeLanguage(languageManager); 30 | } 31 | 32 | void OnDestroy() 33 | { 34 | if(LanguageManager.HasInstance) 35 | { 36 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 37 | } 38 | } 39 | 40 | void OnChangeLanguage(LanguageManager languageManager) 41 | { 42 | uiLabel.text = LanguageManager.Instance.GetTextValue(localizedKey); 43 | } 44 | } 45 | #endif -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/SmartCultureInfoListAdaptor.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor 3 | { 4 | using UnityEngine; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using SmartLocalization.ReorderableList; 8 | 9 | internal class SmartCultureInfoListAdaptor : GenericListAdaptor 10 | { 11 | public SmartCultureInfoListAdaptor(List list, ReorderableListControl.ItemDrawer itemDrawer, float itemHeight) 12 | : base(list, itemDrawer, itemHeight) 13 | { 14 | 15 | } 16 | 17 | public override void DrawItem(Rect position, int index) 18 | { 19 | base.DrawItem(position, index); 20 | } 21 | 22 | public SmartCultureInfo GetCultureInfo(int itemIndex) 23 | { 24 | return this[itemIndex]; 25 | } 26 | 27 | public override bool CanDrag(int index) 28 | { 29 | return false; 30 | } 31 | 32 | public override bool CanRemove(int index) 33 | { 34 | return IsRoot(GetCultureInfo(index)) ? false : true; 35 | } 36 | 37 | bool IsRoot(SmartCultureInfo info) 38 | { 39 | return info.englishName == "ROOT"; 40 | } 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/NGUI/SL_NGUISpriteName.cs: -------------------------------------------------------------------------------- 1 | // SL_NGUISpriteName.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | //#define SMARTLOC_NGUI //<--- UNCOMMENT THIS FOR NGUI CLASSES 7 | 8 | #if SMARTLOC_NGUI 9 | using UnityEngine; 10 | using System.Collections; 11 | using SmartLocalization; 12 | 13 | public class SL_NGUISpriteName : MonoBehaviour 14 | { 15 | public string localizedKey = "INSERT KEY HERE"; 16 | UISprite uiSprite = null; 17 | 18 | void Awake() 19 | { 20 | uiSprite = GetComponent(); 21 | } 22 | 23 | void Start () 24 | { 25 | //Subscribe to the change language event 26 | LanguageManager languageManager = LanguageManager.Instance; 27 | languageManager.OnChangeLanguage += OnChangeLanguage; 28 | 29 | OnChangeLanguage(languageManager); 30 | } 31 | 32 | void OnDestroy() 33 | { 34 | if(LanguageManager.HasInstance) 35 | { 36 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 37 | } 38 | } 39 | 40 | void OnChangeLanguage(LanguageManager languageManager) 41 | { 42 | uiSprite.spriteName = languageManager.GetTextValue(localizedKey); 43 | } 44 | } 45 | #endif -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/NGUI/Editor/SL_NGUILabelInspector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // SL_NGUILabelInspector.cs 3 | // 4 | // Written by Niklas Borglund and Jakob Hillerström 5 | // 6 | 7 | //#define SMARTLOC_NGUI //<--- UNCOMMENT THIS FOR NGUI CLASSES 8 | 9 | #if SMARTLOC_NGUI 10 | namespace SmartLocalization.Editor 11 | { 12 | 13 | using UnityEngine; 14 | using UnityEditor; 15 | 16 | [CustomEditor(typeof(SL_NGUILabel))] 17 | public class SL_NGUILabelInspector : Editor 18 | { 19 | private string selectedKey = null; 20 | 21 | void Awake() 22 | { 23 | SL_NGUILabel textObject = ((SL_NGUILabel)target); 24 | if(textObject != null) 25 | { 26 | selectedKey = textObject.localizedKey; 27 | } 28 | } 29 | 30 | public override void OnInspectorGUI() 31 | { 32 | base.OnInspectorGUI(); 33 | 34 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.STRING); 35 | 36 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 37 | { 38 | SL_NGUILabel textObject = ((SL_NGUILabel)target); 39 | 40 | textObject.localizedKey = selectedKey; 41 | } 42 | } 43 | } 44 | } //namespace SmartLocalization.Editor 45 | #endif -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/NGUI/Editor/SL_NGUISpriteNameInspector.cs: -------------------------------------------------------------------------------- 1 | // SL_NGUISpriteNameInspector.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | //#define SMARTLOC_NGUI //<--- UNCOMMENT THIS FOR NGUI CLASSES 7 | 8 | #if SMARTLOC_NGUI 9 | namespace SmartLocalization.Editor 10 | { 11 | using UnityEngine; 12 | using UnityEditor; 13 | 14 | [CustomEditor(typeof(SL_NGUISpriteName))] 15 | public class SL_NGUISpriteNameInspector : Editor 16 | { 17 | private string selectedKey = null; 18 | 19 | void Awake() 20 | { 21 | SL_NGUISpriteName textObject = ((SL_NGUISpriteName)target); 22 | if(textObject != null) 23 | { 24 | selectedKey = textObject.localizedKey; 25 | } 26 | } 27 | 28 | public override void OnInspectorGUI() 29 | { 30 | base.OnInspectorGUI(); 31 | 32 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.STRING); 33 | 34 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 35 | { 36 | SL_NGUISpriteName spriteObject = ((SL_NGUISpriteName)target); 37 | spriteObject.localizedKey = selectedKey; 38 | } 39 | } 40 | } 41 | } //namespace SmartLocalization.Editor 42 | #endif 43 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/NGUI/Editor/SL_NGUISpriteAtlasInspector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // SL_NGUISpriteAtlasInspector.cs 3 | // 4 | // Written by Niklas Borglund and Jakob Hillerström 5 | // 6 | 7 | //#define SMARTLOC_NGUI //<--- UNCOMMENT THIS FOR NGUI CLASSES 8 | 9 | #if SMARTLOC_NGUI 10 | namespace SmartLocalization.Editor 11 | { 12 | using UnityEngine; 13 | using UnityEditor; 14 | 15 | [CustomEditor(typeof(SL_NGUISpriteAtlas))] 16 | public class SL_NGUISpriteAtlasInspector : Editor 17 | { 18 | private string selectedKey = null; 19 | 20 | void Awake() 21 | { 22 | SL_NGUISpriteAtlas textObject = ((SL_NGUISpriteAtlas)target); 23 | if(textObject != null) 24 | { 25 | selectedKey = textObject.localizedKey; 26 | } 27 | } 28 | 29 | public override void OnInspectorGUI() 30 | { 31 | base.OnInspectorGUI(); 32 | 33 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.GAME_OBJECT); 34 | 35 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 36 | { 37 | SL_NGUISpriteAtlas atlasObject = ((SL_NGUISpriteAtlas)target); 38 | 39 | atlasObject.localizedKey = selectedKey; 40 | } 41 | } 42 | } 43 | } //namespace SmartLocalization.Editor 44 | #endif 45 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/InspectorScripts/LocalizedGUITextInspector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedGUITextInspector.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | 11 | using UnityEngine; 12 | using UnityEditor; 13 | 14 | /// 15 | /// Editor class for a localized GUIText 16 | /// 17 | [CustomEditor(typeof(LocalizedGUIText))] 18 | public class LocalizedGUITextInspector : Editor 19 | { 20 | private string selectedKey = null; 21 | 22 | void Awake() 23 | { 24 | LocalizedGUIText textObject = ((LocalizedGUIText)target); 25 | if(textObject != null) 26 | { 27 | selectedKey = textObject.localizedKey; 28 | } 29 | } 30 | 31 | /// 32 | /// Override of the OnInspectorGUI method 33 | /// 34 | public override void OnInspectorGUI() 35 | { 36 | base.OnInspectorGUI(); 37 | 38 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.STRING); 39 | 40 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 41 | { 42 | LocalizedGUIText textObject = ((LocalizedGUIText)target); 43 | textObject.localizedKey = selectedKey; 44 | } 45 | } 46 | } 47 | } //namespace SmartLocalization.Editor 48 | -------------------------------------------------------------------------------- /docs/append-and-change-data-runtime.md: -------------------------------------------------------------------------------- 1 | # Appending and changing language data at runtime 2 | Waiting 6-10 working days for an approval to one of the app stores can be a tedious process - especially if you only want to do something like a minor update in a translation. 3 | 4 | One way to circumvent this and push translations directly into your published game would be to create a system to store updates on a server and then checking if there’s an update at a timed interval in your game. 5 | 6 | If you are a developer with a solution similar to the one described - we have the following methods that you can use to add/update your translations into the Smart Localization system. 7 | 8 | Both of these methods are available in the LanguageManager class and they only take in data in the .resx format. 9 | ```csharp 10 | void ChangeLanguageWithData(string languageDataInResX, string languageCode); 11 | ``` 12 | This method clears the currently loaded language from the system and loads a new one with the specified .resx data and the accompanied language code. 13 | ```csharp 14 | bool AppendLanguageWithTextData(string languageDataInResX); 15 | ``` 16 | Append Language with Text Data takes the data in the resx data and appends it to your currently loaded language. If there’s duplicate keys, the new one will be chosen. -------------------------------------------------------------------------------- /docs/how-to-use-watch-file.md: -------------------------------------------------------------------------------- 1 | # How to use the Watch File Feature 2 | Smart Localization PRO comes with a watch-file feature. This means that you can edit your .csv files in any csv compatible editor you'd like, and the game will instantly pick up your changes. This short tutorial will show you how to use it. 3 | 4 | First off, you'll need to create a csv file that corresponds with your translated language. Open up the main Smart Localization window and press export. Save your file at a location of your choice. 5 | ![alt](img/watch-file-1.png) 6 | 7 | After you've done this, navigate to a scene that uses the Smart Localization in your UnityProject and press play. I'll use the LoadAllLanguages.unity scene that comes with the unitypackage. 8 | 9 | ![alt](img/watch-file-2.png) 10 | 11 | Once the scene is started, open up the translation window for the selected language and press "watch file for changes". Navigate to your exported .csv file and select it using the popup that was brought up when you clicked the button. 12 | 13 | Finally, open up the file you've selected to be watched in a csv editor of your choice. I'll use notepad++ for this tutorial. 14 | 15 | Now when you change and save the values in the watched file, it will be instantly reflected in your game! 16 | 17 | ![alt](img/watch-file-3.png) 18 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/InspectorScripts/LocalizedGUITextureInspector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedGUITextureInspector.cs 3 | // 4 | // Written by Niklas Borglund and Jakob Hillerström 5 | // 6 | 7 | namespace SmartLocalization.Editor 8 | { 9 | 10 | using UnityEngine; 11 | using UnityEditor; 12 | 13 | /// 14 | /// Editor class for a localized GUITexture 15 | /// 16 | [CustomEditor(typeof(LocalizedGUITexture))] 17 | public class LocalizedGUITextureInspector : Editor 18 | { 19 | private string selectedKey = null; 20 | 21 | void Awake() 22 | { 23 | LocalizedGUITexture textObject = ((LocalizedGUITexture)target); 24 | if(textObject != null) 25 | { 26 | selectedKey = textObject.localizedKey; 27 | } 28 | } 29 | 30 | /// 31 | /// Override of the OnInspectorGUI method 32 | /// 33 | public override void OnInspectorGUI() 34 | { 35 | base.OnInspectorGUI(); 36 | 37 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.TEXTURE); 38 | 39 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 40 | { 41 | LocalizedGUITexture textObject = ((LocalizedGUITexture)target); 42 | textObject.localizedKey = selectedKey; 43 | } 44 | } 45 | } 46 | } //namespace SmartLocalization.Editor 47 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/InspectorScripts/LocalizedAudioSourceInspector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedAudioSourceInspector.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | 11 | using UnityEngine; 12 | using UnityEditor; 13 | 14 | /// 15 | /// Editor class for a localized Audio Source 16 | /// 17 | [CustomEditor(typeof(LocalizedAudioSource))] 18 | public class LocalizedAudioSourceInspector : Editor 19 | { 20 | private string selectedKey = null; 21 | 22 | void Awake() 23 | { 24 | LocalizedAudioSource audioObject = ((LocalizedAudioSource)target); 25 | if(audioObject != null) 26 | { 27 | selectedKey = audioObject.localizedKey; 28 | } 29 | } 30 | 31 | /// 32 | /// Override of the OnInspectorGUI method 33 | /// 34 | public override void OnInspectorGUI() 35 | { 36 | base.OnInspectorGUI(); 37 | 38 | selectedKey = LocalizedKeySelector.SelectKeyGUI(selectedKey, true, LocalizedObjectType.AUDIO); 39 | 40 | if(!Application.isPlaying && GUILayout.Button("Use Key", GUILayout.Width(70))) 41 | { 42 | LocalizedAudioSource audioObject = ((LocalizedAudioSource)target); 43 | audioObject.localizedKey = selectedKey; 44 | } 45 | } 46 | } 47 | } //namespace SmartLocalization.Editor 48 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LocalizedAudioSource.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedAudioSource.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization 9 | { 10 | using UnityEngine; 11 | using System.Collections; 12 | 13 | public class LocalizedAudioSource : MonoBehaviour 14 | { 15 | public string localizedKey = "INSERT_KEY_HERE"; 16 | public AudioClip audioClip; 17 | private AudioSource audioSource; 18 | 19 | void Start () 20 | { 21 | //Subscribe to the change language event 22 | LanguageManager languageManager = LanguageManager.Instance; 23 | languageManager.OnChangeLanguage += OnChangeLanguage; 24 | 25 | //Get the audio source 26 | audioSource = GetComponent(); 27 | 28 | //Run the method one first time 29 | OnChangeLanguage(languageManager); 30 | } 31 | 32 | void OnDestroy() 33 | { 34 | if(LanguageManager.HasInstance) 35 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 36 | } 37 | 38 | void OnChangeLanguage(LanguageManager languageManager) 39 | { 40 | //Initialize all your language specific variables here 41 | audioClip = languageManager.GetAudioClip(localizedKey); 42 | 43 | if(audioSource != null) 44 | { 45 | audioSource.clip = audioClip; 46 | } 47 | } 48 | } 49 | }//namespace SmartLocalization 50 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/LocalizedObjectMenuControl.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor 3 | { 4 | using UnityEngine; 5 | using System.Collections; 6 | using SmartLocalization.ReorderableList; 7 | using UnityEditor; 8 | using System; 9 | 10 | internal class LocalizedObjectMenuControl : ReorderableListControl 11 | { 12 | Action onItemRemoved = null; 13 | Action onItemAdded = null; 14 | 15 | public LocalizedObjectMenuControl(Action onItemRemoved, Action onItemAdded) : base(ReorderableListFlags.DisableContextMenu) 16 | { 17 | this.onItemRemoved = onItemRemoved; 18 | this.onItemAdded = onItemAdded; 19 | } 20 | 21 | public LocalizedObjectMenuControl() : base(ReorderableListFlags.HideAddButton | ReorderableListFlags.DisableContextMenu){} 22 | 23 | public void ClearEvents() 24 | { 25 | onItemRemoved = null; 26 | } 27 | 28 | protected override void OnItemInserted(ItemInsertedEventArgs args) 29 | { 30 | base.OnItemInserted(args); 31 | 32 | if(onItemAdded != null) 33 | { 34 | onItemAdded(args.itemIndex); 35 | } 36 | } 37 | 38 | protected override void OnItemRemoving(ItemRemovingEventArgs args) 39 | { 40 | base.OnItemRemoving(args); 41 | 42 | if(onItemRemoved != null) 43 | { 44 | onItemRemoved(args.itemIndex); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/NGUI/SL_NGUISpriteAtlas.cs: -------------------------------------------------------------------------------- 1 | // SL_NGUISpriteAtlas.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | //#define SMARTLOC_NGUI //<--- UNCOMMENT THIS FOR NGUI CLASSES 7 | 8 | #if SMARTLOC_NGUI 9 | using UnityEngine; 10 | using System.Collections; 11 | using SmartLocalization; 12 | 13 | public class SL_NGUISpriteAtlas : MonoBehaviour 14 | { 15 | public string localizedKey = "INSERT KEY HERE"; 16 | UISprite uiSprite = null; 17 | 18 | void Awake() 19 | { 20 | uiSprite = GetComponent(); 21 | } 22 | 23 | void Start () 24 | { 25 | //Subscribe to the change language event 26 | LanguageManager languageManager = LanguageManager.Instance; 27 | languageManager.OnChangeLanguage += OnChangeLanguage; 28 | 29 | OnChangeLanguage(languageManager); 30 | } 31 | 32 | void OnDestroy() 33 | { 34 | if(LanguageManager.HasInstance) 35 | { 36 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 37 | } 38 | } 39 | 40 | void OnChangeLanguage(LanguageManager languageManager) 41 | { 42 | GameObject atlasPrefab = languageManager.GetPrefab(localizedKey); 43 | if(atlasPrefab != null) 44 | { 45 | UIAtlas atlasObject = atlasPrefab.GetComponent(); 46 | if(atlasObject == null) 47 | { 48 | if(languageManager.VerboseLogging) 49 | { 50 | Debug.LogError("No UIAtlas was found with key:" + localizedKey); 51 | } 52 | } 53 | else 54 | { 55 | uiSprite.atlas = atlasObject; 56 | } 57 | } 58 | } 59 | } 60 | #endif -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/RuntimeLocalizedAssetLoader.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace SmartLocalization{ 4 | internal class RuntimeLocalizedAssetLoader : ILocalizedAssetLoader 5 | { 6 | static readonly System.Type GameObjectType = typeof(GameObject); 7 | static readonly System.Type AudioClipType = typeof(AudioClip); 8 | static readonly System.Type TextureType = typeof(Texture); 9 | static readonly System.Type TextAssetType = typeof(TextAsset); 10 | static readonly System.Type FontType = typeof(Font); 11 | 12 | public T LoadAsset(string assetKey, string languageCode) where T : UnityEngine.Object 13 | { 14 | var loadedObject = Resources.Load(GetAssetFolderPath(typeof(T), languageCode) + "/" + assetKey); 15 | if(loadedObject != null){ 16 | return (T)loadedObject; 17 | } 18 | return default(T); 19 | } 20 | 21 | string GetAssetFolderPath(System.Type assetType, string languageCode) 22 | { 23 | if(assetType == GameObjectType) 24 | { 25 | return LanguageRuntimeData.PrefabsFolderPath(languageCode); 26 | } 27 | else if(assetType == AudioClipType) 28 | { 29 | return LanguageRuntimeData.AudioFilesFolderPath(languageCode); 30 | } 31 | else if(assetType == TextureType) 32 | { 33 | return LanguageRuntimeData.TexturesFolderPath(languageCode); 34 | } 35 | else if(assetType == TextAssetType) 36 | { 37 | return LanguageRuntimeData.TextAssetsFolderPath(languageCode); 38 | } 39 | else if(assetType == FontType) 40 | { 41 | return LanguageRuntimeData.FontsFolderPath(languageCode); 42 | } 43 | 44 | return string.Empty; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Rotorz Limited 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/CreateLanguageMenuControl.cs: -------------------------------------------------------------------------------- 1 | namespace SmartLocalization.Editor 2 | { 3 | using UnityEngine; 4 | using System.Collections; 5 | using SmartLocalization.ReorderableList; 6 | using UnityEditor; 7 | 8 | internal class CreateLanguageMenuControl : ReorderableListControl 9 | { 10 | static GUIContent commandCreate = new GUIContent("Create"); 11 | static GUIContent commandImport = new GUIContent("Import from CSV"); 12 | 13 | public CreateLanguageMenuControl() : base(ReorderableListFlags.HideAddButton | ReorderableListFlags.DisableContextMenu){} 14 | 15 | 16 | //Nothing in here is used ATM, the context menu is disabled 17 | protected override void AddItemsToMenu(GenericMenu menu, int itemIndex, IReorderableListAdaptor adaptor) 18 | { 19 | menu.AddItem(commandCreate, false, defaultContextHandler, commandCreate); 20 | menu.AddItem(commandImport, false, defaultContextHandler, commandImport); 21 | } 22 | 23 | protected override bool HandleCommand(string commandName, int itemIndex,IReorderableListAdaptor adaptor) 24 | { 25 | CreateLanguageListAdaptor smartAdaptor = adaptor as CreateLanguageListAdaptor; 26 | 27 | if(smartAdaptor == null) 28 | { 29 | return false; 30 | } 31 | 32 | switch (commandName) 33 | { 34 | case "Create": 35 | OnCreateClick(smartAdaptor.GetCultureInfo(itemIndex)); 36 | return true; 37 | case "Import from CSV": 38 | OnImportClick(smartAdaptor.GetCultureInfo(itemIndex)); 39 | return true; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | void OnCreateClick(SmartCultureInfo info) 46 | { 47 | 48 | } 49 | 50 | void OnImportClick(SmartCultureInfo info) 51 | { 52 | 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/Utility/SmartCultureInfoEx.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Xml.Serialization; 4 | using System.IO; 5 | using System.Xml; 6 | using System.Text; 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | public static class SmartCultureInfoEx 11 | { 12 | /// 13 | /// Extension method to deserialize a SmartCultureInfoCollection 14 | /// 15 | /// The full path where the serialized SmartCultureInfoCollection can be found 16 | /// The deserialized SmartCultureInfoCollection 17 | public static SmartCultureInfoCollection Deserialize(string fullPath) 18 | { 19 | var serializer = new XmlSerializer(typeof(SmartCultureInfoCollection)); 20 | SmartCultureInfoCollection deserializedCulture = null; 21 | using(var stream = new FileStream(fullPath, FileMode.Open)) 22 | { 23 | deserializedCulture = serializer.Deserialize(stream) as SmartCultureInfoCollection; 24 | } 25 | 26 | return deserializedCulture; 27 | } 28 | 29 | /// 30 | /// Extension method to serialize a SmartCultureInfoCollection to a given file path using XMLSerializer. 31 | /// 32 | /// The collection to serialize 33 | /// The full path where the collection will be saved 34 | public static void Serialize(this SmartCultureInfoCollection info, string fullPath) 35 | { 36 | var serializer = new XmlSerializer(typeof(SmartCultureInfoCollection)); 37 | 38 | using(XmlTextWriter writer = new XmlTextWriter(fullPath,Encoding.UTF8)) 39 | { 40 | writer.Formatting = Formatting.Indented; 41 | serializer.Serialize(writer, info); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Assets/Tests/Editor/SmartCultureInfoTests.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor 3 | { 4 | using NUnit.Framework; 5 | 6 | [TestFixture] 7 | public class SmartCultureInfoTests 8 | { 9 | [Test] 10 | public void AddCultureToCollection_Success() 11 | { 12 | SmartCultureInfoCollection testCollection = new SmartCultureInfoCollection(); 13 | testCollection.AddCultureInfo(new SmartCultureInfo("te-ST", "TEST", "TEEEST", false)); 14 | 15 | Assert.AreEqual(1, testCollection.cultureInfos.Count); 16 | } 17 | 18 | [Test] 19 | public void AddCultureToCollection_Failure() 20 | { 21 | SmartCultureInfoCollection testCollection = new SmartCultureInfoCollection(); 22 | testCollection.AddCultureInfo(null); 23 | 24 | Assert.AreEqual(0, testCollection.cultureInfos.Count); 25 | } 26 | 27 | [Test] 28 | public void FindCultureInCollection_Success() 29 | { 30 | SmartCultureInfoCollection testCollection = new SmartCultureInfoCollection(); 31 | SmartCultureInfo cultureInfoToFind = new SmartCultureInfo("te-ST", "TEST", "TEEEST", false); 32 | testCollection.AddCultureInfo(cultureInfoToFind); 33 | 34 | SmartCultureInfo foundCultureInfo = testCollection.FindCulture(cultureInfoToFind); 35 | Assert.AreEqual(cultureInfoToFind, foundCultureInfo); 36 | } 37 | 38 | [Test] 39 | public void FindCulture_Success() 40 | { 41 | var testCollection = new SmartCultureInfoCollection(); 42 | var chineseOne = new SmartCultureInfo("zh-CHT", "Chinese", "Chinese", false); 43 | var chineseTwo = new SmartCultureInfo("zh-CHS", "Chinese", "Chinese", false); 44 | 45 | testCollection.AddCultureInfo(chineseTwo); 46 | testCollection.AddCultureInfo(chineseOne); 47 | 48 | var foundCulture = testCollection.FindCulture(chineseTwo); 49 | 50 | Assert.AreEqual(foundCulture, chineseTwo); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/Utility/EditorThreadQueuer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System; 5 | using UnityEditor; 6 | 7 | namespace SmartLocalization.Editor 8 | { 9 | public class EditorThreadQueuer 10 | { 11 | static EditorThreadQueuer _instance = null; 12 | 13 | static EditorThreadQueuer Instance 14 | { 15 | get 16 | { 17 | if(_instance == null) 18 | { 19 | _instance = new EditorThreadQueuer(); 20 | } 21 | 22 | return _instance; 23 | } 24 | } 25 | 26 | IList queuedActions = new List(); 27 | IList runningActions = new List(); 28 | bool isRegisteredToUpdate = false; 29 | object listLock = new object(); 30 | 31 | void RegisterToEditorUpdate() 32 | { 33 | if(!isRegisteredToUpdate) 34 | { 35 | EditorApplication.update += Update; 36 | isRegisteredToUpdate = true; 37 | } 38 | } 39 | 40 | void UnregisterToEditorUpdate() 41 | { 42 | if(isRegisteredToUpdate) 43 | { 44 | EditorApplication.update -= Update; 45 | isRegisteredToUpdate = false; 46 | } 47 | } 48 | 49 | void RunOnMainThread(Action action) 50 | { 51 | lock(listLock) 52 | { 53 | queuedActions.Add(action); 54 | RegisterToEditorUpdate(); 55 | } 56 | } 57 | 58 | void Update() 59 | { 60 | lock(listLock) 61 | { 62 | for(int i = 0; i < queuedActions.Count;++i) 63 | { 64 | runningActions.Add(queuedActions[i]); 65 | } 66 | queuedActions.Clear(); 67 | UnregisterToEditorUpdate(); 68 | } 69 | 70 | for(int i = 0; i < runningActions.Count;++i) 71 | { 72 | try 73 | { 74 | runningActions[i](); 75 | } 76 | catch(System.Exception ex) 77 | { 78 | Debug.LogError(ex.Message); 79 | } 80 | } 81 | runningActions.Clear(); 82 | } 83 | 84 | public static void QueueOnMainThread(Action action) 85 | { 86 | Instance.RunOnMainThread(action); 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Editor/Internal/GUIHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2013 Rotorz Limited. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | using UnityEngine; 6 | using UnityEditor; 7 | 8 | using System; 9 | using System.Reflection; 10 | 11 | namespace SmartLocalization.ReorderableList.Internal { 12 | 13 | /// 14 | /// Utility functions to assist with GUIs. 15 | /// 16 | internal static class GUIHelper { 17 | 18 | static GUIHelper() { 19 | var tyGUIClip = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip"); 20 | if (tyGUIClip != null) { 21 | var piVisibleRect = tyGUIClip.GetProperty("visibleRect", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 22 | if (piVisibleRect != null) { 23 | var getMethod = piVisibleRect.GetGetMethod(true) ?? piVisibleRect.GetGetMethod(false); 24 | VisibleRect = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); 25 | } 26 | } 27 | 28 | var miFocusTextInControl = typeof(EditorGUI).GetMethod("FocusTextInControl", BindingFlags.Static | BindingFlags.Public); 29 | if (miFocusTextInControl == null) 30 | miFocusTextInControl = typeof(GUI).GetMethod("FocusControl", BindingFlags.Static | BindingFlags.Public); 31 | 32 | FocusTextInControl = (Action)Delegate.CreateDelegate(typeof(Action), miFocusTextInControl); 33 | } 34 | 35 | /// 36 | /// Gets visible rectangle within GUI. 37 | /// 38 | /// 39 | /// VisibleRect = TopmostRect + scrollViewOffsets 40 | /// 41 | public static Func VisibleRect; 42 | 43 | /// 44 | /// Focus control and text editor where applicable. 45 | /// 46 | public static Action FocusTextInControl; 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Editor/ReorderableListFlags.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2013 Rotorz Limited. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | using System; 6 | 7 | namespace SmartLocalization.ReorderableList { 8 | 9 | /// 10 | /// Additional flags which can be passed into reorderable list field. 11 | /// 12 | /// 13 | /// Multiple flags can be specified if desired: 14 | /// 18 | /// 19 | [Flags] 20 | public enum ReorderableListFlags { 21 | /// 22 | /// Hide grab handles and disable reordering of list items. 23 | /// 24 | DisableReordering = 0x01, 25 | /// 26 | /// Hide add button at base of control. 27 | /// 28 | HideAddButton = 0x02, 29 | /// 30 | /// Hide remove buttons from list items. 31 | /// 32 | HideRemoveButtons = 0x04, 33 | /// 34 | /// Do not display context menu upon right-clicking grab handle. 35 | /// 36 | DisableContextMenu = 0x08, 37 | /// 38 | /// Hide "Duplicate" option from context menu. 39 | /// 40 | DisableDuplicateCommand = 0x10, 41 | /// 42 | /// Do not automatically focus first control of newly added items. 43 | /// 44 | DisableAutoFocus = 0x20, 45 | /// 46 | /// Show zero-based index of array elements. 47 | /// 48 | ShowIndices = 0x40, 49 | /// 50 | /// Do not attempt to clip items which are out of view. 51 | /// 52 | /// 53 | /// Clipping helps to boost performance, though may lead to issues on 54 | /// some interfaces. 55 | /// 56 | DisableClipping = 0x80, 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LocalizationSystem/SmartCultureInfoCollectionDeserializer.cs: -------------------------------------------------------------------------------- 1 | // SmartCultureInfoCollectionDeserializer.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization 7 | { 8 | using UnityEngine; 9 | using System.Collections; 10 | using System.IO; 11 | using System.Xml; 12 | 13 | internal static class SmartCultureInfoCollectionDeserializer 14 | { 15 | public static SmartCultureInfoCollection Deserialize(TextAsset xmlFile) 16 | { 17 | if(xmlFile == null) 18 | { 19 | return null; 20 | } 21 | 22 | SmartCultureInfoCollection newCollection = new SmartCultureInfoCollection(); 23 | using(StringReader stringReader = new StringReader(xmlFile.text)) 24 | { 25 | using(XmlReader reader = XmlReader.Create(stringReader)) 26 | { 27 | ReadElements(reader, newCollection); 28 | } 29 | } 30 | return newCollection; 31 | } 32 | 33 | static void ReadElements(XmlReader reader, SmartCultureInfoCollection newCollection) 34 | { 35 | while (reader.Read()) 36 | { 37 | if(reader.NodeType == XmlNodeType.Element && reader.Name == "CultureInfo") 38 | { 39 | ReadData(reader, newCollection); 40 | } 41 | } 42 | } 43 | 44 | static void ReadData(XmlReader reader, SmartCultureInfoCollection newCollection) 45 | { 46 | string languageCode = string.Empty; 47 | string englishName = string.Empty; 48 | string nativeName = string.Empty; 49 | bool isRightToLeft = false; 50 | 51 | //Read the child nodes 52 | if (reader.ReadToDescendant("languageCode")) 53 | { 54 | languageCode = reader.ReadElementContentAsString(); 55 | } 56 | 57 | if(reader.ReadToNextSibling("englishName")) 58 | { 59 | englishName = reader.ReadElementContentAsString(); 60 | } 61 | 62 | if(reader.ReadToNextSibling("nativeName")) 63 | { 64 | nativeName = reader.ReadElementContentAsString(); 65 | } 66 | 67 | if(reader.ReadToNextSibling("isRightToLeft")) 68 | { 69 | isRightToLeft = reader.ReadElementContentAsBoolean(); 70 | } 71 | 72 | 73 | newCollection.AddCultureInfo(new SmartCultureInfo(languageCode, englishName, nativeName, isRightToLeft)); 74 | } 75 | 76 | } 77 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/Utility/LocalizationWindowUtility.cs: -------------------------------------------------------------------------------- 1 | // 2 | // EditorWindowUtility.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | 11 | using UnityEngine; 12 | using UnityEditor; 13 | 14 | /// The different search types for a language 15 | public enum LanguageSearchType 16 | { 17 | /// Search by key 18 | KEY, 19 | /// Search by value 20 | VALUE, 21 | } 22 | 23 | /// The different sort types for a language 24 | public enum LanguageSortType 25 | { 26 | /// Sort by keys 27 | KEY, 28 | /// Sort by values 29 | VALUE, 30 | /// Sort by type 31 | TYPE, 32 | } 33 | 34 | /// 35 | /// A Utility class with helper methods for editor windows using Smart Localization 36 | /// 37 | public static class LocalizationWindowUtility 38 | { 39 | /// 40 | /// Returns true if the window should show, returns false if not. 41 | /// If false, it will draw an editor label that says the window is unavailable 42 | /// 43 | public static bool ShouldShowWindow(bool isAvailableInPlayMode = false) 44 | { 45 | if(Application.isPlaying && !isAvailableInPlayMode) 46 | { 47 | GUILayout.Label ("This Smart Localization Window is not available in play mode", EditorStyles.boldLabel); 48 | if(LanguageManager.HasInstance) 49 | { 50 | if(GUILayout.Button("Go to translation window")) 51 | { 52 | TranslateLanguageWindow.ShowWindow(LanguageManager.Instance.GetCultureInfo(LanguageManager.Instance.CurrentlyLoadedCulture.languageCode), null); 53 | } 54 | } 55 | return false; 56 | } 57 | else if(!LocalizationWorkspace.Exists()) 58 | { 59 | GUILayout.Label ("There is no localization workspace available in this project", EditorStyles.boldLabel); 60 | if(GUILayout.Button("Create localization workspace")) 61 | { 62 | if(LocalizationWorkspace.Create()) 63 | { 64 | return true; 65 | } 66 | } 67 | 68 | return false; 69 | } 70 | else 71 | { 72 | return true; 73 | } 74 | } 75 | } 76 | } //namespace SmartLocalization.Editor -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/FileOperations/CustomResxImporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // CustomResxImporter.cs 3 | // 4 | // Creates or rewrites a .txt file for each .resx file in a subfolder called 5 | // GeneratedAssets whenever the .resx changes 6 | // 7 | // Written by Niklas Borglund and Jakob Hillerström 8 | // 9 | 10 | namespace SmartLocalization.Editor 11 | { 12 | 13 | using UnityEditor; 14 | using System.IO; 15 | 16 | /// 17 | /// Resx importer that detects if a resx file changed or was added to the project 18 | /// 19 | public class CustomResxImporter : AssetPostprocessor 20 | { 21 | /// 22 | /// Checks .resx files and converts them into text assets that can be used at runtime 23 | /// 24 | public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) 25 | { 26 | //Only use this if there's a localization system created 27 | if(!LocalizationWorkspace.Exists()) 28 | { 29 | return; 30 | } 31 | 32 | foreach (string asset in importedAssets) 33 | { 34 | if (asset.EndsWith(LocalizationWorkspace.resXFileEnding)) 35 | { 36 | string newFileName = LocalizationWorkspace.ResourcesFolderFilePath() + "/" + Path.GetFileNameWithoutExtension(asset) + LocalizationWorkspace.txtFileEnding; 37 | 38 | if(!DirectoryUtility.CheckAndCreate(LocalizationWorkspace.ResourcesFolderFilePath())) 39 | { 40 | return; 41 | } 42 | 43 | //Delete the file if it already exists 44 | if(FileUtility.Exists(newFileName)) 45 | { 46 | FileUtility.Delete(newFileName); 47 | } 48 | 49 | string fileData = ""; 50 | using(StreamReader reader = new StreamReader(asset)) 51 | { 52 | fileData = reader.ReadToEnd(); 53 | } 54 | 55 | 56 | FileUtility.WriteToFile(newFileName, fileData); 57 | 58 | SmartCultureInfoCollection allCultures = SmartCultureInfoEx.Deserialize(LocalizationWorkspace.CultureInfoCollectionFilePath()); 59 | LanguageHandlerEditor.CheckAndSaveAvailableLanguages(allCultures); 60 | 61 | AssetDatabase.Refresh(ImportAssetOptions.Default); 62 | } 63 | } 64 | } 65 | 66 | } 67 | } //namespace SmartLocalization.Editor 68 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LanguageRuntimeData.cs: -------------------------------------------------------------------------------- 1 | //LanguageRuntimeData.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | namespace SmartLocalization 6 | { 7 | /// 8 | /// An information class containing runtime data. 9 | /// 10 | public static class LanguageRuntimeData 11 | { 12 | //todo:: load this from a file generated by the editor classes 13 | static string AvailableCulturesFileName = "AvailableCultures"; 14 | static string rootLanguageName = "Language"; 15 | 16 | static string AudioFileFolderName = "AudioFiles"; 17 | static string TexturesFolderName = "Textures"; 18 | static string PrefabsFolderName = "Prefabs"; 19 | static string TextAssetsFolderName = "TextAssets"; 20 | static string FontsFolderName = "Fonts"; 21 | 22 | 23 | #region Lookups & Paths 24 | /// 25 | /// Gets the relative language file path for a certain language. Mostly used with Resources.Load as a TextAsset. 26 | /// 27 | /// The language code of the culture 28 | /// The relative language file path 29 | public static string LanguageFilePath(string languageCode) 30 | { 31 | return rootLanguageName + "." + languageCode; 32 | } 33 | 34 | /// 35 | /// Gets the relative file path for the AvailableCultures xml. Use with Resources.Load as a TextAsset. 36 | /// 37 | /// The AvailableCultures.xml relative file path 38 | public static string AvailableCulturesFilePath() 39 | { 40 | return AvailableCulturesFileName; 41 | } 42 | 43 | /// 44 | /// Gets the relative folder path for the audio files by a specific language 45 | /// 46 | public static string AudioFilesFolderPath(string languageCode) 47 | { 48 | return languageCode + "/" + AudioFileFolderName; 49 | } 50 | 51 | /// 52 | /// Gets the relative folder path for the texture files by a specific language 53 | /// 54 | public static string TexturesFolderPath(string languageCode) 55 | { 56 | return languageCode + "/" + TexturesFolderName; 57 | } 58 | 59 | public static string TextAssetsFolderPath(string languageCode) 60 | { 61 | return languageCode + "/" + TextAssetsFolderName; 62 | } 63 | public static string FontsFolderPath(string languageCode) 64 | { 65 | return languageCode + "/" + FontsFolderName; 66 | } 67 | 68 | public static string PrefabsFolderPath(string languageCode) 69 | { 70 | return languageCode + "/" + PrefabsFolderName; 71 | } 72 | 73 | #endregion 74 | } 75 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/FileSystem/CSVExporter.cs: -------------------------------------------------------------------------------- 1 | // CSVExporter.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.IO; 8 | 9 | namespace SmartLocalization.Editor 10 | { 11 | /// 12 | /// The delimiter type for CSV 13 | /// 14 | [System.Obsolete("use CSVParser.Delimiter")] 15 | public enum CSVDelimiter 16 | { 17 | /// COMMA 18 | COMMA, 19 | /// SEMI_COLON 20 | SEMI_COLON, 21 | /// TAB 22 | TAB, 23 | /// VERTICAL_BAR 24 | VERTICAL_BAR, 25 | /// CARET 26 | CARET, 27 | } 28 | 29 | /// 30 | /// A CSV Exporter 31 | /// 32 | public static class CSVExporter 33 | { 34 | /// 35 | /// Gets the actual delimiter char based on the CSVDelimiter type 36 | /// 37 | /// The delimiter type 38 | /// the delimiter 39 | [System.Obsolete("use CSVParser.GetDelimiter")] 40 | public static char GetDelimiter(CSVDelimiter delimiter) 41 | { 42 | return CSVParser.GetDelimiter((CSVParser.Delimiter)delimiter); 43 | } 44 | 45 | /// 46 | /// Write the csv to file 47 | /// 48 | /// The destination path 49 | /// The delimiter to separate values with 50 | /// The Values 51 | [System.Obsolete("use CSVParser.Write")] 52 | public static void WriteCSV(string path, char delimiter, List> input) 53 | { 54 | CSVParser.Write(path, delimiter, input); 55 | } 56 | 57 | /// 58 | /// Write a combined CSV to File 59 | /// 60 | /// The destination path 61 | /// The delimiter to separate values with 62 | [System.Obsolete("use CSVParser.Write")] 63 | public static void WriteCSV(string path, char delimiter, List keys, Dictionary> languages) 64 | { 65 | CSVParser.Write(path, delimiter, keys, languages); 66 | } 67 | 68 | /// 69 | /// Read a csv file 70 | /// 71 | /// The path to the file 72 | /// The delimiter used in the file 73 | /// The parsed csv values 74 | [System.Obsolete("use CSVParser.Read")] 75 | public static List> ReadCSV(string path, char delimiter) 76 | { 77 | return CSVParser.Read(path, delimiter); 78 | } 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/SmartCultureInfoMenuControl.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor 3 | { 4 | using UnityEngine; 5 | using System.Collections; 6 | using SmartLocalization.ReorderableList; 7 | using UnityEditor; 8 | 9 | internal class SmartCultureInfoMenuControl : ReorderableListControl 10 | { 11 | static GUIContent commandTranslate = new GUIContent("Translate"); 12 | static GUIContent commandUpdate = new GUIContent("Update"); 13 | static GUIContent commandExport = new GUIContent("Export"); 14 | 15 | public SmartCultureInfoMenuControl() : base(ReorderableListFlags.HideAddButton | ReorderableListFlags.DisableContextMenu){} 16 | 17 | //Nothing in here is used ATM, the context menu is disabled 18 | protected override void AddItemsToMenu(GenericMenu menu, int itemIndex, IReorderableListAdaptor adaptor) 19 | { 20 | menu.AddItem(commandTranslate, false, defaultContextHandler, commandTranslate); 21 | menu.AddItem(commandUpdate, false, defaultContextHandler, commandUpdate); 22 | menu.AddItem(commandExport, false, defaultContextHandler, commandExport); 23 | } 24 | 25 | protected override bool HandleCommand(string commandName, int itemIndex,IReorderableListAdaptor adaptor) 26 | { 27 | SmartCultureInfoListAdaptor smartAdaptor = adaptor as SmartCultureInfoListAdaptor; 28 | 29 | if(smartAdaptor == null) 30 | return false; 31 | 32 | switch (commandName) 33 | { 34 | case "Translate": 35 | OnTranslateClick(smartAdaptor.GetCultureInfo(itemIndex)); 36 | return true; 37 | case "Update": 38 | OnUpdateClick(smartAdaptor.GetCultureInfo(itemIndex)); 39 | return true; 40 | case "Export": 41 | OnExportClick(smartAdaptor.GetCultureInfo(itemIndex)); 42 | return true; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | protected override void OnItemInserted(ItemInsertedEventArgs args) 49 | { 50 | //base.OnItemInserted(args); 51 | } 52 | 53 | protected override void OnItemRemoving(ItemRemovingEventArgs args) 54 | { 55 | SmartCultureInfoListAdaptor smartAdaptor = args.adaptor as SmartCultureInfoListAdaptor; 56 | 57 | if(smartAdaptor == null) 58 | { 59 | return; 60 | } 61 | 62 | SmartCultureInfo info = smartAdaptor.GetCultureInfo(args.itemIndex); 63 | 64 | if(EditorUtility.DisplayDialog("Delete " + info.englishName + "?", 65 | "Are you sure you want to delete " + info.englishName + " and all of its content from the project? You cannot undo this action.", 66 | "Yes, delete it.", "Cancel")) 67 | { 68 | LanguageHandlerEditor.DeleteLanguage(info); 69 | base.OnItemRemoving(args); 70 | } 71 | else 72 | { 73 | args.Cancel = true; 74 | } 75 | } 76 | 77 | public void OnTranslateClick(SmartCultureInfo info) 78 | { 79 | Debug.Log("Translate: " + info.englishName); 80 | } 81 | 82 | public void OnUpdateClick(SmartCultureInfo info) 83 | { 84 | Debug.Log("Update: " + info.englishName); 85 | } 86 | 87 | public void OnExportClick(SmartCultureInfo info) 88 | { 89 | Debug.Log("Export: " + info.englishName); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Examples/LoadAllLanguages.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LoadAllLanguages.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization 9 | { 10 | using UnityEngine; 11 | using System.Collections.Generic; 12 | 13 | public class LoadAllLanguages : MonoBehaviour 14 | { 15 | private List currentLanguageKeys; 16 | private List availableLanguages; 17 | private LanguageManager languageManager; 18 | private Vector2 valuesScrollPosition = Vector2.zero; 19 | private Vector2 languagesScrollPosition = Vector2.zero; 20 | 21 | void Start () 22 | { 23 | languageManager = LanguageManager.Instance; 24 | 25 | SmartCultureInfo deviceCulture = languageManager.GetDeviceCultureIfSupported(); 26 | if(deviceCulture != null) 27 | { 28 | languageManager.ChangeLanguage(deviceCulture); 29 | } 30 | else 31 | { 32 | Debug.Log("The device language is not available in the current application. Loading default."); 33 | } 34 | 35 | if(languageManager.NumberOfSupportedLanguages > 0) 36 | { 37 | currentLanguageKeys = languageManager.GetAllKeys(); 38 | availableLanguages = languageManager.GetSupportedLanguages(); 39 | } 40 | else 41 | { 42 | Debug.LogError("No languages are created!, Open the Smart Localization plugin at Window->Smart Localization and create your language!"); 43 | } 44 | 45 | LanguageManager.Instance.OnChangeLanguage += OnLanguageChanged; 46 | } 47 | 48 | void OnDestroy() 49 | { 50 | if(LanguageManager.HasInstance) 51 | { 52 | LanguageManager.Instance.OnChangeLanguage -= OnLanguageChanged; 53 | } 54 | } 55 | 56 | void OnLanguageChanged(LanguageManager languageManager) 57 | { 58 | currentLanguageKeys = languageManager.GetAllKeys(); 59 | } 60 | 61 | void OnGUI() 62 | { 63 | if(languageManager.NumberOfSupportedLanguages > 0) 64 | { 65 | if(languageManager.CurrentlyLoadedCulture != null) 66 | { 67 | GUILayout.Label("Current Language:" + languageManager.CurrentlyLoadedCulture.ToString()); 68 | } 69 | 70 | GUILayout.BeginHorizontal(); 71 | GUILayout.Label("Keys:", GUILayout.Width(460)); 72 | GUILayout.Label("Values:", GUILayout.Width(460)); 73 | GUILayout.EndHorizontal(); 74 | 75 | valuesScrollPosition = GUILayout.BeginScrollView(valuesScrollPosition); 76 | foreach(var languageKey in currentLanguageKeys) 77 | { 78 | GUILayout.BeginHorizontal(); 79 | GUILayout.Label(languageKey, GUILayout.Width(460)); 80 | GUILayout.Label(languageManager.GetTextValue(languageKey), GUILayout.Width(460)); 81 | GUILayout.EndHorizontal(); 82 | } 83 | GUILayout.EndScrollView(); 84 | 85 | languagesScrollPosition = GUILayout.BeginScrollView (languagesScrollPosition); 86 | foreach(SmartCultureInfo language in availableLanguages) 87 | { 88 | if(GUILayout.Button(language.nativeName, GUILayout.Width(960))) 89 | { 90 | languageManager.ChangeLanguage(language); 91 | } 92 | } 93 | 94 | GUILayout.EndScrollView(); 95 | } 96 | } 97 | } 98 | }//namespace SmartLocalization 99 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/Utility/LanguageDictionaryHelper.cs: -------------------------------------------------------------------------------- 1 | //LanguageDictionaryHelper.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | namespace SmartLocalization.Editor 6 | { 7 | using System.Collections.Generic; 8 | using UnityEngine; 9 | /// 10 | /// Helper methods for language values using parsed & raw language dictionaries 11 | /// 12 | public static class LanguageDictionaryHelper 13 | { 14 | /// 15 | /// Adds a new key to a dictionary and does not stop until a unique key is found 16 | /// 17 | public static string AddNewKeyPersistent(Dictionary languageDictionary, string desiredKey, string newValue) 18 | { 19 | LocalizedObjectType keyType = LocalizedObject.GetLocalizedObjectType(desiredKey); 20 | //Clean the key from unwanted type identifiers 21 | //Nothing will happen to a regular string, since a string doesn't have an identifier 22 | desiredKey = LocalizedObject.GetCleanKey(desiredKey, keyType); 23 | 24 | bool newKeyFound = false; 25 | int count = 0; 26 | string newKeyName = desiredKey; 27 | while(!newKeyFound) 28 | { 29 | if(!languageDictionary.ContainsKey(newKeyName)) 30 | { 31 | bool duplicateFound = false; 32 | foreach(KeyValuePair stringPair in languageDictionary) 33 | { 34 | string cleanKey = LocalizedObject.GetCleanKey(stringPair.Key); 35 | if(cleanKey == newKeyName) 36 | { 37 | duplicateFound = true; 38 | break; 39 | } 40 | } 41 | if(!duplicateFound) 42 | { 43 | languageDictionary.Add(LocalizedObject.GetFullKey(newKeyName, keyType), newValue); 44 | newKeyFound = true; 45 | return desiredKey; 46 | } 47 | else 48 | { 49 | newKeyName = desiredKey + count; 50 | count++; 51 | } 52 | } 53 | else 54 | { 55 | newKeyName = desiredKey + count; 56 | count++; 57 | } 58 | } 59 | Debug.Log("Duplicate keys in dictionary was found! - renaming key:" + desiredKey + " to:" + newKeyName); 60 | return newKeyName; 61 | } 62 | /// 63 | /// Adds a new key to a dictionary and does not stop until a unique key is found 64 | /// 65 | public static string AddNewKeyPersistent(Dictionary languageDictionary, string desiredKey, LocalizedObject newValue) 66 | { 67 | if(!languageDictionary.ContainsKey(desiredKey)) 68 | { 69 | languageDictionary.Add(desiredKey, newValue); 70 | return desiredKey; 71 | } 72 | else 73 | { 74 | bool newKeyFound = false; 75 | int count = 0; 76 | while(!newKeyFound) 77 | { 78 | if(!languageDictionary.ContainsKey(desiredKey + count)) 79 | { 80 | languageDictionary.Add(desiredKey + count, newValue); 81 | newKeyFound = true; 82 | } 83 | else 84 | { 85 | count++; 86 | } 87 | } 88 | Debug.LogWarning("Duplicate keys in dictionary was found! - renaming key:" + desiredKey + " to:" + (desiredKey + count)); 89 | return (desiredKey + count); 90 | } 91 | } 92 | } 93 | }//namespace SmartLocalization.Editor -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/FileSystem/DirectoryUtility.cs: -------------------------------------------------------------------------------- 1 | //DirectoryUtility.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | using UnityEngine; 6 | using System.IO; 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | /// 11 | /// Utility class for handling Directories 12 | /// 13 | public class DirectoryUtility 14 | { 15 | /// 16 | /// Checks if a directory exists 17 | /// 18 | /// The full path to the directory 19 | /// If the directory exists 20 | public static bool Exists(string fullPath) 21 | { 22 | return Directory.Exists(fullPath); 23 | } 24 | 25 | /// Checks if a directory exists. Appends the relativePath to Application.dataPath 26 | public static bool ExistsRelative(string relativePath) 27 | { 28 | return Exists(Application.dataPath + relativePath); 29 | } 30 | 31 | /// Returns the names of files(including their paths) in the directory 32 | public static string[] GetFiles(string fullPath) 33 | { 34 | return Directory.GetFiles(fullPath); 35 | } 36 | 37 | /// Appends the relativePath to Application.dataPath 38 | public static string[] GetFilesRelative(string relativePath) 39 | { 40 | return GetFiles(Application.dataPath + relativePath); 41 | } 42 | 43 | public static void DeleteAllFilesAndFolders(string folderPath, bool recursive = true, bool isTop = true) 44 | { 45 | if(!Exists(folderPath)) 46 | { 47 | return; 48 | } 49 | 50 | foreach(string file in GetFiles(folderPath)) 51 | { 52 | File.Delete(file); 53 | } 54 | 55 | if(recursive) 56 | { 57 | foreach(string dir in Directory.GetDirectories(folderPath)) 58 | { 59 | DeleteAllFilesAndFolders(dir, recursive, false); 60 | } 61 | } 62 | 63 | if(!isTop) 64 | { 65 | Directory.Delete(folderPath); 66 | } 67 | } 68 | 69 | /// 70 | /// Creates a directory 71 | /// 72 | /// The path where the directory should be created 73 | /// If the directory was created 74 | public static bool Create(string fullPath) 75 | { 76 | try 77 | { 78 | Directory.CreateDirectory(fullPath); 79 | return true; 80 | } 81 | catch(System.Exception ex) 82 | { 83 | Debug.LogError("Failed to create directory! error - " + ex.Message); 84 | return false; 85 | } 86 | } 87 | /// 88 | /// Checks the folder and creates it if it does not exist, only returns false if the directory failed to create 89 | /// 90 | public static bool CheckAndCreate(string fullPath) 91 | { 92 | if(!Exists(fullPath)) 93 | { 94 | return Create(fullPath); 95 | } 96 | return true; 97 | } 98 | 99 | /// Appends the relativePath to Application.dataPath 100 | public static bool CreateRelative(string relativePath) 101 | { 102 | return Create(Application.dataPath + relativePath); 103 | } 104 | 105 | } 106 | }//JaneTools.Editor.FileSystem -------------------------------------------------------------------------------- /Assets/Tests/Editor/LanguageDictionaryHelperTests.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor 3 | { 4 | using NUnit.Framework; 5 | using System.Collections.Generic; 6 | 7 | [TestFixture] 8 | public class LanguageDictionaryHelperTests 9 | { 10 | [Test] 11 | public void AddToStringDictionary_UniqueNames() 12 | { 13 | Dictionary testDictionary = new Dictionary(); 14 | string tempKey = "TestKey"; 15 | string tempValue = "temp"; 16 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 17 | LocalizedObject.GetFullKey(tempKey, LocalizedObjectType.STRING), 18 | tempValue); 19 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 20 | LocalizedObject.GetFullKey(tempKey, LocalizedObjectType.AUDIO), 21 | tempValue); 22 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 23 | LocalizedObject.GetFullKey(tempKey, LocalizedObjectType.GAME_OBJECT), 24 | tempValue); 25 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 26 | LocalizedObject.GetFullKey(tempKey, LocalizedObjectType.TEXTURE), 27 | tempValue); 28 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 29 | LocalizedObject.GetFullKey(tempKey, LocalizedObjectType.STRING), 30 | tempValue); 31 | } 32 | 33 | [Test] 34 | public void AddToObjectDictionary_UniqueNames() 35 | { 36 | var testDictionary = new Dictionary(); 37 | string tempKey = "TestKey"; 38 | LocalizedObject tempStringValue = new LocalizedObject(); 39 | 40 | tempStringValue.ObjectType = LocalizedObjectType.STRING; 41 | tempStringValue.TextValue = "TEMP"; 42 | 43 | LocalizedObject tempAudioValue = new LocalizedObject(tempStringValue); 44 | tempAudioValue.ObjectType = LocalizedObjectType.AUDIO; 45 | 46 | LocalizedObject tempPrefabValue = new LocalizedObject(tempStringValue); 47 | tempPrefabValue.ObjectType = LocalizedObjectType.GAME_OBJECT; 48 | 49 | LocalizedObject tempTextureValue = new LocalizedObject(tempStringValue); 50 | tempTextureValue.ObjectType = LocalizedObjectType.TEXTURE; 51 | 52 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 53 | tempKey, 54 | tempStringValue); 55 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 56 | tempKey, 57 | new LocalizedObject(tempStringValue)); 58 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 59 | tempKey, 60 | tempAudioValue); 61 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 62 | tempKey, 63 | tempPrefabValue); 64 | LanguageDictionaryHelper.AddNewKeyPersistent(testDictionary, 65 | tempKey, 66 | tempTextureValue); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /docs/how-to-use-microsoft-translator.md: -------------------------------------------------------------------------------- 1 | # How to use Microsoft Translator with Smart Localization 2 | ![alt](img/microsoft-translator-1.jpeg) 3 | 4 | The ability to automatically translate your text content in Smart Localization is one of our most popular features. It quickly provides placeholder text into your game so that you can continue to customize your game to a specific language, without the immediate need of a professional translator. 5 | The Microsoft Translator API also offers 2 million characters per month for free which makes it perfect for our purposes. 6 | 7 | This guide will presumes that you have done the following already 8 | 9 | * Created a Microsoft Azure Marketplace account - https://datamarket.azure.com/home 10 | * Signed up to use the free tier of the Microsoft Translator API - www.aka.ms/TranslatorADM 11 | 12 | ## Creating a Microsoft Translator Application 13 | With this setup, we need to create a new app in the marketplace in order to be able to use it within Smart Localization. Go to the Azure Marketplace and make sure you are signed in. 14 | 15 | At their main page, you should see a button that says "My Account". Click that, and you should be redirected to your account page. When you are there - click the developer button. 16 | ![alt](img/microsoft-translator-2.png) 17 | 18 | This will take you to the developer site of Azure where you can find a button to register a new application. Press the register button. 19 | ![alt](img/microsoft-translator-3.png) 20 | 21 | his should let you create a new application. Fill in correct values of the fields by following the instructions. 22 | ![alt](img/microsoft-translator-4.png) 23 | 24 | Press create and voila! Your microsoft app is created. 25 | 26 | ## Using the Microsoft Translator App in Unity 27 | 28 | For this tutorial, I'll use a completely new Smart Localization project. Navigate to the main project window by clicking Window->Smart Localization. 29 | 30 | We'll add our newly created application credentials in the section that says "Automatic Translation - Microsoft". Press the Authenticate button when you are done. 31 | 32 | ![alt](img/microsoft-translator-5.png) 33 | 34 | Now we are ready to start translating. We'll begin by creating a couple of new keys in the root language file. Press the "Edit Root Language File" window. 35 | 36 | Here, I'll just create two keys. One that says "Hello" and one that says "Welcome". 37 | ![alt](img/microsoft-translator-6.png) 38 | 39 | Now, go back to the main Smart Localization Window. It's time to create the languages that we'll automatically translate between. For this, I'll create English and Swedish. 40 | ![alt](img/microsoft-translator-7.png) 41 | 42 | Since our root language file values already are written in english, we can just use these values for our english translation. Press the translate button on the English row to enter the Translate Window. 43 | 44 | When you are in this window. Press the "Copy All Values From Root" button and then save. 45 | ![alt](img/microsoft-translator-8.png) 46 | 47 | Now you can head back to the main Smart Localization window and press the Translate button for Swedish. 48 | 49 | You should now see a dropdown with the available languages to translate from if they are compatible with Microsoft Translator. Choose English from the dropdown menu. 50 | 51 | From here, you can either choose to press the "Translate All Text" button - or you can press the "Translate" button next to each individual key. It might take a few seconds before the translation returns from the Microsoft API, but within short the translated keys should appear in the Value column of the translate view. Don't forget to press save. 52 | 53 | ![alt](img/microsoft-translator-9.png) 54 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LanguageParser.cs: -------------------------------------------------------------------------------- 1 | //LanguageParser.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization 7 | { 8 | using UnityEngine; 9 | using System.Xml; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | 13 | /// 14 | /// Helper class that parses a Smart Localization language file at runtime. 15 | /// 16 | public static class LanguageParser 17 | { 18 | static string xmlHeader = "\n"; 19 | 20 | 21 | #region Language Loading 22 | 23 | /// 24 | /// Loads and initializes a language file 25 | /// 26 | public static SortedDictionary LoadLanguage(string languageDataInResX) 27 | { 28 | if(languageDataInResX == null || languageDataInResX == "") 29 | { 30 | Debug.LogError("Cannot load language file - languageDataInResX is null!"); 31 | return null; 32 | } 33 | 34 | SortedDictionary loadedLanguageDictionary = new SortedDictionary(); 35 | 36 | string resxDocument = languageDataInResX; 37 | int index = resxDocument.IndexOf(""); 38 | 39 | //13 == Length of "" 40 | index += 13; 41 | string xmlDocument = resxDocument.Substring(index); 42 | 43 | //add the header to the document 44 | xmlDocument = xmlHeader + xmlDocument; 45 | 46 | //Create the xml file with the new reduced resx document 47 | using(StringReader stringReader = new StringReader(xmlDocument)) 48 | { 49 | using(XmlReader reader = XmlReader.Create(stringReader)) 50 | { 51 | ReadElements(reader, loadedLanguageDictionary); 52 | } 53 | } 54 | 55 | return loadedLanguageDictionary; 56 | } 57 | 58 | static void ReadElements(XmlReader reader, SortedDictionary loadedLanguageDictionary) 59 | { 60 | while (reader.Read()) 61 | { 62 | if(reader.NodeType == XmlNodeType.Element && reader.Name == "data") 63 | { 64 | ReadData(reader, loadedLanguageDictionary); 65 | } 66 | } 67 | } 68 | 69 | static void ReadData(XmlReader reader, SortedDictionary loadedLanguageDictionary) 70 | { 71 | string key = string.Empty; 72 | string value = string.Empty; 73 | 74 | if (reader.HasAttributes) 75 | { 76 | while (reader.MoveToNextAttribute()) 77 | { 78 | if (reader.Name == "name") 79 | { 80 | key = reader.Value; 81 | } 82 | } 83 | } 84 | 85 | //Move back to the element 86 | reader.MoveToElement(); 87 | 88 | //Read the child nodes 89 | if (reader.ReadToDescendant("value")) 90 | { 91 | do 92 | { 93 | value = reader.ReadElementContentAsString(); 94 | } 95 | while (reader.ReadToNextSibling("value")); 96 | } 97 | 98 | //Add the localized parsed values to the localizedObjectDict 99 | LocalizedObject newLocalizedObject = new LocalizedObject(); 100 | newLocalizedObject.ObjectType = LocalizedObject.GetLocalizedObjectType(key); 101 | newLocalizedObject.TextValue = value; 102 | if(newLocalizedObject.ObjectType != LocalizedObjectType.STRING && newLocalizedObject.TextValue != null && newLocalizedObject.TextValue.StartsWith("override=")) 103 | { 104 | newLocalizedObject.OverrideLocalizedObject = true; 105 | newLocalizedObject.OverrideObjectLanguageCode = newLocalizedObject.TextValue.Substring("override=".Length); 106 | } 107 | loadedLanguageDictionary.Add(LocalizedObject.GetCleanKey(key, newLocalizedObject.ObjectType), newLocalizedObject); 108 | } 109 | 110 | #endregion 111 | } 112 | }// namespace SmartLocalization 113 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Editor/IReorderableListAdaptor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2013 Rotorz Limited. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | using UnityEngine; 6 | 7 | namespace SmartLocalization.ReorderableList { 8 | 9 | /// 10 | /// Adaptor allowing reorderable list control to interface with list data. 11 | /// 12 | public interface IReorderableListAdaptor { 13 | 14 | /// 15 | /// Gets count of elements in list. 16 | /// 17 | int Count { get; } 18 | 19 | /// 20 | /// Determines whether an item can be reordered by dragging mouse. 21 | /// 22 | /// 23 | /// This should be a light-weight method since it will be used to determine 24 | /// whether grab handle should be included for each item in a reorderable list. 25 | /// Please note that returning a value of false does not prevent movement 26 | /// on list item since other draggable items can be moved around it. 27 | /// 28 | /// Zero-based index for list element. 29 | /// 30 | /// A value of true if item can be dragged; otherwise false. 31 | /// 32 | bool CanDrag(int index); 33 | /// 34 | /// Determines whether an item can be removed from list. 35 | /// 36 | /// 37 | /// This should be a light-weight method since it will be used to determine 38 | /// whether remove button should be included for each item in list. 39 | /// This is redundant when 40 | /// is specified. 41 | /// 42 | /// Zero-based index for list element. 43 | /// 44 | /// A value of true if item can be removed; otherwise false. 45 | /// 46 | bool CanRemove(int index); 47 | 48 | /// 49 | /// Add new element at end of list. 50 | /// 51 | void Add(); 52 | /// 53 | /// Insert new element at specified index. 54 | /// 55 | /// Zero-based index for list element. 56 | void Insert(int index); 57 | /// 58 | /// Duplicate existing element. 59 | /// 60 | /// Zero-based index of list element. 61 | void Duplicate(int index); 62 | /// 63 | /// Remove element at specified index. 64 | /// 65 | /// Zero-based index of list element. 66 | void Remove(int index); 67 | /// 68 | /// Move element from source index to destination index. 69 | /// 70 | /// Zero-based index of source element. 71 | /// Zero-based index of destination element. 72 | void Move(int sourceIndex, int destIndex); 73 | /// 74 | /// Clear all elements from list. 75 | /// 76 | void Clear(); 77 | 78 | bool CanDraw(int index); 79 | 80 | /// 81 | /// Draw interface for list element. 82 | /// 83 | /// Position in GUI. 84 | /// Zero-based index of array element. 85 | void DrawItem(Rect position, int index); 86 | /// 87 | /// Gets height of list item in pixels. 88 | /// 89 | /// Zero-based index of array element. 90 | /// 91 | /// Measurement in pixels. 92 | /// 93 | float GetItemHeight(int index); 94 | 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /Assets/Tests/Editor/XLSExporterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | [TestFixture] 9 | public class XLSExporterTests 10 | { 11 | public static readonly Dictionary testData = new Dictionary() 12 | { { "TestKey", "TestValue" }, { "TestKey2", "TestValue2" } 13 | }; 14 | 15 | [Test] 16 | public void CreateWorkbookFromDictWithNullSheetName_ExpectedException() 17 | { 18 | Assert.That(()=> XLSExporter.CreateWorkbookFromDictionary(null, testData), Throws.ArgumentNullException); 19 | } 20 | 21 | [Test] 22 | public void CreateWorkbookFromDictWithNullData_ExpectedException() 23 | { 24 | Assert.That(()=> XLSExporter.CreateWorkbookFromDictionary("MySheet", null), Throws.ArgumentNullException); 25 | } 26 | 27 | [Test] 28 | public void CreateWorkbookFromMultipleDictsWithNullSheetName_ExpectedException() 29 | { 30 | var dictionaries = new Dictionary>(); 31 | dictionaries.Add("en", testData); 32 | Assert.That(()=> XLSExporter.CreateWorkbookFromMultipleDictionaries(null, new List(), dictionaries), Throws.ArgumentNullException); 33 | } 34 | 35 | [Test] 36 | public void CreateWorkbookFromMultipleDictsWithNullKeys_ExpectedException() 37 | { 38 | var dictionaries = new Dictionary>(); 39 | dictionaries.Add("en", testData); 40 | Assert.That(()=> XLSExporter.CreateWorkbookFromMultipleDictionaries("MySheet", null, dictionaries), Throws.ArgumentNullException); 41 | } 42 | 43 | [Test] 44 | public void CreateWorkbookFromMultipleDictsWithNullValues_ExpectedException() 45 | { 46 | Assert.That(()=> XLSExporter.CreateWorkbookFromMultipleDictionaries("MySheet", new List(), null), Throws.ArgumentNullException); 47 | } 48 | 49 | [Test] 50 | public void CreateWorkbookFromDict() 51 | { 52 | var workbook = XLSExporter.CreateWorkbookFromDictionary("MySheet", testData); 53 | Assert.AreEqual(1, workbook.NumberOfSheets); 54 | var sheet = workbook.GetSheetAt(0); 55 | Assert.AreEqual("MySheet", sheet.SheetName); 56 | Assert.AreEqual("TestKey", sheet.GetRow(0).GetCell(0).StringCellValue); 57 | Assert.AreEqual("TestValue", sheet.GetRow(0).GetCell(1).StringCellValue); 58 | Assert.AreEqual("TestKey2", sheet.GetRow(1).GetCell(0).StringCellValue); 59 | Assert.AreEqual("TestValue2", sheet.GetRow(1).GetCell(1).StringCellValue); 60 | } 61 | 62 | [Test] 63 | public void CreateWorkbookFromMultipleDicts() 64 | { 65 | var dictionaries = new Dictionary>(); 66 | dictionaries.Add("en", testData); 67 | dictionaries.Add("sv", testData); 68 | var workbook = XLSExporter.CreateWorkbookFromMultipleDictionaries("MySheet", new List(testData.Keys), dictionaries); 69 | Assert.AreEqual(1, workbook.NumberOfSheets); 70 | 71 | var sheet = workbook.GetSheetAt(0); 72 | Assert.AreEqual("MySheet", sheet.SheetName); 73 | Assert.AreEqual(string.Empty, sheet.GetRow(0).GetCell(0).StringCellValue); 74 | Assert.AreEqual("en", sheet.GetRow(0).GetCell(1).StringCellValue); 75 | Assert.AreEqual("sv", sheet.GetRow(0).GetCell(2).StringCellValue); 76 | Assert.AreEqual("TestKey", sheet.GetRow(1).GetCell(0).StringCellValue); 77 | Assert.AreEqual("TestKey2", sheet.GetRow(2).GetCell(0).StringCellValue); 78 | Assert.AreEqual("TestValue", sheet.GetRow(1).GetCell(1).StringCellValue); 79 | Assert.AreEqual("TestValue", sheet.GetRow(1).GetCell(2).StringCellValue); 80 | Assert.AreEqual("TestValue2", sheet.GetRow(2).GetCell(1).StringCellValue); 81 | Assert.AreEqual("TestValue2", sheet.GetRow(2).GetCell(2).StringCellValue); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/ApplicationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | 4 | //ApplicationExtensions.cs 5 | // 6 | // Written by Niklas Borglund and Jakob Hillerström 7 | // 8 | 9 | namespace SmartLocalization 10 | { 11 | internal static class ApplicationExtensions 12 | { 13 | //using ToString() on an enum is not accepted when building the project 14 | // as a dll for web player projects. Using the solution below instead. 15 | internal static string GetSystemLanguage() 16 | { 17 | return GetStringValueOfSystemLanguage(Application.systemLanguage); 18 | } 19 | 20 | internal static string GetStringValueOfSystemLanguage(SystemLanguage systemLanguage) 21 | { 22 | switch (systemLanguage) 23 | { 24 | case SystemLanguage.Afrikaans: 25 | return "Afrikaans"; 26 | case SystemLanguage.Arabic: 27 | return "Arabic"; 28 | case SystemLanguage.Basque: 29 | return "Basque"; 30 | case SystemLanguage.Belarusian: 31 | return "Belarusian"; 32 | case SystemLanguage.Bulgarian: 33 | return "Bulgarian"; 34 | case SystemLanguage.Catalan: 35 | return "Catalan"; 36 | case SystemLanguage.Chinese: 37 | return "Chinese"; 38 | case SystemLanguage.Czech: 39 | return "Czech"; 40 | case SystemLanguage.Danish: 41 | return "Danish"; 42 | case SystemLanguage.Dutch: 43 | return "Dutch"; 44 | case SystemLanguage.English: 45 | return "English"; 46 | case SystemLanguage.Estonian: 47 | return "Estonian"; 48 | case SystemLanguage.Faroese: 49 | return "Faroese"; 50 | case SystemLanguage.Finnish: 51 | return "Finnish"; 52 | case SystemLanguage.French: 53 | return "French"; 54 | case SystemLanguage.German: 55 | return "German"; 56 | case SystemLanguage.Greek: 57 | return "Greek"; 58 | case SystemLanguage.Hebrew: 59 | return "Hebrew"; 60 | case SystemLanguage.Hungarian: 61 | return "Hungarian"; 62 | case SystemLanguage.Icelandic: 63 | return "Icelandic"; 64 | case SystemLanguage.Indonesian: 65 | return "Indonesian"; 66 | case SystemLanguage.Italian: 67 | return "Italian"; 68 | case SystemLanguage.Japanese: 69 | return "Japanese"; 70 | case SystemLanguage.Korean: 71 | return "Korean"; 72 | case SystemLanguage.Latvian: 73 | return "Latvian"; 74 | case SystemLanguage.Lithuanian: 75 | return "Lithuanian"; 76 | case SystemLanguage.Norwegian: 77 | return "Norwegian"; 78 | case SystemLanguage.Polish: 79 | return "Polish"; 80 | case SystemLanguage.Portuguese: 81 | return "Portuguese"; 82 | case SystemLanguage.Romanian: 83 | return "Romanian"; 84 | case SystemLanguage.Russian: 85 | return "Russian"; 86 | case SystemLanguage.SerboCroatian: 87 | return "SerboCroatian"; 88 | case SystemLanguage.Slovak: 89 | return "Slovak"; 90 | case SystemLanguage.Slovenian: 91 | return "Slovenian"; 92 | case SystemLanguage.Spanish: 93 | return "Spanish"; 94 | case SystemLanguage.Swedish: 95 | return "Swedish"; 96 | case SystemLanguage.Thai: 97 | return "Thai"; 98 | case SystemLanguage.Turkish: 99 | return "Turkish"; 100 | case SystemLanguage.Ukrainian: 101 | return "Ukrainian"; 102 | case SystemLanguage.Unknown: 103 | return "Unknown"; 104 | case SystemLanguage.Vietnamese: 105 | return "Vietnamese"; 106 | case SystemLanguage.ChineseSimplified: 107 | return "ChineseSimplified"; 108 | case SystemLanguage.ChineseTraditional: 109 | return "ChineseTraditional"; 110 | default: 111 | return "Unknown"; 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Editor/GenericListAdaptor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2013 Rotorz Limited. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | using UnityEngine; 6 | 7 | using System.Collections.Generic; 8 | 9 | namespace SmartLocalization.ReorderableList { 10 | 11 | /// 12 | /// Reorderable list adaptor for generic list. 13 | /// 14 | /// 15 | /// This adaptor can be subclassed to add special logic to item height calculation. 16 | /// You may want to implement a custom adaptor class where specialised functionality 17 | /// is needed. 18 | /// 19 | /// Type of list element. 20 | public class GenericListAdaptor : IReorderableListAdaptor { 21 | 22 | private IList _list; 23 | 24 | private ReorderableListControl.ItemDrawer _itemDrawer; 25 | 26 | /// 27 | /// Fixed height of each list item. 28 | /// 29 | public float fixedItemHeight; 30 | 31 | /// 32 | /// Gets the underlying list data structure. 33 | /// 34 | public IList List { 35 | get { return _list; } 36 | } 37 | 38 | /// 39 | /// Gets element from list. 40 | /// 41 | /// Zero-based index of element. 42 | /// 43 | /// The element. 44 | /// 45 | public T this[int index] { 46 | get { return _list[index]; } 47 | } 48 | 49 | #region Construction 50 | 51 | /// 52 | /// Initializes a new instance of . 53 | /// 54 | /// The list which can be reordered. 55 | /// Callback to draw list item. 56 | /// Height of list item in pixels. 57 | public GenericListAdaptor(IList list, ReorderableListControl.ItemDrawer itemDrawer, float itemHeight) { 58 | this._list = list; 59 | this._itemDrawer = itemDrawer ?? ReorderableListGUI.DefaultItemDrawer; 60 | this.fixedItemHeight = itemHeight; 61 | } 62 | 63 | #endregion 64 | 65 | #region IReorderableListAdaptor - Implementation 66 | 67 | /// 68 | public int Count { 69 | get { return _list.Count; } 70 | } 71 | 72 | /// 73 | public virtual bool CanDrag(int index) { 74 | return true; 75 | } 76 | /// 77 | public virtual bool CanRemove(int index) { 78 | return true; 79 | } 80 | 81 | public virtual bool CanDraw(int index) 82 | { 83 | return true; 84 | } 85 | 86 | /// 87 | public void Add() { 88 | _list.Add(default(T)); 89 | } 90 | /// 91 | public void Insert(int index) { 92 | _list.Insert(index, default(T)); 93 | } 94 | /// 95 | public void Duplicate(int index) { 96 | _list.Insert(index + 1, _list[index]); 97 | } 98 | /// 99 | public void Remove(int index) { 100 | _list.RemoveAt(index); 101 | } 102 | /// 103 | public void Move(int sourceIndex, int destIndex) { 104 | if (destIndex > sourceIndex) 105 | --destIndex; 106 | 107 | T item = _list[sourceIndex]; 108 | _list.RemoveAt(sourceIndex); 109 | _list.Insert(destIndex, item); 110 | } 111 | /// 112 | public void Clear() { 113 | _list.Clear(); 114 | } 115 | 116 | /// 117 | public virtual void DrawItem(Rect position, int index) { 118 | _list[index] = _itemDrawer(position, _list[index]); 119 | } 120 | 121 | /// 122 | public virtual float GetItemHeight(int index) { 123 | return fixedItemHeight; 124 | } 125 | 126 | #endregion 127 | 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/LanguageUpdateWindow.cs: -------------------------------------------------------------------------------- 1 | //LanguageImportWindow.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | using System; 9 | using System.Collections.Generic; 10 | using UnityEditor; 11 | using UnityEngine; 12 | 13 | [System.Serializable] 14 | public class LanguageUpdateWindow : EditorWindow 15 | { 16 | #region Members 17 | 18 | CSVParser.Delimiter delimiter = CSVParser.Delimiter.COMMA; 19 | SmartCultureInfo chosenCulture; 20 | SmartLocalizationWindow parentWindow; 21 | 22 | int chosenFileFormat = 0; 23 | 24 | static readonly string csvFileEnding = ".csv"; 25 | static readonly string xlsFileEnding = ".xls"; 26 | 27 | static readonly string[] availableFileFormats = {".csv", ".xls"}; 28 | 29 | #endregion 30 | 31 | #region Initialization 32 | 33 | void Initialize(SmartCultureInfo chosenCulture, SmartLocalizationWindow parentWindow) 34 | { 35 | this.chosenCulture = chosenCulture; 36 | this.parentWindow = parentWindow; 37 | 38 | if(chosenFileFormat >= availableFileFormats.Length) 39 | { 40 | chosenFileFormat = 0; 41 | } 42 | } 43 | 44 | #endregion 45 | 46 | #region GUI Methods 47 | 48 | void OnGUI() 49 | { 50 | if(LocalizationWindowUtility.ShouldShowWindow()) 51 | { 52 | GUILayout.Label ("Update Language from file", EditorStyles.boldLabel); 53 | GUILayout.Label ("Language to Update: " + chosenCulture.englishName + " - " + chosenCulture.languageCode); 54 | chosenFileFormat = EditorGUILayout.Popup("File Format", chosenFileFormat, availableFileFormats); 55 | 56 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 57 | { 58 | delimiter = (CSVParser.Delimiter)EditorGUILayout.EnumPopup("Delimiter",delimiter); 59 | } 60 | 61 | if(GUILayout.Button("Update")) 62 | { 63 | OnUpdateClicked(); 64 | } 65 | } 66 | } 67 | 68 | #endregion 69 | 70 | 71 | #region Event Handlers 72 | void OnUpdateClicked() 73 | { 74 | string file = EditorUtility.OpenFilePanel("Select Update file.", "", ""); 75 | if (file != null && file != "") 76 | { 77 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 78 | { 79 | UpdateFromCSV(file); 80 | this.Close(); 81 | } 82 | else if(availableFileFormats[chosenFileFormat] == xlsFileEnding) 83 | { 84 | UpdateFromXLS(file); 85 | this.Close(); 86 | } 87 | else 88 | { 89 | Debug.LogError("Unsupported file format! Cannot export file!"); 90 | } 91 | } 92 | else{ 93 | Debug.Log("Failed to export language"); 94 | } 95 | } 96 | 97 | #endregion 98 | 99 | #region Helper Methods 100 | 101 | void UpdateFromCSV(string chosenUpdateFile) 102 | { 103 | LanguageHandlerEditor.UpdateLanguageFile(chosenCulture.languageCode, CSVParser.Read(chosenUpdateFile, CSVParser.GetDelimiter(delimiter))); 104 | 105 | if(parentWindow.translateLanguageWindow != null) 106 | { 107 | parentWindow.translateLanguageWindow.ReloadLanguage(); 108 | } 109 | } 110 | 111 | void UpdateFromXLS(string chosenUpdateFile) 112 | { 113 | var values = XLSExporter.Read(chosenUpdateFile); 114 | LanguageHandlerEditor.UpdateLanguageFile(chosenCulture.languageCode, values); 115 | 116 | if(parentWindow.translateLanguageWindow != null) 117 | { 118 | parentWindow.translateLanguageWindow.ReloadLanguage(); 119 | } 120 | } 121 | 122 | #endregion 123 | 124 | #region Show Windows 125 | public static LanguageUpdateWindow ShowWindow(SmartCultureInfo info, SmartLocalizationWindow parentWindow) 126 | { 127 | LanguageUpdateWindow languageUpdateWindow = (LanguageUpdateWindow)EditorWindow.GetWindow("Update"); 128 | languageUpdateWindow.Initialize(info, parentWindow); 129 | 130 | return languageUpdateWindow; 131 | } 132 | #endregion 133 | } 134 | } -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started with Smart Localization 2 | Start by navigating to Window->Smart Localization. 3 | ![alt](img/getting-started-1.png) 4 | 5 | The window will prompt you to create a Smart Localization workspace if you haven't done so already. Follow the instructions until you see the main localization window. 6 | ![alt](img/getting-started-2.png) 7 | Press the Edit Root Language File button. This will bring up a new window where you create all the keys and root values along with the key types for your project. We'll start by adding two keys; SmartLocalization.Welcome and SmartLocalization.LocalizedString. 8 | ![alt](img/getting-started-3.png) 9 | Once you've done that, press save and return to the main localization window. 10 | Now it's time to create some languages. For the purpose of this tutorial, we'll create two languages; English and Swedish. Find the two cultures in the Add/Update Languages scroll list and press Create on each one. 11 | ![alt](img/getting-started-4.png) 12 | 13 | Now press the 'Translate' button placed on the English culture row. A new window will be brought up. Since we wrote our root values in english, all we have to do here is to press the "Copy All Values From Root" – button. Don't forget to press save and return to the main window. 14 | ![alt](img/getting-started-5.png) 15 | Now do the same procedure with Swedish. Press Translate and write the Swedish values in the 'Value' column. If you don't know Swedish, just copy what it says in the image below or write whatever value you want. Again, don't forget to press save when you are done. 16 | ![alt](img/getting-started-6.png) 17 | That's basically all the setup you need to get started. By now, you can open the scene LoadAllLanguages.unity that can be found in the Smart Localization package from the store and you should see the created values in the scene when you press play. 18 | 19 | Next I'll show a basic and easy way to get the localized values from code. I'll start by creating a new script called SmartLocTutorial.cs. In the script we'll load the english language and print the two localized strings we created earlier. 20 | ```csharp 21 | using UnityEngine; 22 | using System.Collections; 23 | using SmartLocalization; 24 | 25 | public class SmartLocTutorial : MonoBehaviour 26 | { 27 | string welcomeMessage; 28 | string localizedString; 29 | 30 | void Start(){ 31 | LanguageManager languageManager = LanguageManager.Instance; 32 | languageManager.OnChangeLanguage += OnChangeLanguage; 33 | languageManager.ChangeLanguage("en"); 34 | } 35 | 36 | void OnDestroy(){ 37 | if(LanguageManager.HasInstance){ 38 | LanguageManager.Instance.OnChangeLanguage -= OnChangeLanguage; 39 | } 40 | } 41 | 42 | void OnChangeLanguage(LanguageManager languageManager){ 43 | welcomeMessage = languageManager.GetTextValue("SmartLocalization.Welcome"); 44 | localizedString = languageManager.GetTextValue("SmartLocalization.LocalizedString"); 45 | } 46 | 47 | void OnGUI(){ 48 | GUILayout.Label(welcomeMessage); 49 | GUILayout.Label(localizedString); 50 | } 51 | } 52 | 53 | ``` 54 | Adding this script to an empty game object in your scene should produce the following output. 55 | ![alt](img/getting-started-7.png) 56 | 57 | What if you want to switch to the swedish values at runtime? Simplest way to do that would be to expand the OnGUI method in our SmartLocTutorial script. 58 | ```csharp 59 | void OnGUI(){ 60 | GUILayout.Label(welcomeMessage); 61 | GUILayout.Label(localizedString); 62 | 63 | if(GUILayout.Button("English")){ 64 | LanguageManager.Instance.ChangeLanguage("en"); 65 | } 66 | 67 | if(GUILayout.Button("Swedish")){ 68 | LanguageManager.Instance.ChangeLanguage("sv"); 69 | } 70 | } 71 | ``` 72 | Play your scene again, press the Swedish button, and you should see the Swedish values on the screen. 73 | 74 | ![alt](img/getting-started-8.png) -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/LanguageExportWindow.cs: -------------------------------------------------------------------------------- 1 | //LanguageExportWindow.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | using System.Collections.Generic; 9 | using UnityEditor; 10 | using UnityEngine; 11 | 12 | [System.Serializable] 13 | public class LanguageExportWindow : EditorWindow 14 | { 15 | #region Members 16 | 17 | public SmartCultureInfo chosenCulture = null; 18 | public CSVParser.Delimiter delimiter = CSVParser.Delimiter.COMMA; 19 | int chosenFileFormat = 0; 20 | 21 | static readonly string csvFileEnding = ".csv"; 22 | static readonly string xlsFileEnding = ".xls"; 23 | 24 | static readonly string[] availableFileFormats = {".csv", ".xls"}; 25 | 26 | #endregion 27 | 28 | #region Initialization 29 | 30 | void Initialize(SmartCultureInfo chosenCulture) 31 | { 32 | this.chosenCulture = chosenCulture; 33 | 34 | if(chosenFileFormat >= availableFileFormats.Length) 35 | { 36 | chosenFileFormat = 0; 37 | } 38 | } 39 | 40 | #endregion 41 | #region GUI Methods 42 | 43 | void OnGUI() 44 | { 45 | if(LocalizationWindowUtility.ShouldShowWindow()) 46 | { 47 | if(chosenCulture == null) 48 | { 49 | this.Close(); 50 | } 51 | 52 | GUILayout.Label ("Export Language", EditorStyles.boldLabel); 53 | GUILayout.Label ("Language to Export: " + chosenCulture.englishName + " - " + chosenCulture.languageCode); 54 | chosenFileFormat = EditorGUILayout.Popup("File Format", chosenFileFormat, availableFileFormats); 55 | 56 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 57 | { 58 | delimiter = (CSVParser.Delimiter)EditorGUILayout.EnumPopup("Delimiter", delimiter); 59 | } 60 | 61 | if(GUILayout.Button("Export")) 62 | { 63 | OnExportClicked(); 64 | } 65 | } 66 | } 67 | 68 | #endregion 69 | 70 | #region Event Handlers 71 | 72 | void OnExportClicked() 73 | { 74 | string folder = EditorUtility.OpenFolderPanel("Select folder to save to.", "", ""); 75 | if (folder != null && folder != string.Empty) 76 | { 77 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 78 | { 79 | ExportToCSV(folder); 80 | this.Close(); 81 | } 82 | else if(availableFileFormats[chosenFileFormat] == xlsFileEnding) 83 | { 84 | ExportToXLS(folder); 85 | this.Close(); 86 | } 87 | else 88 | { 89 | Debug.LogError("Unsupported file format! Cannot export file!"); 90 | } 91 | } 92 | else{ 93 | Debug.Log("Failed to export language"); 94 | } 95 | } 96 | 97 | 98 | #endregion 99 | 100 | #region Helper Methods 101 | 102 | void ExportToCSV(string chosenExportFolder) 103 | { 104 | string name = chosenCulture.englishName + " - " + chosenCulture.languageCode + ".csv"; 105 | var input = new List>(); 106 | Dictionary languageItems = LanguageHandlerEditor.LoadParsedLanguageFile(chosenCulture.languageCode, false); 107 | foreach (var item in languageItems) 108 | { 109 | var row = new List(); 110 | row.Add(item.Key); 111 | row.Add(item.Value.TextValue); 112 | input.Add(row); 113 | } 114 | CSVParser.Write(chosenExportFolder + "/" + name, CSVParser.GetDelimiter(delimiter), input); 115 | } 116 | 117 | void ExportToXLS(string chosenExportFolder) 118 | { 119 | string name = chosenCulture.englishName + " - " + chosenCulture.languageCode + xlsFileEnding; 120 | 121 | XLSExporter.Write(chosenExportFolder + "/" + name, chosenCulture.englishName, 122 | LanguageHandlerEditor.LoadLanguageFile(chosenCulture.languageCode, false)); 123 | } 124 | 125 | #endregion 126 | 127 | #region Show Windows 128 | public static LanguageExportWindow ShowWindow(SmartCultureInfo chosenCulture) 129 | { 130 | LanguageExportWindow thisWindow = (LanguageExportWindow)EditorWindow.GetWindow("Export"); 131 | thisWindow.Initialize(chosenCulture); 132 | return thisWindow; 133 | } 134 | #endregion 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/CreateLanguageWindow.cs: -------------------------------------------------------------------------------- 1 | // CreateLanguageWindow.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | using UnityEditor; 9 | using UnityEngine; 10 | 11 | public class CreateLanguageWindow : EditorWindow 12 | { 13 | [SerializeField] 14 | string englishName = null; 15 | [SerializeField] 16 | string languageCode = null; 17 | [SerializeField] 18 | string nativeName = null; 19 | [SerializeField] 20 | bool isRightToLeft = false; 21 | 22 | bool showHelpMessage = false; 23 | string helpMessage = null; 24 | MessageType helpMessageType = MessageType.Info; 25 | SmartLocalizationWindow parentWindow = null; 26 | 27 | void OnGUI() 28 | { 29 | if(LocalizationWindowUtility.ShouldShowWindow()) 30 | { 31 | GUILayout.Label ("Create a new culture info", EditorStyles.boldLabel); 32 | 33 | languageCode = EditorGUILayout.TextField("Language Code", languageCode); 34 | if(languageCode != null) 35 | languageCode = languageCode.RemoveWhitespace(); 36 | 37 | englishName = EditorGUILayout.TextField("English Name", englishName); 38 | nativeName = EditorGUILayout.TextField("Native Name", nativeName); 39 | isRightToLeft = EditorGUILayout.Toggle("Is Right To Left", isRightToLeft); 40 | 41 | if(GUILayout.Button("Create")) 42 | { 43 | SmartCultureInfo newInfo = new SmartCultureInfo(); 44 | newInfo.languageCode = languageCode; 45 | newInfo.englishName = englishName.Trim(); 46 | newInfo.nativeName = nativeName.Trim(); 47 | newInfo.isRightToLeft = isRightToLeft; 48 | 49 | SmartCultureInfoCollection allCultures = SmartCultureInfoEx.Deserialize(LocalizationWorkspace.CultureInfoCollectionFilePath()); 50 | if(!allCultures.IsCultureInCollection(newInfo)) 51 | { 52 | allCultures.AddCultureInfo(newInfo); 53 | allCultures.Serialize(LocalizationWorkspace.CultureInfoCollectionFilePath()); 54 | LanguageHandlerEditor.CheckAndSaveAvailableLanguages(allCultures); 55 | 56 | showHelpMessage = true; 57 | helpMessageType = MessageType.Info; 58 | helpMessage = string.Format("Successfully created language!\n Language Code: {0}\n English Name:{1}\n Native Name:{2}\n IsRightToLeft:{3}", 59 | newInfo.languageCode, newInfo.englishName, newInfo.nativeName, newInfo.isRightToLeft); 60 | 61 | if(parentWindow != null) 62 | { 63 | parentWindow.InitializeCultureCollections(true); 64 | } 65 | 66 | this.Close(); 67 | } 68 | else 69 | { 70 | SmartCultureInfo conflictingCulture = allCultures.FindCulture(newInfo); 71 | string conflictingVariable = null; 72 | 73 | if(conflictingCulture.languageCode.ToLower() == newInfo.languageCode.ToLower()) 74 | { 75 | conflictingVariable = "Language Code:" + newInfo.languageCode; 76 | } 77 | else if(conflictingCulture.englishName.ToLower() == newInfo.englishName.ToLower()) 78 | { 79 | conflictingVariable = "English Name:" + newInfo.englishName; 80 | } 81 | 82 | showHelpMessage = true; 83 | helpMessageType = MessageType.Error; 84 | helpMessage = string.Format("Failed to create language!\n Conflicting variable - {0}\n\n", 85 | conflictingVariable); 86 | 87 | helpMessage += string.Format("Conflicting Culture \n Language Code: {0}\n English Name:{1}\n Native Name:{2}", 88 | conflictingCulture.languageCode, conflictingCulture.englishName, conflictingCulture.nativeName); 89 | } 90 | } 91 | 92 | if(showHelpMessage) 93 | { 94 | EditorGUILayout.HelpBox(helpMessage, helpMessageType); 95 | } 96 | } 97 | } 98 | 99 | 100 | #region Show Window 101 | public static CreateLanguageWindow ShowWindow(SmartLocalizationWindow parentWindow) 102 | { 103 | CreateLanguageWindow thisWindow = (CreateLanguageWindow)EditorWindow.GetWindow("New Language"); 104 | thisWindow.parentWindow = parentWindow; 105 | return thisWindow; 106 | } 107 | #endregion 108 | } 109 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /[Dd]ist/ 7 | /SmartLocalizationWorkspace/ 8 | /.vscode/ 9 | /node_modules/ 10 | unitTestResults.xml 11 | 12 | 13 | ################# 14 | ## Eclipse 15 | ################# 16 | 17 | *.pydevproject 18 | .project 19 | .metadata 20 | bin/ 21 | tmp/ 22 | *.tmp 23 | *.bak 24 | *.swp 25 | *~.nib 26 | local.properties 27 | .classpath 28 | .settings/ 29 | .loadpath 30 | Library/ 31 | 32 | # External tool builders 33 | .externalToolBuilders/ 34 | 35 | # Locally stored "Eclipse launch configurations" 36 | *.launch 37 | 38 | # CDT-specific 39 | .cproject 40 | 41 | # PDT-specific 42 | .buildpath 43 | 44 | 45 | ################# 46 | ## Visual Studio 47 | ################# 48 | 49 | ## Ignore Visual Studio temporary files, build results, and 50 | ## files generated by popular Visual Studio add-ons. 51 | 52 | # User-specific files 53 | *.svd 54 | *.userprefs 55 | *.csproj 56 | *.pidb 57 | *.suo 58 | *.sln 59 | *.user 60 | *.unityproj 61 | *.booproj 62 | 63 | # Build results 64 | 65 | [Dd]ebug/ 66 | [Rr]elease/ 67 | x64/ 68 | build/ 69 | [Bb]in/ 70 | [Oo]bj/ 71 | 72 | # MSTest test Results 73 | [Tt]est[Rr]esult*/ 74 | [Bb]uild[Ll]og.* 75 | 76 | *_i.c 77 | *_p.c 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.pch 82 | *.pdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.log 98 | *.scc 99 | 100 | # Visual C++ cache files 101 | ipch/ 102 | *.aps 103 | *.ncb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | 108 | # Visual Studio profiler 109 | *.psess 110 | *.vsp 111 | *.vspx 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | 120 | # TeamCity is a build add-in 121 | _TeamCity* 122 | 123 | # DotCover is a Code Coverage Tool 124 | *.dotCover 125 | 126 | # NCrunch 127 | *.ncrunch* 128 | .*crunch*.local.xml 129 | 130 | # Installshield output folder 131 | [Ee]xpress/ 132 | 133 | # DocProject is a documentation generator add-in 134 | DocProject/buildhelp/ 135 | DocProject/Help/*.HxT 136 | DocProject/Help/*.HxC 137 | DocProject/Help/*.hhc 138 | DocProject/Help/*.hhk 139 | DocProject/Help/*.hhp 140 | DocProject/Help/Html2 141 | DocProject/Help/html 142 | 143 | # Click-Once directory 144 | publish/ 145 | 146 | # Publish Web Output 147 | *.Publish.xml 148 | *.pubxml 149 | 150 | # NuGet Packages Directory 151 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 152 | #packages/ 153 | 154 | # Windows Azure Build Output 155 | csx 156 | *.build.csdef 157 | 158 | # Windows Store app package directory 159 | AppPackages/ 160 | 161 | # Others 162 | sql/ 163 | *.Cache 164 | ClientBin/ 165 | [Ss]tyle[Cc]op.* 166 | ~$* 167 | *~ 168 | *.dbmdl 169 | *.[Pp]ublish.xml 170 | *.pfx 171 | *.publishsettings 172 | 173 | # RIA/Silverlight projects 174 | Generated_Code/ 175 | 176 | # Backup & report files from converting an old project file to a newer 177 | # Visual Studio version. Backup files are not needed, because we have git ;-) 178 | _UpgradeReport_Files/ 179 | Backup*/ 180 | UpgradeLog*.XML 181 | UpgradeLog*.htm 182 | 183 | # SQL Server files 184 | App_Data/*.mdf 185 | App_Data/*.ldf 186 | 187 | ############# 188 | ## Windows detritus 189 | ############# 190 | 191 | # Windows image file caches 192 | Thumbs.db 193 | ehthumbs.db 194 | 195 | # Folder config file 196 | Desktop.ini 197 | 198 | # Recycle Bin used on file shares 199 | $RECYCLE.BIN/ 200 | 201 | # Mac crap 202 | .DS_Store 203 | 204 | 205 | ############# 206 | ## Python 207 | ############# 208 | 209 | *.py[co] 210 | 211 | # Packages 212 | *.egg 213 | *.egg-info 214 | dist/ 215 | build/ 216 | eggs/ 217 | parts/ 218 | var/ 219 | sdist/ 220 | develop-eggs/ 221 | .installed.cfg 222 | 223 | # Installer logs 224 | pip-log.txt 225 | 226 | # Unit test / coverage reports 227 | .coverage 228 | .tox 229 | 230 | #Translations 231 | *.mo 232 | 233 | #Mr Developer 234 | .mr.developer.cfg 235 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | var gulp = require('gulp'); 3 | var gulpsync = require('gulp-sync')(gulp); 4 | var execSync = require('child_process').execSync; 5 | var filesExist = require('files-exist'); 6 | var rimraf = require('rimraf'); 7 | var upath = require('upath'); 8 | var fs = require('fs'); 9 | 10 | function isWindows(){ 11 | return /^win/.test(process.platform); 12 | } 13 | 14 | function getConvertedPath(pPath){ 15 | if(isWindows()){ 16 | return pPath.replace(/\//g,"\\"); 17 | } 18 | else{ 19 | return upath.toUnix(pPath); 20 | } 21 | }; 22 | 23 | var tasks = { 24 | buildAll: 'build-all', 25 | runTests: 'run-tests', 26 | buildPro: 'build-pro', 27 | createTempBuildProject: 'create-temp-build-project', 28 | copyProFilesToTempProject: 'copy-pro-files-to-temp-project', 29 | removeTempBuildProject: 'remove-temp-build-project', 30 | createProUnityPackage: 'create-pro-unity-package', 31 | }; 32 | 33 | var paths = { 34 | dist: getConvertedPath('dist\\'), 35 | tempBuildFolder: getConvertedPath('TempBuildProject\\') 36 | } 37 | 38 | 39 | gulp.task('default', [tasks.buildAll]); 40 | gulp.task(tasks.buildAll, gulpsync.sync([tasks.runTests, tasks.buildPro])); 41 | gulp.task(tasks.buildPro, gulpsync.sync([tasks.createTempBuildProject, tasks.copyProFilesToTempProject, tasks.createProUnityPackage, tasks.removeTempBuildProject])); 42 | 43 | 44 | gulp.task(tasks.runTests, function() { 45 | runUnityJob('-quit -batchmode -executeMethod UnityTest.Batch.RunUnitTests -resultFilePath=unitTestResults.xml'); 46 | }); 47 | 48 | 49 | gulp.task(tasks.removeTempBuildProject, function() { 50 | rimraf.sync('./' + paths.tempBuildFolder); 51 | console.log(paths.tempBuildFolder + ' was removed!'); 52 | }); 53 | 54 | gulp.task(tasks.createTempBuildProject, function() { 55 | return gulp.src('ProjectSettings/**/*.*') 56 | .pipe(gulp.dest(paths.tempBuildFolder + getConvertedPath('ProjectSettings/'))); 57 | }); 58 | 59 | gulp.task(tasks.copyProFilesToTempProject, function() { 60 | return gulp.src('Assets/SmartLocalization/**/*.*') 61 | .pipe(gulp.dest(paths.tempBuildFolder + getConvertedPath('Assets/SmartLocalization/'))); 62 | }); 63 | 64 | 65 | gulp.task(tasks.createProUnityPackage, function() { 66 | var d = new Date(); 67 | var unityPackageName = 'SmartLocalization_PRO_'; 68 | unityPackageName += d.getFullYear() + '-' + d.getMonth()+'-' + d.getDate() + '_' + (d.getHours() + 1) + '-' + d.getMinutes() + '-' + d.getSeconds(); 69 | unityPackageName += '.unitypackage'; 70 | runUnityJob('-quit -batchmode -projectPath \"' + __dirname + '\\' + paths.tempBuildFolder + '\" -exportPackage \"Assets\\SmartLocalization\" \"' + unityPackageName + '\"'); 71 | return gulp.src('TempBuildProject/*.unitypackage') 72 | .pipe(gulp.dest(paths.dist)); 73 | }); 74 | 75 | function getDefaultUnityPath(){ 76 | if(isWindows() === true){ 77 | return 'C:\\Program Files\\Unity\\Editor\\Unity.exe'; 78 | } 79 | return '/Applications/Unity/Unity.app/Contents/MacOS/Unity'; 80 | } 81 | 82 | 83 | function runUnityJob(pArguments){ 84 | var unityPath = process.env.npm_config_unityPath; 85 | if(unityPath === null || unityPath === undefined){ 86 | console.log('a valid unity path was not provided. provide with npm --unityPath= run gulp '); 87 | unityPath = getDefaultUnityPath(); 88 | console.log('Trying default unity path:' + unityPath); 89 | } 90 | if(pArguments === null || pArguments === undefined){ 91 | throw 'invalid arguments in unity job'; 92 | } 93 | 94 | executeCommand('\"' + unityPath + '\" ' + pArguments); 95 | } 96 | 97 | function executeCommand(pFullCommand){ 98 | var commandToExecute = getConvertedPath(pFullCommand);; 99 | console.log('Executing command: ' + commandToExecute); 100 | try { 101 | var output = execSync(commandToExecute, {encoding:'utf-8'}); 102 | console.log(String(output)); 103 | } 104 | catch(error) { 105 | throw error.stdout.toString(); 106 | } 107 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/LanguageImportWindow.cs: -------------------------------------------------------------------------------- 1 | //LanguageImportWindow.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | using System; 9 | using System.Collections.Generic; 10 | using UnityEditor; 11 | using UnityEngine; 12 | 13 | [System.Serializable] 14 | public class LanguageImportWindow : EditorWindow 15 | { 16 | #region Members 17 | 18 | public CSVParser.Delimiter delimiter = CSVParser.Delimiter.COMMA; 19 | public SmartCultureInfo chosenCulture = null; 20 | public Action creationDelegate = null; 21 | int chosenFileFormat = 0; 22 | 23 | static readonly string csvFileEnding = ".csv"; 24 | static readonly string xlsFileEnding = ".xls"; 25 | 26 | 27 | static readonly string[] availableFileFormats = {".csv", ".xls"}; 28 | 29 | #endregion 30 | 31 | #region Initialization 32 | 33 | void OnDestroy() 34 | { 35 | creationDelegate = null; 36 | } 37 | 38 | void Initialize(SmartCultureInfo cultureInfo, Action creationDelegate = null) 39 | { 40 | if(creationDelegate != null) 41 | { 42 | this.creationDelegate = creationDelegate; 43 | } 44 | chosenCulture = cultureInfo; 45 | if(chosenFileFormat >= availableFileFormats.Length) 46 | { 47 | chosenFileFormat = 0; 48 | } 49 | } 50 | 51 | #endregion 52 | 53 | #region GUI Methods 54 | 55 | void OnGUI() 56 | { 57 | if(LocalizationWindowUtility.ShouldShowWindow()) 58 | { 59 | if(chosenCulture == null) 60 | { 61 | this.Close(); 62 | } 63 | 64 | GUILayout.Label ("Import Language", EditorStyles.boldLabel); 65 | GUILayout.Label ("Language to Import: " + chosenCulture.englishName + " - " + chosenCulture.languageCode); 66 | 67 | chosenFileFormat = EditorGUILayout.Popup("File Format", chosenFileFormat, availableFileFormats); 68 | 69 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 70 | { 71 | delimiter = (CSVParser.Delimiter)EditorGUILayout.EnumPopup("Delimiter",delimiter); 72 | } 73 | 74 | if(GUILayout.Button("Import")) 75 | { 76 | OnImportClicked(); 77 | } 78 | } 79 | } 80 | 81 | #endregion 82 | 83 | #region Event Handlers 84 | void OnImportClicked() 85 | { 86 | string file = EditorUtility.OpenFilePanel("Select Import file.", "", ""); 87 | if (file != null && file != "") 88 | { 89 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 90 | { 91 | ImportFromCSV(file); 92 | this.Close(); 93 | } 94 | else if(availableFileFormats[chosenFileFormat] == xlsFileEnding) 95 | { 96 | ImportFromXLS(file); 97 | this.Close(); 98 | } 99 | else 100 | { 101 | Debug.LogError("Unsupported file format! Cannot export file!"); 102 | } 103 | } 104 | else{ 105 | Debug.Log("Failed to export language"); 106 | } 107 | } 108 | #endregion 109 | 110 | #region Helper Methods 111 | 112 | void ImportFromCSV(string chosenImportFile) 113 | { 114 | List> values = CSVParser.Read(chosenImportFile, CSVParser.GetDelimiter(delimiter)); 115 | 116 | if(chosenCulture == null) 117 | { 118 | Debug.LogError("The language: " + chosenCulture.englishName + " could not be created"); 119 | this.Close(); 120 | return; 121 | } 122 | LanguageHandlerEditor.CreateNewLanguage(chosenCulture.languageCode, values); 123 | 124 | if(creationDelegate != null) 125 | { 126 | creationDelegate(); 127 | creationDelegate = null; 128 | } 129 | } 130 | 131 | void ImportFromXLS(string chosenImportFile) 132 | { 133 | List> values = XLSExporter.Read(chosenImportFile); 134 | if(chosenCulture == null) 135 | { 136 | Debug.LogError("The language: " + chosenCulture.englishName + " could not be created"); 137 | this.Close(); 138 | return; 139 | } 140 | LanguageHandlerEditor.CreateNewLanguage(chosenCulture.languageCode, values); 141 | 142 | if(creationDelegate != null) 143 | { 144 | creationDelegate(); 145 | creationDelegate = null; 146 | } 147 | } 148 | 149 | #endregion 150 | 151 | #region Show Windows 152 | public static LanguageImportWindow ShowWindow(SmartCultureInfo chosenCulture, Action creationDelegate) 153 | { 154 | LanguageImportWindow languageImportWindow = (LanguageImportWindow)EditorWindow.GetWindow("CSV Import"); 155 | languageImportWindow.Initialize(chosenCulture, creationDelegate); 156 | return languageImportWindow; 157 | } 158 | #endregion 159 | } 160 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Localization 2 by Niklas Borglund & Jakob Hillerström 2 | 3 | Localizing your game has never been this easy. Localize your game with only a few mouse clicks. 4 | 5 | Just open the Smart Localization Window in Window->Smart Localization and start by pressing 6 | "Create new localization system". 7 | Edit the root language file by adding some keys and base values. 8 | 9 | To add a new language to your project, choose a language in the "Add/Update Languages" panel and click "Create". 10 | To edit and translate a language, press the "Translate" button on the language you want to work with in the 11 | Created Languages panel. 12 | 13 | There's an example scene in SmartLocalization->Examples called LoadAllLanguages, which draws all the available 14 | languages and values on the screen. The code for the example is also included and lies within the same folder. 15 | 16 | # Building the Smart Localization Unity Package 17 | You can build the unitypackage with the built in tools in unity manually - or you can use the gulp jobs in the project. 18 | The gulp jobs will by default run all the unit tests and export the package into a folder called "dist" 19 | 20 | First, make sure you have npm installed - https://nodejs.org/en/download/ 21 | 22 | Then, either with terminal or the windows command prompt, locate yourself to the location of your clone of this repository and 23 | run the following commands: 24 | 25 | First, you need to install the node modules required. 26 | ``` 27 | npm install 28 | ``` 29 | 30 | Then you only have to write the following command everytime you want to export the package given that your Unity installation is at the default location. 31 | ``` 32 | npm run gulp 33 | ``` 34 | 35 | If it's not in the default location, you can set it yourself with the --unityPath parameter in the gulp job. 36 | ``` 37 | npm --unityPath= run gulp' 38 | ``` 39 | 40 | # Code Examples 41 | 42 | ```csharp 43 | //Returns a text value in the current language for the key 44 | string myKey = LanguageManager.Instance.GetTextValue("MYKEY"); 45 | ``` 46 | ```csharp 47 | //Gets the audio clip for the current language 48 | AudioClip myClip = LanguageManager.Instance.GetAudioClip("MYKEY"); 49 | ``` 50 | ```csharp 51 | //Gets the prefab game object for the current language 52 | GameObject myPrefab = LanguageManager.Instance.GetPrefab("MYKEY"); 53 | ``` 54 | ```csharp 55 | //Gets the texture for the current language 56 | Texture myTexture = LanguageManager.Instance.GetTexture("MYKEY"); 57 | ``` 58 | ```csharp 59 | //(Pro feature)Gets a localized TextAsset 60 | TextAsset myTextAsset = LanguageManager.Instance.GetTextAsset("MYKEY"); 61 | ``` 62 | ```csharp 63 | //To cache the LanguageManager in a variable 64 | LanguageManager languageManager = LanguageManager.Instance; 65 | ``` 66 | ```csharp 67 | //Get a list of all the available languages 68 | List availableLanguages = thisLanguageManager.GetSupportedLanguages(); 69 | ``` 70 | ```csharp 71 | Get the smart culture info of the system language if it is supported. otherwise it will return null 72 | SmartCultureInfo systemLanguage = thisLanguageManager.GetSupportedSystemLanguage(); 73 | ``` 74 | ```csharp 75 | //Check if a language is supported with an ISO-639 language code (string = "en" "sv" "es" etc.) 76 | LanguageManager.Instance.IsLanguageSupported("en"); 77 | ``` 78 | ```csharp 79 | //Check if a language is supported with an instance of SmartCultureInfo 80 | SmartCultureInfo swedishCulture = new SmartCultureInfo("sv", "Swedish", "Svenska", false); 81 | LanguageManager.Instance.IsLanguageSupported(swedishCulture); 82 | ``` 83 | ```csharp 84 | //Change a language with an ISO-639 language code ("en" "sv" "es" etc., Make sure the language is supported) 85 | LanguageManager.Instance.ChangeLanguage("en"); 86 | ``` 87 | ```csharp 88 | //Change the language with a SmartCultureInfo instance 89 | SmartCultureInfo swedishCulture = new SmartCultureInfo("sv", "Swedish", "Svenska", false); 90 | LanguageManager.Instance.ChangeLanguage(swedishCulture); 91 | ``` 92 | ```csharp 93 | //How to register on the event that fires when a language changed 94 | LanguageManager.Instance.OnChangeLanguage += OnLanguageChanged; //OnLanguageChanged = delegate method that you created 95 | ``` 96 | ```csharp 97 | //Enable extensive debug logging 98 | LanguageManager.Instance.VerboseLogging = true; 99 | ``` 100 | ```csharp 101 | //Check if a localized key exists 102 | LanguageManager.Instance.HasKey("myKey") 103 | ``` -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/ListControls/LocalizedObjectListAdaptor.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SmartLocalization.Editor 3 | { 4 | using UnityEngine; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using SmartLocalization.ReorderableList; 8 | 9 | internal class LocalizedObjectListAdaptor : GenericListAdaptor 10 | { 11 | public bool collapseMultiline = false; 12 | string searchLine = string.Empty; 13 | LanguageSearchType searchType = LanguageSearchType.KEY; 14 | List changedRootKeys = null; 15 | bool isTranslationView = false; 16 | 17 | Dictionary searchCache = new Dictionary(); 18 | 19 | public string SearchLine 20 | { 21 | get 22 | { 23 | return searchLine; 24 | } 25 | set 26 | { 27 | if(searchLine != value) 28 | { 29 | ClearSearchCache(); 30 | } 31 | searchLine = value; 32 | } 33 | } 34 | public LanguageSearchType SearchType 35 | { 36 | get 37 | { 38 | return searchType; 39 | } 40 | set 41 | { 42 | if(searchType != value) 43 | { 44 | ClearSearchCache(); 45 | } 46 | searchType = value; 47 | } 48 | } 49 | 50 | 51 | public LocalizedObjectListAdaptor(List list, 52 | List changedRootKeys, 53 | ReorderableListControl.ItemDrawer itemDrawer, 54 | float itemHeight, 55 | bool isTranslationView = false) 56 | : base(list, itemDrawer, itemHeight) 57 | { 58 | this.changedRootKeys = changedRootKeys; 59 | this.isTranslationView = isTranslationView; 60 | } 61 | 62 | public SerializableLocalizationObjectPair GetObjectPair(int itemIndex) 63 | { 64 | return this[itemIndex]; 65 | } 66 | 67 | public string GetCurrentKey(int index) 68 | { 69 | if(!isTranslationView) 70 | { 71 | if(changedRootKeys == null) 72 | { 73 | return string.Empty; 74 | } 75 | 76 | return changedRootKeys[index].changedValue; 77 | } 78 | else 79 | { 80 | return GetObjectPair(index).keyValue; 81 | } 82 | } 83 | 84 | public override void DrawItem(Rect position, int index) 85 | { 86 | if(IsWithinSearch(index)) 87 | { 88 | base.DrawItem(position, index); 89 | } 90 | } 91 | 92 | public override float GetItemHeight(int index) 93 | { 94 | SerializableLocalizationObjectPair currentObjectPair = GetObjectPair(index); 95 | 96 | if(IsWithinSearch(index)) 97 | { 98 | if(currentObjectPair.changedValue.ObjectType == LocalizedObjectType.STRING) 99 | { 100 | if(collapseMultiline) 101 | { 102 | return base.GetItemHeight(index); 103 | } 104 | 105 | var textDimensions = GUI.skin.label.CalcSize(new GUIContent(currentObjectPair.changedValue.TextValue)); 106 | float currentHeight = base.GetItemHeight(index); 107 | 108 | return Mathf.Max(textDimensions.y, currentHeight); 109 | } 110 | else 111 | { 112 | return base.GetItemHeight(index); 113 | } 114 | } 115 | else 116 | { 117 | return 0; 118 | } 119 | } 120 | 121 | public override bool CanDrag(int index) 122 | { 123 | return false; 124 | } 125 | 126 | public override bool CanRemove(int index) 127 | { 128 | if(isTranslationView) 129 | { 130 | return false; 131 | } 132 | else 133 | { 134 | return base.CanRemove(index); 135 | } 136 | } 137 | 138 | public override bool CanDraw(int index) 139 | { 140 | if(IsWithinSearch(index)) 141 | { 142 | return base.CanDraw(index); 143 | } 144 | else 145 | { 146 | return false; 147 | } 148 | } 149 | 150 | void ClearSearchCache() 151 | { 152 | searchCache.Clear(); 153 | } 154 | 155 | bool IsWithinSearch(int index) 156 | { 157 | if(searchLine == string.Empty || searchLine == null) 158 | { 159 | return true; 160 | } 161 | 162 | if(searchCache.ContainsKey(index)) 163 | { 164 | return searchCache[index]; 165 | } 166 | else 167 | { 168 | string key = null; 169 | 170 | if(searchType == LanguageSearchType.KEY) 171 | { 172 | key = GetCurrentKey(index); 173 | } 174 | else if(searchType == LanguageSearchType.VALUE) 175 | { 176 | key = GetObjectPair(index).changedValue.TextValue; 177 | } 178 | 179 | if(key != null) 180 | { 181 | bool isWithinSearch = key.ToLower().Contains(searchLine.ToLower()); 182 | searchCache.Add(index, isWithinSearch); 183 | return isWithinSearch; 184 | } 185 | else 186 | { 187 | return false; 188 | } 189 | } 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /docs/plural-form-feature.md: -------------------------------------------------------------------------------- 1 | # Using the plural form feature 2 | The plural support feature is used to handle strings with different numbers in them. For example: — “I have {0} apples” 3 | 4 | This key works fine for when you have zero or more than one apples but becomes “I have 1 apples” if you have exactly one. The string you want in that case is “I have 1 apple” and that is what plural support is trying to solve. 5 | 6 | ## Naming your keys 7 | 8 | For the plural form system to pick the correct plural form the keys have to be named in a special way: “[yourKeyName][pluralForm]” ex. “MyAppleSentence3” for plural form 3 9 | ![alt](img/plural-form-1.png) 10 | 11 | This results in the plural form functions correctly picking the right plural form. 12 | 13 | ## Feature Details 14 | 15 | We do this by extending the functions that you use to get strings today. If you add a number after your call to GetTextValue(key) so it becomes GetTextValue(key, count) we figure out what plural form that corresponds to in the currently selected language. We then map your key to that plural form by appending an underscore “” and the number of the plural form. The key “MyAppleSentence” then becomes “MyAppleSentene0” for the first plural form. 16 | 17 | Its important to note that plural from number does not correspond directly to the number passed in to the GetTextValue function. “I have 2 apples” resolves to the second plural form for english but so does every call that requests the key with a number over 1. This is because English has two plural forms and the rule for them goes like this: 18 | 19 | * plural from 0 for n=1 20 | * plural form 1 for n=0 or n>1 21 | 22 | To get this in perspective we can look at the Arabic plural form which goes like this: 23 | 24 | * plural form 0 for n=0 25 | * plural form 1 for n=1 26 | * plural form 2 for n=2 27 | * plural form 3 for n%100 >= 3 && n%100 <= data-preserve-html-node="true" 10 28 | * plural form 4 for n%100 >= 11 29 | * plural form 5 for everything else 30 | 31 | ## Using the plural system 32 | When you have created the keys for your different plural forms all you have to do to use them is to add another parameter to the functions you usually use when getting strings from keys. 33 | 34 | And plural forms of course works not just on string values but on any of the localized keys in your game. 35 | 36 | Here is a list of the function signatures for the basic plural functions: 37 | 38 | ```csharp 39 | string GetTextValue(string key, int count); 40 | TextAsset GetTextAsset(string key, int count); 41 | AudioClip GetAudioClip(string key, int count); 42 | GameObject GetPrefab(string key, int count); 43 | Texture GetTexture(string key, int count); 44 | ``` 45 | 46 | ### Caveat 47 | If you have a key that uses plural forms and you support languages with different number of plural forms there will be some empty keys in the languages with less forms. 48 | 49 | This is needed because of the root keys system and also because of bulk export to single csv/xls to have rows match even in languages which doesn’t have a use for that many plural forms. 50 | 51 | ### Defining your own plural function 52 | In addition to providing the plural functions for each supported language we also expose an overloaded function for getting a key and defining your own plural rule. 53 | ```csharp 54 | string GetTextValue(string key, int count, Func pluralForm); 55 | TextAsset GetTextAsset(string key, int count, Func pluralForm); 56 | AudioClip GetAudioClip(string key, int count, Func pluralForm); 57 | GameObject GetPrefab(string key, int count, Func pluralForm); 58 | Texture GetTexture(string key, int count, Func pluralForm); 59 | ``` 60 | These are great to have if you want to define an arbitrary rule for a specific key. Take these keys for example: 61 | ```csharp 62 | “Unread_0” = “No unread messages” 63 | “Unread_1” = “One unread message” 64 | “Unread_2” = “{0} unread messages” 65 | “Unread_3” = “Over one hundred unread messages” 66 | ``` 67 | To get these to work correctly you will have to define your own plural rule since it doesn’t correspond to any language defined plural rule. 68 | ```csharp 69 | string messages = languageManager.GetTextValue(“Unread”, unreadMessages, (n) => n==0 ? 0 : n==1 ? 1 : n>100 ? 3 : 2); 70 | ``` 71 | Or you can store the plural rule for later use 72 | ```csharp 73 | Func customRule = (n) => n==0 ? 0 : n==1 ? 1 : n>100 ? 3 : 2; 74 | string messages = languageManager.GetTextValue(“Unread”, unreadMessages, customRule); 75 | ``` 76 | If you want more details about handling plural forms in localization you can go to this link: http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/Translation/IAutomaticTranslator.cs: -------------------------------------------------------------------------------- 1 | // IAutomaticTranslator.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | using System.Collections.Generic; 9 | 10 | /// 11 | /// Event handler for initializing an automatic translator 12 | /// 13 | /// If the translator was initialized 14 | public delegate void InitializeTranslatorEventHandler(bool success); 15 | /// 16 | /// Event handler for getting the available translation languages for an Automatic Translator 17 | /// 18 | /// If the fetching operation was a success 19 | /// A list of all the available languages 20 | public delegate void GetAvailableLanguagesEventHandler(bool success, List availableLanguages); 21 | /// 22 | /// Event handler for translating a single string with an Automatic Translator 23 | /// 24 | /// If the translation was a successful operation 25 | /// The key of the translated text. Used for identification 26 | /// The translated text 27 | public delegate void TranslateTextEventHandler(bool success, string key, string translatedText); 28 | /// 29 | /// Event handler for translating an array of strings with an Automatic Translator 30 | /// 31 | /// If the translation was a successful operation 32 | /// A list of the translated keys. The indices in this list correspond with the list of translated values. 33 | /// A list of the translated values. The indices in this list correspond with the list of keys. 34 | public delegate void TranslateTextArrayEventHandler(bool success, List keys, List translatedValues); 35 | 36 | /// 37 | /// Generic Interface for Automatic Translators 38 | /// 39 | public interface IAutomaticTranslator 40 | { 41 | /// 42 | /// Returns if the Automatic Translator is currently in the process of initializing 43 | /// 44 | bool IsInitializing {get;} 45 | /// 46 | /// Returns if the Automatic Translator is initialized 47 | /// 48 | bool IsInitialized {get;} 49 | /// 50 | /// Returns if the Translator is not Initialized anymore due to an expiration of an access token 51 | /// 52 | bool InitializationDidExpire{get;} 53 | /// 54 | /// Initializes the Automatic Translator 55 | /// 56 | /// The Event handler returning success/failure of the operation 57 | /// The client API ID 58 | /// The client API Secret 59 | void Initialize(InitializeTranslatorEventHandler eventHandler, string cliendID, string clientSecret); 60 | /// 61 | /// Gets all the available languages that can be translated. 62 | /// 63 | /// Event handler returning the success/failure along with a list of available languages 64 | void GetAvailableTranslationLanguages(GetAvailableLanguagesEventHandler eventHandler); 65 | /// 66 | /// Translate a single string 67 | /// 68 | /// The event handler returning the translated value 69 | /// The key used for identification 70 | /// The text to translate 71 | /// The language code to translate from 72 | /// The language code to translate to 73 | void TranslateText(TranslateTextEventHandler eventHandler, string key, string textToTranslate, string languageFrom, string languageTo); 74 | 75 | /// 76 | /// Translate an array of texts to a specific language. Note: the event handler can be called multiple times if splitting of the array is necessary 77 | /// 78 | /// The event handler returning the translated values. Can be called multiple times. 79 | /// A list of keys used for identification. Make sure the indices correspond with the list of translated values. 80 | /// A list of strings to translate. Make sure the indices correspond with the list of keys. 81 | /// The language code to translate from 82 | /// The language code to translate to 83 | void TranslateTextArray(TranslateTextArrayEventHandler eventHandler, List keys, List textsToTranslate, string languageFrom, string languageTo); 84 | 85 | } 86 | } //namespace SmartLocalization.Editor -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/Utility/LocalizedKeySelector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizedKeySelector.cs 3 | // 4 | // 5 | // Written by Niklas Borglund and Jakob Hillerström 6 | // 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | 11 | using UnityEngine; 12 | using UnityEditor; 13 | using System.Collections.Generic; 14 | 15 | /// 16 | /// A class providing a simple GUI for selecting a key from the root. 17 | /// 18 | public static class LocalizedKeySelector 19 | { 20 | private static List parsedRootValues = new List(); 21 | private static LocalizedObjectType loadedObjectType; 22 | 23 | /// If the SelectKeyGUI should continously refresh the key list. 24 | public static bool autoRefresh = false; 25 | 26 | 27 | /// 28 | /// Call this from OnInspectorGUI in your own editor class. It will create buttons and a selectable popup with all the keys available. 29 | /// 30 | /// The currently chosen key 31 | /// If the select key GUI should filter keys with a certain type 32 | /// If sort is true, this is the type of keys that will be shown. 33 | /// The currently chosen key 34 | public static string SelectKeyGUI(string currentKey, bool sort = false, LocalizedObjectType sortType = LocalizedObjectType.INVALID) 35 | { 36 | if(!ShouldShowKeySelector()) 37 | { 38 | return currentKey; 39 | } 40 | 41 | EditorGUILayout.BeginHorizontal(); 42 | GUILayout.Label("Smart Localization",EditorStyles.boldLabel); 43 | if (GUILayout.Button("Open", GUILayout.Width(50))) 44 | { 45 | SmartLocalizationWindow.ShowWindow(); 46 | } 47 | EditorGUILayout.EndHorizontal(); 48 | 49 | if(autoRefresh || parsedRootValues.Count == 0 || sortType != loadedObjectType) 50 | { 51 | RefreshList(sort, sortType); 52 | } 53 | 54 | EditorGUILayout.BeginHorizontal(); 55 | GUILayout.Label("Sort Mode: ",EditorStyles.miniLabel, GUILayout.Width(55)); 56 | if(sort) 57 | { 58 | GUILayout.Label(sortType.ToString() + " only.",EditorStyles.miniLabel); 59 | } 60 | else 61 | { 62 | GUILayout.Label("Showing ALL types",EditorStyles.miniLabel); 63 | } 64 | EditorGUILayout.EndHorizontal(); 65 | 66 | 67 | int index = parsedRootValues.IndexOf(currentKey); 68 | index = Mathf.Max(0, index); 69 | int newIndex = index; 70 | newIndex = EditorGUILayout.Popup(index, parsedRootValues.ToArray()); 71 | 72 | if(newIndex != index) 73 | { 74 | currentKey = parsedRootValues[newIndex]; 75 | } 76 | 77 | if(!autoRefresh && GUILayout.Button("Refresh list", GUILayout.Width(70))) 78 | { 79 | RefreshList(sort, sortType); 80 | } 81 | 82 | 83 | return currentKey; 84 | } 85 | 86 | /// 87 | /// Returns if the key selector should be shown. 88 | /// 89 | /// Returns if the key selector should be shown. 90 | public static bool ShouldShowKeySelector() 91 | { 92 | if(Application.isPlaying) 93 | { 94 | GUILayout.Label("Feature not available in play mode.",EditorStyles.miniLabel); 95 | return false; 96 | } 97 | 98 | if(!LocalizationWorkspace.Exists()) 99 | { 100 | GUILayout.Label("There is no Smart Localization workspace created",EditorStyles.miniLabel); 101 | //There is no language created 102 | if (GUILayout.Button("Create Workspace")) 103 | { 104 | LocalizationWorkspace.Create(); 105 | } 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | 112 | /// 113 | /// Refreshes the list containing the selectable keys 114 | /// 115 | /// If the refreshed list should be sorted and filtered with a certain key type 116 | /// The key type to use as filter 117 | public static void RefreshList(bool sort, LocalizedObjectType sortType) 118 | { 119 | if(!Application.isPlaying) 120 | { 121 | parsedRootValues.Clear(); 122 | 123 | Dictionary values = LanguageHandlerEditor.LoadParsedLanguageFile(null, true); 124 | if(sort) 125 | { 126 | loadedObjectType = sortType; 127 | foreach(KeyValuePair pair in values) 128 | { 129 | if(pair.Value.ObjectType == sortType) 130 | { 131 | parsedRootValues.Add(pair.Key); 132 | } 133 | } 134 | } 135 | else 136 | { 137 | //Use invalid for showing all 138 | loadedObjectType = LocalizedObjectType.INVALID; 139 | 140 | parsedRootValues.AddRange(values.Keys); 141 | } 142 | 143 | if(parsedRootValues.Count > 0) 144 | { 145 | parsedRootValues.Insert(0,"--- No key selected ---"); 146 | } 147 | else 148 | { 149 | parsedRootValues.Add("--- No localized keys available ---"); 150 | } 151 | } 152 | } 153 | } 154 | } //namespace SmartLocalization.Editor 155 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/FileSystem/FileUtility.cs: -------------------------------------------------------------------------------- 1 | // FileUtility.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | using UnityEngine; 6 | using System.IO; 7 | 8 | namespace SmartLocalization.Editor 9 | { 10 | /// 11 | /// Utility class for handling files 12 | /// 13 | public class FileUtility 14 | { 15 | #region Lookups 16 | /// 17 | /// Checks if a file exists 18 | /// 19 | /// The path to the file 20 | /// If the file exists 21 | public static bool Exists(string fullPath) 22 | { 23 | return File.Exists(fullPath); 24 | } 25 | 26 | /// 27 | /// Gets the file extension for the file at the specified path 28 | /// 29 | /// 30 | /// The file name without the extension. If the full name is for example hello.png, this parameter 31 | /// should be only "hello" 32 | /// 33 | /// 34 | /// The relative path to the folder containing the asset file 35 | /// relativeFolderPath should be relative to the project folder. Like: "Assets/MyTextures/". 36 | /// 37 | public static string GetFileExtension(string fileName, string relativeFolderPath) 38 | { 39 | string fullFolderPath = Application.dataPath + relativeFolderPath; 40 | 41 | 42 | if(!DirectoryUtility.Exists(fullFolderPath)) 43 | { 44 | return string.Empty; 45 | } 46 | 47 | string[] assetsInFolder = DirectoryUtility.GetFiles(fullFolderPath); 48 | 49 | foreach(string asset in assetsInFolder) 50 | { 51 | if(!asset.EndsWith(".meta")) //If this is not a .meta file 52 | { 53 | string currentFileName = RemoveExtension(asset); 54 | if(fileName == currentFileName) 55 | { 56 | return GetFileExtension(asset); 57 | } 58 | } 59 | } 60 | 61 | return string.Empty; 62 | } 63 | 64 | 65 | /// 66 | /// Gets a file extension of a file 67 | /// 68 | /// The full path to the file 69 | /// The file extension of a file 70 | public static string GetFileExtension(string fullPath) 71 | { 72 | return Path.GetExtension(fullPath); 73 | } 74 | 75 | /// Appends the relativePath to Application.dataPath 76 | public static bool ExistsRelative(string relativePath) 77 | { 78 | return Exists(Application.dataPath + relativePath); 79 | } 80 | 81 | 82 | /// 83 | /// Returns the file name without the extension 84 | /// 85 | /// The full path to the file 86 | /// The file name without the extension 87 | public static string RemoveExtension(string fullPath) 88 | { 89 | return Path.GetFileNameWithoutExtension(fullPath); 90 | } 91 | 92 | #endregion 93 | 94 | #region File Actions /Create / Delete / Read 95 | 96 | /// 97 | /// Deletes a file 98 | /// 99 | /// The full path to the file to delete 100 | /// If the file was deleted 101 | public static bool Delete(string fullPath) 102 | { 103 | try 104 | { 105 | File.Delete(fullPath); 106 | return true; 107 | } 108 | catch(System.Exception ex) 109 | { 110 | Debug.LogError("Failed to delete file! error:" + ex.Message); 111 | return false; 112 | } 113 | } 114 | 115 | /// 116 | /// Writes string data to a file 117 | /// 118 | /// The full path to the file to write to 119 | /// The string data to write 120 | /// If the writing operation was a success 121 | public static bool WriteToFile(string fullPath, string data) 122 | { 123 | try 124 | { 125 | File.WriteAllText(fullPath, data); 126 | return true; 127 | } 128 | catch(System.Exception ex) 129 | { 130 | Debug.LogError("Error! Could not save to file! error -" + ex.Message); 131 | return false; 132 | } 133 | } 134 | 135 | /// 136 | /// Reads string data from a file 137 | /// 138 | /// The full path to the file to read from 139 | /// The data that was read from the file 140 | /// If the operation was a success 141 | public static bool ReadFromFile(string fullPath, out string data) 142 | { 143 | if(!Exists(fullPath)) 144 | { 145 | data = null; 146 | Debug.LogError("FileUtility.cs: File to read from does not exist!"); 147 | return false; 148 | } 149 | 150 | try 151 | { 152 | data = File.ReadAllText(fullPath); 153 | return true; 154 | } 155 | catch(System.Exception ex) 156 | { 157 | data = null; 158 | Debug.LogError("FileUtility.cs: Could not load from file! error -" + ex.Message); 159 | return false; 160 | } 161 | } 162 | #endregion 163 | } 164 | }//SmartLocalization.Editor 165 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/EditorWindows/BulkUpdateWindow.cs: -------------------------------------------------------------------------------- 1 | // BulkUpdateWindow.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | using UnityEngine; 9 | using System.Collections; 10 | using UnityEditor; 11 | using System.Collections.Generic; 12 | 13 | public class BulkUpdateWindow : EditorWindow 14 | { 15 | public enum BulkUpdateMethod 16 | { 17 | Import, 18 | Export, 19 | } 20 | 21 | static readonly string exportFileName = "Languages"; 22 | static readonly string csvFileEnding = ".csv"; 23 | static readonly string xlsFileEnding = ".xls"; 24 | 25 | static readonly string[] availableFileFormats = {".csv", ".xls"}; 26 | 27 | [SerializeField] 28 | BulkUpdateMethod updateMedhod = BulkUpdateMethod.Export; 29 | CSVParser.Delimiter delimiter = CSVParser.Delimiter.COMMA; 30 | SmartLocalizationWindow parentWindow = null; 31 | int chosenFileFormat = 0; 32 | 33 | public void Initialize(BulkUpdateMethod updateMethod, SmartLocalizationWindow parentWindow) 34 | { 35 | this.updateMedhod = updateMethod; 36 | this.parentWindow = parentWindow; 37 | } 38 | 39 | #region GUI Methods 40 | 41 | void OnGUI() 42 | { 43 | if(this.parentWindow == null) 44 | { 45 | this.Close(); 46 | } 47 | 48 | if(LocalizationWindowUtility.ShouldShowWindow()) 49 | { 50 | if(updateMedhod == BulkUpdateMethod.Import) 51 | { 52 | GUILayout.Label ("Import all languages from single file", EditorStyles.boldLabel); 53 | ShowCommonGUI(); 54 | ShowImportGUI(); 55 | } 56 | else 57 | { 58 | GUILayout.Label ("Export all languages to single file", EditorStyles.boldLabel); 59 | ShowCommonGUI(); 60 | ShowExportGUI(); 61 | } 62 | } 63 | } 64 | 65 | void ShowImportGUI() 66 | { 67 | if(GUILayout.Button("Import")) 68 | { 69 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 70 | { 71 | string file = EditorUtility.OpenFilePanel("Select CSV file.", "", ""); 72 | if (file != null && file != "") 73 | { 74 | var values = CSVParser.Read(file, CSVParser.GetDelimiter(delimiter)); 75 | if(values.Count > 0) 76 | { 77 | LanguageHandlerEditor.BulkUpdateLanguageFiles(values); 78 | } 79 | } 80 | this.Close(); 81 | } 82 | else if(availableFileFormats[chosenFileFormat] == xlsFileEnding) 83 | { 84 | string file = EditorUtility.OpenFilePanel("Select XLS file.", "", ""); 85 | if (file != null && file != "") 86 | { 87 | var values = XLSExporter.Read(file); 88 | if(values.Count > 0) 89 | { 90 | LanguageHandlerEditor.BulkUpdateLanguageFiles(values); 91 | } 92 | } 93 | this.Close(); 94 | } 95 | else 96 | { 97 | Debug.LogError("BulkUpdateWindow: Unsupported import format!"); 98 | } 99 | 100 | if(parentWindow.translateLanguageWindow != null) 101 | { 102 | parentWindow.translateLanguageWindow.ReloadLanguage(); 103 | } 104 | } 105 | } 106 | 107 | void ShowExportGUI() 108 | { 109 | if(GUILayout.Button("Export")) 110 | { 111 | string folderPath = EditorUtility.OpenFolderPanel("Select folder to save to.", "", ""); 112 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 113 | { 114 | string fullPath = folderPath + "/" + exportFileName + csvFileEnding; 115 | CSVParser.Write(fullPath, CSVParser.GetDelimiter(delimiter), 116 | new List(LanguageHandlerEditor.LoadLanguageFile(null, true).Keys), LanguageHandlerEditor.LoadAllLanguageFiles()); 117 | 118 | Debug.Log("Exported CSV file to " + fullPath); 119 | this.Close(); 120 | } 121 | else if(availableFileFormats[chosenFileFormat] == xlsFileEnding) 122 | { 123 | string fullPath = folderPath + "/" + exportFileName + xlsFileEnding; 124 | XLSExporter.Write(fullPath, "Languages", 125 | new List(LanguageHandlerEditor.LoadLanguageFile(null, true).Keys), LanguageHandlerEditor.LoadAllLanguageFiles()); 126 | 127 | Debug.Log("Exported XLS file to " + fullPath); 128 | this.Close(); 129 | } 130 | else 131 | { 132 | Debug.LogError("BulkUpdateWindow: Unsupported export format!"); 133 | } 134 | } 135 | } 136 | 137 | void ShowCommonGUI() 138 | { 139 | chosenFileFormat = EditorGUILayout.Popup("File Format", chosenFileFormat, availableFileFormats); 140 | 141 | if(availableFileFormats[chosenFileFormat] == csvFileEnding) 142 | ShowCSVOptions(); 143 | } 144 | 145 | void ShowCSVOptions() 146 | { 147 | delimiter = (CSVParser.Delimiter)EditorGUILayout.EnumPopup("Delimiter", delimiter); 148 | } 149 | 150 | #endregion 151 | 152 | #region Show Windows 153 | public static BulkUpdateWindow ShowWindow(BulkUpdateMethod updateMethod, SmartLocalizationWindow parentWindow) 154 | { 155 | BulkUpdateWindow thisWindow = (BulkUpdateWindow)EditorWindow.GetWindow("Update Languages"); 156 | thisWindow.Initialize(updateMethod, parentWindow); 157 | 158 | return thisWindow; 159 | } 160 | #endregion 161 | } 162 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LanguageDataHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | namespace SmartLocalization{ 5 | internal class LanguageDataHandler 6 | { 7 | SortedDictionary loadedValuesDictionary; 8 | bool verboseLogging = false; 9 | SmartCultureInfo loadedCultureInfo; 10 | ILocalizedAssetLoader assetLoader; 11 | 12 | internal SmartCultureInfo LoadedCulture 13 | { 14 | get 15 | { 16 | return loadedCultureInfo; 17 | } 18 | set 19 | { 20 | loadedCultureInfo = value; 21 | } 22 | } 23 | 24 | internal bool VerboseLogging{ 25 | get 26 | { 27 | return verboseLogging; 28 | } 29 | set 30 | { 31 | verboseLogging = value; 32 | } 33 | } 34 | 35 | internal SortedDictionary LoadedValuesDictionary 36 | { 37 | get 38 | { 39 | return loadedValuesDictionary; 40 | } 41 | set 42 | { 43 | loadedValuesDictionary = value; 44 | } 45 | } 46 | 47 | internal ILocalizedAssetLoader AssetLoader 48 | { 49 | get 50 | { 51 | if(assetLoader == null) 52 | { 53 | assetLoader = new RuntimeLocalizedAssetLoader(); 54 | } 55 | 56 | return assetLoader; 57 | } 58 | set 59 | { 60 | assetLoader = value; 61 | } 62 | } 63 | 64 | internal LanguageDataHandler() 65 | { 66 | loadedValuesDictionary = new SortedDictionary(); 67 | } 68 | 69 | internal bool Load(string resxData) 70 | { 71 | var languageValues = LoadLanguageDictionary(resxData); 72 | if(languageValues != null && languageValues.Count > 0) 73 | { 74 | loadedValuesDictionary = languageValues; 75 | 76 | if(verboseLogging) 77 | { 78 | Debug.Log("Successfully loaded language"); 79 | } 80 | 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | internal bool Append(string resxData) 87 | { 88 | var languageValues = LoadLanguageDictionary(resxData); 89 | if(languageValues != null && languageValues.Count > 0) 90 | { 91 | foreach(var pair in languageValues) 92 | { 93 | if(loadedValuesDictionary.ContainsKey(pair.Key)) 94 | { 95 | loadedValuesDictionary[pair.Key] = pair.Value; 96 | } 97 | else 98 | { 99 | loadedValuesDictionary.Add(pair.Key, pair.Value); 100 | } 101 | } 102 | 103 | if(verboseLogging) 104 | { 105 | Debug.Log("Successfully appended language with " + languageValues.Count + " values"); 106 | } 107 | 108 | return true; 109 | } 110 | return false; 111 | } 112 | 113 | internal List GetKeysWithinCategory(string category) 114 | { 115 | if(string.IsNullOrEmpty(category) || loadedValuesDictionary == null) 116 | { 117 | return new List(); 118 | } 119 | 120 | var categoryList = new List(); 121 | foreach(var pair in loadedValuesDictionary) 122 | { 123 | if(pair.Key.StartsWith(category)) 124 | { 125 | categoryList.Add(pair.Key); 126 | } 127 | } 128 | 129 | return categoryList; 130 | } 131 | 132 | internal List GetAllKeys() 133 | { 134 | return new List(loadedValuesDictionary.Keys); 135 | } 136 | 137 | internal LocalizedObject GetLocalizedObject(string key) 138 | { 139 | LocalizedObject localizedObject; 140 | loadedValuesDictionary.TryGetValue(key, out localizedObject); 141 | return localizedObject; 142 | } 143 | 144 | internal string GetTextValue(string key) 145 | { 146 | LocalizedObject localizedObject = GetLocalizedObject(key); 147 | 148 | if(localizedObject != null) 149 | { 150 | return localizedObject.TextValue; 151 | } 152 | 153 | if(VerboseLogging) 154 | { 155 | Debug.LogError("LanguageManager.cs: Invalid Key:" + key + ". Could not get language value."); 156 | } 157 | 158 | return null; 159 | } 160 | 161 | internal T GetAsset(string key) where T : UnityEngine.Object 162 | { 163 | LocalizedObject localizedObject = GetLocalizedObject(key); 164 | 165 | if(localizedObject != null) 166 | { 167 | return AssetLoader.LoadAsset(key, CheckLanguageOverrideCode(localizedObject)); 168 | } 169 | 170 | if(VerboseLogging) 171 | { 172 | Debug.LogError("Could not get asset with key: " + key + " as asset type:" + typeof(T).ToString()); 173 | } 174 | 175 | return default (T); 176 | } 177 | 178 | internal bool HasKey(string key) 179 | { 180 | return GetLocalizedObject(key) != null; 181 | } 182 | 183 | SortedDictionary LoadLanguageDictionary(string resxData) 184 | { 185 | if(string.IsNullOrEmpty(resxData)) 186 | { 187 | return null; 188 | } 189 | 190 | return LanguageParser.LoadLanguage(resxData); 191 | } 192 | 193 | /// 194 | /// Helper method that checks and gets the language override code of the object is overridden. 195 | /// If it's not - it will return the currently loaded language. 196 | /// 197 | string CheckLanguageOverrideCode(LocalizedObject localizedObject) 198 | { 199 | if(localizedObject == null) 200 | { 201 | return loadedCultureInfo.languageCode; 202 | } 203 | 204 | string objectLanguage = localizedObject.OverrideLocalizedObject ? localizedObject.OverrideObjectLanguageCode : loadedCultureInfo.languageCode; 205 | if(string.IsNullOrEmpty(objectLanguage)) 206 | { 207 | objectLanguage = loadedCultureInfo.languageCode; 208 | } 209 | 210 | return objectLanguage; 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/FileSystem/CSVParser.cs: -------------------------------------------------------------------------------- 1 | // CSVParser.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.IO; 8 | using CsvHelper; 9 | 10 | namespace SmartLocalization.Editor 11 | { 12 | /// 13 | /// A CSV Parser 14 | /// 15 | public static class CSVParser 16 | { 17 | /// 18 | /// The delimiter type for CSV 19 | /// 20 | public enum Delimiter 21 | { 22 | /// COMMA 23 | COMMA, 24 | /// SEMI_COLON 25 | SEMI_COLON, 26 | /// TAB 27 | TAB, 28 | /// VERTICAL_BAR 29 | VERTICAL_BAR, 30 | /// CARET 31 | CARET, 32 | } 33 | 34 | /// 35 | /// Gets the actual delimiter char based on the CSVDelimiter type 36 | /// 37 | /// The delimiter type 38 | /// the delimiter 39 | public static char GetDelimiter(Delimiter delimiter) 40 | { 41 | switch(delimiter) 42 | { 43 | case Delimiter.COMMA: 44 | return ','; 45 | case Delimiter.SEMI_COLON: 46 | return ';'; 47 | case Delimiter.TAB: 48 | return '\t'; 49 | case Delimiter.VERTICAL_BAR: 50 | return '|'; 51 | case Delimiter.CARET: 52 | return '^'; 53 | default: 54 | return ','; 55 | } 56 | } 57 | 58 | /// 59 | /// Write the csv to file 60 | /// 61 | /// The destination path 62 | /// The delimiter to separate values with 63 | /// The Values 64 | public static void Write(string path, char delimiter, List> input) 65 | { 66 | var csvContent = WriteToString(delimiter, input); 67 | FileUtility.WriteToFile(path, csvContent); 68 | } 69 | 70 | /// 71 | /// Write a combined CSV to File 72 | /// 73 | /// The destination path 74 | /// The delimiter to separate values with 75 | public static void Write(string path, char delimiter, List keys, Dictionary> languages) 76 | { 77 | var csvContent = WriteToString(delimiter, keys, languages); 78 | FileUtility.WriteToFile(path, csvContent); 79 | } 80 | 81 | internal static string WriteToString(char delimiter, List> input) 82 | { 83 | string csvContent = null; 84 | using (var sw = new StringWriter()) 85 | { 86 | var csv = new CsvWriter(sw); 87 | csv.Configuration.Delimiter = delimiter.ToString(); 88 | foreach (List list in input) 89 | { 90 | foreach (string value in list) 91 | { 92 | csv.WriteField(value); 93 | } 94 | csv.NextRecord(); 95 | } 96 | csvContent = sw.ToString(); 97 | } 98 | return csvContent; 99 | } 100 | 101 | internal static string WriteToString(char delimiter, List keys, Dictionary> languages) 102 | { 103 | string csvContent = null; 104 | keys.Sort(); 105 | using (var sw = new StringWriter()) 106 | { 107 | var csv = new CsvWriter(sw); 108 | csv.Configuration.Delimiter = delimiter.ToString(); 109 | csv.WriteField(""); 110 | foreach (var language in languages.Keys) 111 | { 112 | csv.WriteField(language); 113 | } 114 | 115 | csv.NextRecord(); 116 | 117 | foreach (var key in keys) 118 | { 119 | csv.WriteField(key); 120 | 121 | foreach (var pair in languages) 122 | { 123 | csv.WriteField(pair.Value.ContainsKey(key) ? pair.Value[key] : ""); 124 | } 125 | 126 | csv.NextRecord(); 127 | } 128 | csvContent = sw.ToString(); 129 | } 130 | return csvContent; 131 | } 132 | 133 | /// 134 | /// Read a csv file 135 | /// 136 | /// The path to the file 137 | /// The delimiter used in the file 138 | /// The parsed csv values 139 | public static List> Read(string path, char delimiter) 140 | { 141 | string csvContent = null; 142 | if(FileUtility.Exists(path)) 143 | { 144 | FileUtility.ReadFromFile(path, out csvContent); 145 | return ReadFromString(csvContent, delimiter); 146 | } 147 | 148 | throw new System.IO.FileNotFoundException("CSV file does not exist!", path); 149 | } 150 | 151 | internal static List> ReadFromString(string csvContent, char delimiter) 152 | { 153 | if(string.IsNullOrEmpty(csvContent)) 154 | { 155 | throw new System.ArgumentException("Empty CSV content. Cannot read CSV", "csvContent"); 156 | } 157 | 158 | var rows = new List>(); 159 | using(StringReader reader = new StringReader(csvContent)) 160 | { 161 | var config = new CsvHelper.Configuration.CsvConfiguration(); 162 | config.Delimiter = delimiter.ToString(); 163 | var parser = new CsvParser(reader, config); 164 | 165 | while (true) 166 | { 167 | var row = parser.Read(); 168 | if( row == null ) 169 | { 170 | break; 171 | } 172 | rows.Add(new List(row)); 173 | } 174 | } 175 | 176 | return rows; 177 | } 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/LocalizationSystem/SmartCultureInfo.cs: -------------------------------------------------------------------------------- 1 | // SmartCultureInfo.cs 2 | // 3 | // Written by Niklas Borglund and Jakob Hillerström 4 | // 5 | 6 | namespace SmartLocalization 7 | { 8 | using System.Collections.Generic; 9 | using System.Xml.Serialization; 10 | using UnityEngine; 11 | 12 | /// 13 | /// A Serializeable Collection containing a list of Smart Culture Infos 14 | /// 15 | [XmlRoot("SmartCultureCollections"), System.Serializable] 16 | public class SmartCultureInfoCollection 17 | { 18 | #region Members 19 | 20 | public const int LatestVersion = 4; 21 | 22 | [XmlElement(ElementName = "version")] 23 | public int version = 0; 24 | 25 | [XmlArray("CultureInfos"), XmlArrayItem("CultureInfo")] 26 | public List cultureInfos = new List(); 27 | 28 | #endregion 29 | 30 | #region Add CultureInfo 31 | 32 | /// 33 | /// Removes a culture info from the collection 34 | /// 35 | /// The reference to the culture info to remove 36 | public void RemoveCultureInfo(SmartCultureInfo cultureInfo) 37 | { 38 | if (cultureInfo == null) 39 | { 40 | Debug.LogError("Cannot remove a SmartCultureInfo that's null!"); 41 | return; 42 | } 43 | 44 | if (!cultureInfos.Remove(cultureInfo)) 45 | { 46 | Debug.LogError("Failed to remove cultureInfo!"); 47 | } 48 | } 49 | 50 | /// 51 | /// Adds a culture info to the collection 52 | /// 53 | /// The culture info to add 54 | public void AddCultureInfo(SmartCultureInfo cultureInfo) 55 | { 56 | if (cultureInfo == null) 57 | { 58 | Debug.LogWarning("Cannot add a SmartCultureInfo that's null!"); 59 | return; 60 | } 61 | 62 | cultureInfos.Add(cultureInfo); 63 | } 64 | 65 | #endregion 66 | 67 | #region Serialization 68 | 69 | /// 70 | /// Deserializes and creates a SmartCultureInfoCollection from a text asset. 71 | /// 72 | /// The textasset containing the serialized xml data 73 | /// The deserialized SmartCultureInfoCollection 74 | public static SmartCultureInfoCollection Deserialize(TextAsset xmlFile) 75 | { 76 | return SmartCultureInfoCollectionDeserializer.Deserialize(xmlFile); 77 | } 78 | 79 | #endregion 80 | 81 | #region Lookups 82 | 83 | /// 84 | /// Finds a culture info from the list. Compares the English name and the language code 85 | /// 86 | /// The culture info to find 87 | /// The culture info, returns null if not found 88 | public SmartCultureInfo FindCulture(SmartCultureInfo cultureInfo) 89 | { 90 | if (cultureInfo == null) 91 | { 92 | return null; 93 | } 94 | 95 | return cultureInfos.Find(c => 96 | (c.englishName.ToLower()== cultureInfo.englishName.ToLower())&& 97 | (c.languageCode.ToLower()== cultureInfo.languageCode.ToLower())); 98 | } 99 | 100 | /// 101 | /// Finds a culture info from the list by the ISO-639 language code. 102 | /// 103 | /// The ISO-639 language code 104 | /// The found SmartCultureInfo. Returns null if nothing was found. 105 | public SmartCultureInfo FindCulture(string languageCode) 106 | { 107 | if (string.IsNullOrEmpty(languageCode)) 108 | { 109 | return null; 110 | } 111 | 112 | return cultureInfos.Find(c => 113 | (c.languageCode.ToLower()== languageCode.ToLower())); 114 | } 115 | 116 | /// 117 | /// Checks if a specific culture info is in this collection 118 | /// 119 | /// The culture info to check 120 | /// If the specified culture info was in the collection 121 | public bool IsCultureInCollection(SmartCultureInfo cultureInfo) 122 | { 123 | return FindCulture(cultureInfo)!= null; 124 | } 125 | 126 | #endregion 127 | } 128 | 129 | /// 130 | /// A serializable class containing the basic information about a culture. 131 | /// 132 | [System.Serializable] 133 | public class SmartCultureInfo 134 | { 135 | /// The language code of the culture. (e.g. sv, en, de, fr) 136 | public string languageCode = null; 137 | /// The English name of the culture 138 | public string englishName = null; 139 | /// The native name of the culture 140 | public string nativeName = null; 141 | /// If the language is written and read from right to left 142 | public bool isRightToLeft = false; 143 | 144 | /// Creates a new instance of SmartCultureInfo 145 | public SmartCultureInfo() { } 146 | /// 147 | /// Creates a new instance of SmartCultureInfo 148 | /// 149 | /// The language code of the culture. (e.g. sv, en, de, fr) 150 | /// The English name of the culture 151 | /// The native name of the culture 152 | public SmartCultureInfo(string languageCode, string englishName, string nativeName, bool isRightToLeft) 153 | { 154 | this.languageCode = languageCode; 155 | this.englishName = englishName; 156 | this.nativeName = nativeName; 157 | this.isRightToLeft = isRightToLeft; 158 | } 159 | 160 | public override string ToString() 161 | { 162 | return string.Format(@"[SmartCultureInfo LanguageCode=""{0}"" EnglishName={1} NativeName={2} IsRightToLeft={3}]", 163 | languageCode, englishName, nativeName, isRightToLeft.ToString()); 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/README.txt: -------------------------------------------------------------------------------- 1 | README 2 | ====== 3 | 4 | List control for Unity allowing editor developers to add reorderable list controls to 5 | their GUIs. Supports generic lists and serialized property arrays, though additional 6 | collection types can be supported by implementing `Rotorz.ReorderableList.IReorderableListAdaptor`. 7 | 8 | Use of this source code is governed by a BSD-style license that can be found in 9 | the LICENSE file. DO NOT contribute to this project unless you accept the terms of the 10 | contribution agreement. 11 | 12 | ![screenshot](https://bitbucket.org/rotorz/reorderable-list-editor-field-for-unity/raw/master/screenshot.png) 13 | 14 | Features 15 | -------- 16 | 17 | - Drag and drop reordering! 18 | - Easily customized using flags. 19 | - Adaptors for `IList` and `SerializedProperty`. 20 | - Subscribe to add/remove item events. 21 | - Supports mixed item heights. 22 | - Disable drag and/or removal on per-item basis. 23 | - Styles can be overriden on per-list basis if desired. 24 | - Subclass list control to override context menu. 25 | - User guide (Asset Path/Support/User Guide.pdf). 26 | - API reference documentation (Asset Path/Support/API Reference.chm). 27 | 28 | Installing scripts 29 | ------------------ 30 | 31 | This control can be added to your project by importing the Unity package which 32 | contains a compiled class library (DLL). This can be used by C# and UnityScript 33 | developers. 34 | 35 | [Download RotorzReorderableList_v0.2.4 Package (requires Unity 4.2.1+)]() 36 | 37 | If you would prefer to use the non-compiled source code version in your project, 38 | copy the contents of this repository somewhere into your project. 39 | 40 | **Note to UnityScript (*.js) developers:** 41 | 42 | UnityScript will not work with the source code version of this project unless 43 | the contents of this repository is placed at the path "Assets/Plugins/ReorderableList" 44 | due to compilation ordering. 45 | 46 | Example 1: Serialized array of strings (C#) 47 | ------------------------------------------- 48 | 49 | :::csharp 50 | SerializedProperty _wishlistProperty; 51 | SerializedProperty _pointsProperty; 52 | 53 | void OnEnable() { 54 | _wishlistProperty = serializedObject.FindProperty("wishlist"); 55 | _pointsProperty = serializedObject.FindProperty("points"); 56 | } 57 | 58 | public override void OnInspectorGUI() { 59 | serializedObject.Update(); 60 | 61 | ReorderableListGUI.Title("Wishlist"); 62 | ReorderableListGUI.ListField(_wishlistProperty); 63 | 64 | ReorderableListGUI.Title("Points"); 65 | ReorderableListGUI.ListField(_pointsProperty, ReorderableListFlags.ShowIndices); 66 | 67 | serializedObject.ApplyModifiedProperties(); 68 | } 69 | 70 | Example 2: List of strings (UnityScript) 71 | ---------------------------------------- 72 | 73 | :::javascript 74 | var yourList:List. = new List.(); 75 | 76 | function OnGUI() { 77 | ReorderableListGUI.ListField(yourList, CustomListItem, DrawEmpty); 78 | } 79 | 80 | function CustomListItem(position:Rect, itemValue:String):String { 81 | // Text fields do not like null values! 82 | if (itemValue == null) 83 | itemValue = ''; 84 | return EditorGUI.TextField(position, itemValue); 85 | } 86 | 87 | function DrawEmpty() { 88 | GUILayout.Label('No items in list.', EditorStyles.miniLabel); 89 | } 90 | 91 | Refer to API reference for further examples! 92 | 93 | Submission to the Unity Asset Store 94 | ----------------------------------- 95 | 96 | If you wish to include this asset as part of a package for the asset store, please 97 | include the latest package version as-is to avoid conflict issues in user projects. 98 | It is important that license and documentation files are included and remain intact. 99 | 100 | **To include a modified version within your package:** 101 | 102 | - Ensure that license and documentation files are included and remain intact. It should 103 | be clear that these relate to the reorderable list field library. 104 | 105 | - Copyright and license information must remain intact in source files. 106 | 107 | - Change the namespace `Rotorz.ReorderableList` to something unique and DO NOT use the 108 | name "Rotorz". For example, `YourName.ReorderableList` or `YourName.Internal.ReorderableList`. 109 | 110 | - Place files somewhere within your own asset folder to avoid causing conflicts with 111 | other assets which make use of this project. 112 | 113 | Useful links 114 | ------------ 115 | 116 | - [Rotorz Website]() 117 | 118 | Contribution Agreement 119 | ---------------------- 120 | 121 | This project is licensed under the BSD license (see LICENSE). To be in the best 122 | position to enforce these licenses the copyright status of this project needs to 123 | be as simple as possible. To achieve this the following terms and conditions 124 | must be met: 125 | 126 | - All contributed content (including but not limited to source code, text, 127 | image, videos, bug reports, suggestions, ideas, etc.) must be the 128 | contributors own work. 129 | 130 | - The contributor disclaims all copyright and accepts that their contributed 131 | content will be released to the [public domain](). 132 | 133 | - The act of submitting a contribution indicates that the contributor agrees 134 | with this agreement. This includes (but is not limited to) pull requests, issues, 135 | tickets, e-mails, newsgroups, blogs, forums, etc. 136 | 137 | ### Disclaimer 138 | 139 | External content linked in the above text are for convienence purposes only and 140 | do not contribute to the agreement in any way. Linked content should be digested 141 | under the readers discretion. 142 | -------------------------------------------------------------------------------- /Assets/SmartLocalization/Scripts/Editor/ThirdParty/Rotorz/Reorderable List Field/Editor/SerializedPropertyAdaptor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2013 Rotorz Limited. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | using UnityEngine; 6 | using UnityEditor; 7 | 8 | using System; 9 | 10 | namespace SmartLocalization.ReorderableList { 11 | 12 | /// 13 | /// Reorderable list adaptor for serialized array property. 14 | /// 15 | /// 16 | /// This adaptor can be subclassed to add special logic to item height calculation. 17 | /// You may want to implement a custom adaptor class where specialised functionality 18 | /// is needed. 19 | /// 20 | public class SerializedPropertyAdaptor : IReorderableListAdaptor { 21 | 22 | private SerializedProperty _arrayProperty; 23 | 24 | /// 25 | /// Fixed height of each list item. 26 | /// 27 | /// 28 | /// Non-zero value overrides property drawer height calculation 29 | /// which is more efficient. 30 | /// 31 | public float fixedItemHeight; 32 | 33 | /// 34 | /// Gets element from list. 35 | /// 36 | /// Zero-based index of element. 37 | /// 38 | /// Serialized property wrapper for array element. 39 | /// 40 | public SerializedProperty this[int index] { 41 | get { return _arrayProperty.GetArrayElementAtIndex(index); } 42 | } 43 | 44 | /// 45 | /// Gets the underlying serialized array property. 46 | /// 47 | public SerializedProperty arrayProperty { 48 | get { return _arrayProperty; } 49 | } 50 | 51 | #region Construction 52 | 53 | /// 54 | /// Initializes a new instance of . 55 | /// 56 | /// Serialized property for entire array. 57 | /// Non-zero height overrides property drawer height calculation. 58 | public SerializedPropertyAdaptor(SerializedProperty arrayProperty, float fixedItemHeight) { 59 | if (arrayProperty == null) 60 | throw new ArgumentNullException("Array property was null."); 61 | if (!arrayProperty.isArray) 62 | throw new InvalidOperationException("Specified serialized propery is not an array."); 63 | 64 | this._arrayProperty = arrayProperty; 65 | this.fixedItemHeight = fixedItemHeight; 66 | } 67 | 68 | /// 69 | /// Initializes a new instance of . 70 | /// 71 | /// Serialized property for entire array. 72 | public SerializedPropertyAdaptor(SerializedProperty arrayProperty) : this(arrayProperty, 0f) { 73 | } 74 | 75 | #endregion 76 | 77 | #region IReorderableListAdaptor - Implementation 78 | 79 | /// 80 | public int Count { 81 | get { return _arrayProperty.arraySize; } 82 | } 83 | 84 | /// 85 | public virtual bool CanDrag(int index) { 86 | return true; 87 | } 88 | /// 89 | public virtual bool CanRemove(int index) { 90 | return true; 91 | } 92 | 93 | public virtual bool CanDraw(int index) { 94 | return true; 95 | } 96 | 97 | /// 98 | public void Add() { 99 | int newIndex = _arrayProperty.arraySize; 100 | ++_arrayProperty.arraySize; 101 | ResetValue(_arrayProperty.GetArrayElementAtIndex(newIndex)); 102 | } 103 | /// 104 | public void Insert(int index) { 105 | _arrayProperty.InsertArrayElementAtIndex(index); 106 | ResetValue(_arrayProperty.GetArrayElementAtIndex(index)); 107 | } 108 | /// 109 | public void Duplicate(int index) { 110 | _arrayProperty.InsertArrayElementAtIndex(index); 111 | } 112 | /// 113 | public void Remove(int index) { 114 | _arrayProperty.DeleteArrayElementAtIndex(index); 115 | } 116 | /// 117 | public void Move(int sourceIndex, int destIndex) { 118 | if (destIndex > sourceIndex) 119 | --destIndex; 120 | _arrayProperty.MoveArrayElement(sourceIndex, destIndex); 121 | } 122 | /// 123 | public void Clear() { 124 | _arrayProperty.ClearArray(); 125 | } 126 | 127 | /// 128 | public virtual void DrawItem(Rect position, int index) { 129 | EditorGUI.PropertyField(position, this[index], GUIContent.none, false); 130 | } 131 | 132 | /// 133 | public virtual float GetItemHeight(int index) { 134 | return fixedItemHeight != 0f 135 | ? fixedItemHeight 136 | : EditorGUI.GetPropertyHeight(this[index], GUIContent.none, false) 137 | ; 138 | } 139 | 140 | #endregion 141 | 142 | #region Methods 143 | 144 | /// 145 | /// Reset value of array element. 146 | /// 147 | /// Serializd property for array element. 148 | private void ResetValue(SerializedProperty element) { 149 | switch (element.type) { 150 | case "string": 151 | element.stringValue = ""; 152 | break; 153 | case "Vector2f": 154 | element.vector2Value = Vector2.zero; 155 | break; 156 | case "Vector3f": 157 | element.vector3Value = Vector3.zero; 158 | break; 159 | case "Rectf": 160 | element.rectValue = new Rect(); 161 | break; 162 | case "Quaternionf": 163 | element.quaternionValue = Quaternion.identity; 164 | break; 165 | case "int": 166 | element.intValue = 0; 167 | break; 168 | case "float": 169 | element.floatValue = 0f; 170 | break; 171 | case "UInt8": 172 | element.boolValue = false; 173 | break; 174 | case "ColorRGBA": 175 | element.colorValue = Color.black; 176 | break; 177 | 178 | default: 179 | if (element.type.StartsWith("PPtr")) 180 | element.objectReferenceValue = null; 181 | break; 182 | } 183 | } 184 | 185 | #endregion 186 | 187 | } 188 | 189 | } -------------------------------------------------------------------------------- /Assets/SmartLocalization/Resources/EmptyResourceHeader.txt: -------------------------------------------------------------------------------- 1 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | text/microsoft-resx 108 | 109 | 110 | 2.0 111 | 112 | 113 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 114 | 115 | 116 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 117 | -------------------------------------------------------------------------------- /Assets/Tests/Editor/CSVParserTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | 6 | namespace SmartLocalization.Editor 7 | { 8 | [TestFixture] 9 | public class CSVParserTests 10 | { 11 | static string TestCSVDataWithComma = "\"TestKey\",\"TestValue\""; 12 | static string TestCSVDataWithSemiColon = "\"TestKey\";\"TestValue\""; 13 | static string TestCSVDataWithTab = "\"TestKey\"\t\"TestValue\""; 14 | static string TestCSVDataWithVerticalBar = "\"TestKey\"|\"TestValue\""; 15 | static string TestCSVDataWithCaret = "\"TestKey\"^\"TestValue\""; 16 | static string TestCSVDataWithQuoteMarks = "\"TestKey\",\"TestValue\"\"\"\"\""; 17 | 18 | List> GetCsvWriteTestData(string testKey, string testValue) 19 | { 20 | List> csvData = new List>(); 21 | List firstLine = new List(); 22 | firstLine.Add(testKey); 23 | firstLine.Add(testValue); 24 | csvData.Add(firstLine); 25 | return csvData; 26 | } 27 | 28 | [Test] 29 | public void TestCSVWriteWithComma_Success() 30 | { 31 | var testData = GetCsvWriteTestData("TestKey", "TestValue"); 32 | string csv = CSVParser.WriteToString(CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA), testData); 33 | var actualData = CSVParser.ReadFromString(csv, CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA)); 34 | Assert.AreEqual(testData[0][0], actualData[0][0]); 35 | Assert.AreEqual(testData[0][1], actualData[0][1]); 36 | } 37 | 38 | [Test] 39 | public void TestCSVReadWithWrongDelimiter_Failure() 40 | { 41 | var result = CSVParser.ReadFromString(TestCSVDataWithCaret, CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA)); 42 | Assert.AreEqual(1, result.Count); 43 | Assert.AreNotEqual(2, result[0].Count); 44 | Assert.AreNotEqual("TestKey", result[0][0]); 45 | } 46 | 47 | [Test] 48 | public void TestCSVWriteWithSemiColon_Success() 49 | { 50 | var testData = GetCsvWriteTestData("TestKey", "TestValue"); 51 | string csv = CSVParser.WriteToString(CSVParser.GetDelimiter(CSVParser.Delimiter.SEMI_COLON), testData); 52 | var actualData = CSVParser.ReadFromString(csv, CSVParser.GetDelimiter(CSVParser.Delimiter.SEMI_COLON)); 53 | Assert.AreEqual(testData[0][0], actualData[0][0]); 54 | Assert.AreEqual(testData[0][1], actualData[0][1]); 55 | } 56 | 57 | [Test] 58 | public void TestCSVWriteWithTab_Success() 59 | { 60 | var testData = GetCsvWriteTestData("TestKey", "TestValue"); 61 | string csv = CSVParser.WriteToString(CSVParser.GetDelimiter(CSVParser.Delimiter.TAB), testData); 62 | var actualData = CSVParser.ReadFromString(csv, CSVParser.GetDelimiter(CSVParser.Delimiter.TAB)); 63 | Assert.AreEqual(testData[0][0], actualData[0][0]); 64 | Assert.AreEqual(testData[0][1], actualData[0][1]); 65 | } 66 | 67 | [Test] 68 | public void TestCSVWriteWithVerticalBar_Success() 69 | { 70 | var testData = GetCsvWriteTestData("TestKey", "TestValue"); 71 | string csv = CSVParser.WriteToString(CSVParser.GetDelimiter(CSVParser.Delimiter.VERTICAL_BAR), testData); 72 | var actualData = CSVParser.ReadFromString(csv, CSVParser.GetDelimiter(CSVParser.Delimiter.VERTICAL_BAR)); 73 | Assert.AreEqual(testData[0][0], actualData[0][0]); 74 | Assert.AreEqual(testData[0][1], actualData[0][1]); 75 | } 76 | 77 | [Test] 78 | public void TestCSVWriteWithCaret_Success() 79 | { 80 | var testData = GetCsvWriteTestData("TestKey", "TestValue"); 81 | string csv = CSVParser.WriteToString(CSVParser.GetDelimiter(CSVParser.Delimiter.CARET), testData); 82 | var actualData = CSVParser.ReadFromString(csv, CSVParser.GetDelimiter(CSVParser.Delimiter.CARET)); 83 | Assert.AreEqual(testData[0][0], actualData[0][0]); 84 | Assert.AreEqual(testData[0][1], actualData[0][1]); 85 | } 86 | 87 | [Test] 88 | public void TestCSVReadWithComma_Success() 89 | { 90 | var result = CSVParser.ReadFromString(TestCSVDataWithComma, CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA)); 91 | Assert.AreEqual(1, result.Count); 92 | Assert.AreEqual(2, result[0].Count); 93 | Assert.AreEqual("TestKey", result[0][0]); 94 | Assert.AreEqual("TestValue", result[0][1]); 95 | } 96 | 97 | [Test] 98 | public void TestCSVReadWithSemiColon_Success() 99 | { 100 | var result = CSVParser.ReadFromString(TestCSVDataWithSemiColon, CSVParser.GetDelimiter(CSVParser.Delimiter.SEMI_COLON)); 101 | Assert.AreEqual(1, result.Count); 102 | Assert.AreEqual(2, result[0].Count); 103 | Assert.AreEqual("TestKey", result[0][0]); 104 | Assert.AreEqual("TestValue", result[0][1]); 105 | } 106 | 107 | [Test] 108 | public void TestCSVReadWithTab_Success() 109 | { 110 | var result = CSVParser.ReadFromString(TestCSVDataWithTab, CSVParser.GetDelimiter(CSVParser.Delimiter.TAB)); 111 | Assert.AreEqual(1, result.Count); 112 | Assert.AreEqual(2, result[0].Count); 113 | Assert.AreEqual("TestKey", result[0][0]); 114 | Assert.AreEqual("TestValue", result[0][1]); 115 | } 116 | 117 | [Test] 118 | public void TestCSVReadWithVerticalBar_Success() 119 | { 120 | var result = CSVParser.ReadFromString(TestCSVDataWithVerticalBar, CSVParser.GetDelimiter(CSVParser.Delimiter.VERTICAL_BAR)); 121 | Assert.AreEqual(1, result.Count); 122 | Assert.AreEqual(2, result[0].Count); 123 | Assert.AreEqual("TestKey", result[0][0]); 124 | Assert.AreEqual("TestValue", result[0][1]); 125 | } 126 | 127 | [Test] 128 | public void TestCSVReadWithCaret_Success() 129 | { 130 | var result = CSVParser.ReadFromString(TestCSVDataWithCaret, CSVParser.GetDelimiter(CSVParser.Delimiter.CARET)); 131 | Assert.AreEqual(1, result.Count); 132 | Assert.AreEqual(2, result[0].Count); 133 | Assert.AreEqual("TestKey", result[0][0]); 134 | Assert.AreEqual("TestValue", result[0][1]); 135 | } 136 | 137 | [Test] 138 | public void TestCSVReadWithExtraQuoteMarks_Success() 139 | { 140 | var result = CSVParser.ReadFromString(TestCSVDataWithQuoteMarks, CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA)); 141 | Assert.AreEqual(1, result.Count); 142 | Assert.AreEqual(2, result[0].Count); 143 | Assert.AreEqual("TestKey", result[0][0]); 144 | Assert.AreEqual("TestValue\"\"", result[0][1]); 145 | } 146 | 147 | [Test] 148 | public void TestCSVReadEmptyContent_ArgumentException() 149 | { 150 | Assert.That(()=> CSVParser.ReadFromString(null, CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA)), Throws.ArgumentException); 151 | } 152 | 153 | [Test] 154 | public void TestCSVRead_FileNotFoundException() 155 | { 156 | Assert.Throws(()=> CSVParser.Read(string.Empty, CSVParser.GetDelimiter(CSVParser.Delimiter.COMMA))); 157 | } 158 | } 159 | } --------------------------------------------------------------------------------