Localization System Implementation
Implement multi-language support for international releases of your RPG.
Localization Overview
Localization makes your game accessible to players worldwide by adapting content for different languages and cultures.
📝 Text Translation
UI text, dialogue, item descriptions, and narrative content
🎵 Audio Localization
Voice acting, sound effects, and music for different regions
🎨 Cultural Adaptation
Images, symbols, and UI layout for different cultures
📱 Technical Implementation
Font support, text direction, and dynamic UI resizing
Setting Up Localization System
Step 1: Create Localization Manager
Build the core localization system:
1"keyword">using System.Collections.Generic;
2"keyword">using UnityEngine;
3"keyword">using System.IO;
4
5"keyword">public "keyword">class LocalizationManager : MonoBehaviour
6{
7 "keyword">public "keyword">static LocalizationManager Instance;
8
9 [Header("Localization Settings")]
10 "keyword">public SystemLanguage currentLanguage = SystemLanguage.English;
11 "keyword">public List<SystemLanguage> supportedLanguages = "keyword">new List<SystemLanguage>();
12 "keyword">public "keyword">bool detectSystemLanguage = true;
13
14 [Header("File Settings")]
15 "keyword">public "keyword">string localizationFolder = "Localization";
16 "keyword">public "keyword">string fileExtension = ".json";
17
18 "keyword">private Dictionary<"keyword">string, "keyword">string> localizedText = "keyword">new Dictionary<"keyword">string, "keyword">string>();
19 "keyword">private Dictionary<SystemLanguage, Dictionary<"keyword">string, "keyword">string>> allLanguageData =
20 "keyword">new Dictionary<SystemLanguage, Dictionary<"keyword">string, "keyword">string>>();
21
22 "comment">// Events
23 "keyword">public "keyword">static event System.Action<SystemLanguage> OnLanguageChanged;
24
25 "keyword">void Awake()
26 {
27 "keyword">if (Instance == null)
28 {
29 Instance = "keyword">this;
30 DontDestroyOnLoad(gameObject);
31 InitializeLocalization();
32 }
33 "keyword">else
34 {
35 Destroy(gameObject);
36 }
37 }
38
39 "keyword">void InitializeLocalization()
40 {
41 "comment">// Load saved language preference
42 "keyword">string savedLanguage = PlayerPrefs.GetString("GameLanguage", "");
43
44 "keyword">if (!"keyword">string.IsNullOrEmpty(savedLanguage))
45 {
46 "keyword">if (System.Enum.TryParse(savedLanguage, out SystemLanguage language))
47 {
48 currentLanguage = language;
49 }
50 }
51 "keyword">else "keyword">if (detectSystemLanguage)
52 {
53 currentLanguage = Application.systemLanguage;
54 }
55
56 "comment">// Fallback to English "keyword">if language not supported
57 "keyword">if (!supportedLanguages.Contains(currentLanguage))
58 {
59 currentLanguage = SystemLanguage.English;
60 }
61
62 LoadAllLanguages();
63 SetLanguage(currentLanguage);
64 }
65
66 "keyword">void LoadAllLanguages()
67 {
68 "keyword">foreach (var language in supportedLanguages)
69 {
70 LoadLanguageFile(language);
71 }
72 }
73
74 "keyword">void LoadLanguageFile(SystemLanguage language)
75 {
76 "keyword">string fileName = language.ToString().ToLower();
77 "keyword">string filePath = Path.Combine(Application.streamingAssetsPath, localizationFolder, fileName + fileExtension);
78
79 "keyword">if (File.Exists(filePath))
80 {
81 try
82 {
83 "keyword">string jsonContent = File.ReadAllText(filePath);
84 var languageData = JsonUtility.FromJson<LocalizationData>(jsonContent);
85
86 var textDict = "keyword">new Dictionary<"keyword">string, "keyword">string>();
87 "keyword">foreach (var entry in languageData.entries)
88 {
89 textDict[entry.key] = entry.value;
90 }
91
92 allLanguageData[language] = textDict;
93 Debug.Log($"Loaded {textDict.Count} entries ">for {language}");
94 }
95 catch (System.Exception e)
96 {
97 Debug.LogError($"Failed to load localization file ">for {language}: {e.Message}");
98 }
99 }
100 "keyword">else
101 {
102 Debug.LogWarning($"Localization file not found: {filePath}");
103 }
104 }
105
106 "keyword">public "keyword">void SetLanguage(SystemLanguage language)
107 {
108 "keyword">if (!supportedLanguages.Contains(language))
109 {
110 Debug.LogWarning($"Language {language} not supported. Using English as fallback.");
111 language = SystemLanguage.English;
112 }
113
114 currentLanguage = language;
115
116 "keyword">if (allLanguageData.ContainsKey(language))
117 {
118 localizedText = allLanguageData[language];
119 }
120 "keyword">else
121 {
122 localizedText = "keyword">new Dictionary<"keyword">string, "keyword">string>();
123 }
124
125 "comment">// Save language preference
126 PlayerPrefs.SetString("GameLanguage", language.ToString());
127
128 "comment">// Notify all localized components
129 OnLanguageChanged?.Invoke(language);
130
131 Debug.Log($"Language changed to: {language}");
132 }
133
134 "keyword">public "keyword">string GetLocalizedText("keyword">string key, params object[] args)
135 {
136 "keyword">string localizedString = key; "comment">// Default to key "keyword">if not found
137
138 "keyword">if (localizedText.ContainsKey(key))
139 {
140 localizedString = localizedText[key];
141 }
142 "keyword">else
143 {
144 "comment">// Try fallback to English
145 "keyword">if (currentLanguage != SystemLanguage.English &&
146 allLanguageData.ContainsKey(SystemLanguage.English) &&
147 allLanguageData[SystemLanguage.English].ContainsKey(key))
148 {
149 localizedString = allLanguageData[SystemLanguage.English][key];
150 }
151 "keyword">else
152 {
153 Debug.LogWarning($"Localization key not found: {key}");
154 }
155 }
156
157 "comment">// Apply "keyword">string formatting "keyword">if arguments provided
158 "keyword">if (args.Length > 0)
159 {
160 try
161 {
162 localizedString = "keyword">string.Format(localizedString, args);
163 }
164 catch (System.Exception e)
165 {
166 Debug.LogError($"String formatting error ">for key {key}: {e.Message}");
167 }
168 }
169
170 "keyword">return localizedString;
171 }
172
173 "keyword">public "keyword">bool HasKey("keyword">string key)
174 {
175 "keyword">return localizedText.ContainsKey(key);
176 }
177
178 "keyword">public SystemLanguage GetCurrentLanguage()
179 {
180 "keyword">return currentLanguage;
181 }
182
183 "keyword">public List<SystemLanguage> GetSupportedLanguages()
184 {
185 "keyword">return "keyword">new List<SystemLanguage>(supportedLanguages);
186 }
187}
188
189[System.Serializable]
190"keyword">public "keyword">class LocalizationData
191{
192 "keyword">public LocalizationEntry[] entries;
193}
194
195[System.Serializable]
196"keyword">public "keyword">class LocalizationEntry
197{
198 "keyword">public "keyword">string key;
199 "keyword">public "keyword">string value;
200}
Step 2: Create Language Files
Create JSON files for each language in StreamingAssets/Localization/:
english.json
{
"entries": [
{
"key": "ui.menu.start_game",
"value": "Start Game"
},
{
"key": "ui.menu.load_game",
"value": "Load Game"
},
{
"key": "ui.menu.settings",
"value": "Settings"
},
{
"key": "player.level_up",
"value": "Level Up! You are now level {0}!"
},
{
"key": "quest.completed",
"value": "Quest '{0}' completed!"
},
{
"key": "item.not_enough_gold",
"value": "Not enough gold! You need {0} more."
}
]
}
spanish.json
{
"entries": [
{
"key": "ui.menu.start_game",
"value": "Iniciar Juego"
},
{
"key": "ui.menu.load_game",
"value": "Cargar Juego"
},
{
"key": "ui.menu.settings",
"value": "Configuración"
},
{
"key": "player.level_up",
"value": "¡Subiste de nivel! ¡Ahora eres nivel {0}!"
},
{
"key": "quest.completed",
"value": "¡Misión '{0}' completada!"
},
{
"key": "item.not_enough_gold",
"value": "¡No tienes suficiente oro! Necesitas {0} más."
}
]
}
Text Localization Components
Localized Text Component
Create a component to automatically localize UI text:
1"keyword">using UnityEngine;
2"keyword">using UnityEngine.UI;
3
4[RequireComponent(typeof(Text))]
5"keyword">public "keyword">class LocalizedText : MonoBehaviour
6{
7 [Header("Localization")]
8 "keyword">public "keyword">string localizationKey;
9 "keyword">public "keyword">bool useFormattedString = false;
10 "keyword">public "keyword">string[] formatArguments;
11
12 "keyword">private Text textComponent;
13 "keyword">private "keyword">string originalText;
14
15 "keyword">void Start()
16 {
17 textComponent = GetComponent<Text>();
18 originalText = textComponent.text;
19
20 "comment">// Use original text as key "keyword">if no key specified
21 "keyword">if ("keyword">string.IsNullOrEmpty(localizationKey))
22 {
23 localizationKey = originalText;
24 }
25
26 UpdateText();
27
28 "comment">// Subscribe to language changes
29 LocalizationManager.OnLanguageChanged += OnLanguageChanged;
30 }
31
32 "keyword">void OnDestroy()
33 {
34 "comment">// Unsubscribe to prevent memory leaks
35 LocalizationManager.OnLanguageChanged -= OnLanguageChanged;
36 }
37
38 "keyword">void OnLanguageChanged(SystemLanguage newLanguage)
39 {
40 UpdateText();
41 }
42
43 "keyword">void UpdateText()
44 {
45 "keyword">if (LocalizationManager.Instance == null) "keyword">return;
46
47 "keyword">string localizedText;
48
49 "keyword">if (useFormattedString && formatArguments.Length > 0)
50 {
51 "comment">// Convert "keyword">string arguments to objects
52 object[] args = "keyword">new object[formatArguments.Length];
53 "keyword">for ("keyword">int i = 0; i < formatArguments.Length; i++)
54 {
55 args[i] = formatArguments[i];
56 }
57
58 localizedText = LocalizationManager.Instance.GetLocalizedText(localizationKey, args);
59 }
60 "keyword">else
61 {
62 localizedText = LocalizationManager.Instance.GetLocalizedText(localizationKey);
63 }
64
65 textComponent.text = localizedText;
66
67 "comment">// Handle font changes "keyword">for different languages
68 UpdateFontForLanguage();
69 }
70
71 "keyword">void UpdateFontForLanguage()
72 {
73 var currentLanguage = LocalizationManager.Instance.GetCurrentLanguage();
74
75 "comment">// Load appropriate font "keyword">for language
76 Font newFont = GetFontForLanguage(currentLanguage);
77 "keyword">if (newFont != null)
78 {
79 textComponent.font = newFont;
80 }
81 }
82
83 Font GetFontForLanguage(SystemLanguage language)
84 {
85 "comment">// Load fonts from Resources folder based on language
86 switch (language)
87 {
88 case SystemLanguage.Japanese:
89 "keyword">return Resources.Load<Font>("Fonts/Japanese");
90 case SystemLanguage.Korean:
91 "keyword">return Resources.Load<Font>("Fonts/Korean");
92 case SystemLanguage.Chinese:
93 case SystemLanguage.ChineseSimplified:
94 case SystemLanguage.ChineseTraditional:
95 "keyword">return Resources.Load<Font>("Fonts/Chinese");
96 case SystemLanguage.Arabic:
97 "keyword">return Resources.Load<Font>("Fonts/Arabic");
98 default:
99 "keyword">return Resources.Load<Font>("Fonts/Latin");
100 }
101 }
102
103 "comment">// Public method to update formatting arguments at runtime
104 "keyword">public "keyword">void SetFormatArguments(params "keyword">string[] args)
105 {
106 formatArguments = args;
107 useFormattedString = args.Length > 0;
108 UpdateText();
109 }
110}
111
112"comment">// Custom editor "keyword">for easier setup
113#"keyword">if UNITY_EDITOR
114"keyword">using UnityEditor;
115
116[CustomEditor(typeof(LocalizedText))]
117"keyword">public "keyword">class LocalizedTextEditor : Editor
118{
119 "keyword">public "keyword">override "keyword">void OnInspectorGUI()
120 {
121 DrawDefaultInspector();
122
123 LocalizedText localizedText = (LocalizedText)target;
124
125 EditorGUILayout.Space();
126
127 "keyword">if (GUILayout.Button("Generate Key from Text"))
128 {
129 var textComponent = localizedText.GetComponent<Text>();
130 "keyword">if (textComponent != null)
131 {
132 "keyword">string generatedKey = GenerateKeyFromText(textComponent.text);
133 localizedText.localizationKey = generatedKey;
134 EditorUtility.SetDirty(localizedText);
135 }
136 }
137
138 "keyword">if (GUILayout.Button("Preview Localized Text"))
139 {
140 "keyword">if (Application.isPlaying && LocalizationManager.Instance != null)
141 {
142 var textComponent = localizedText.GetComponent<Text>();
143 "keyword">string localizedString = LocalizationManager.Instance.GetLocalizedText(localizedText.localizationKey);
144 EditorGUILayout.HelpBox($"Current: {localizedString}", MessageType.Info);
145 }
146 "keyword">else
147 {
148 EditorGUILayout.HelpBox("Preview available in Play Mode only", MessageType.Warning);
149 }
150 }
151 }
152
153 "keyword">string GenerateKeyFromText("keyword">string text)
154 {
155 "comment">// Generate a key from text: "Start Game" -> "ui.start_game"
156 "keyword">return "ui." + text.ToLower().Replace(" ", "_").Replace("!", "").Replace("?", "");
157 }
158}
159#endif
Dialogue System Integration
Integrate localization with the dialogue system:
1"comment">// Enhanced dialogue node with localization
2[System.Serializable]
3"keyword">public "keyword">class LocalizedDialogueNode : DialogueNode
4{
5 [Header("Localization")]
6 "keyword">public "keyword">bool useLocalization = true;
7 "keyword">public "keyword">string dialogueKeyPrefix = "dialogue";
8
9 "keyword">public "keyword">override "keyword">string GetDialogueText()
10 {
11 "keyword">if (useLocalization && LocalizationManager.Instance != null)
12 {
13 "keyword">string key = $"{dialogueKeyPrefix}.{characterName}.{GetNodeId()}";
14 "keyword">return LocalizationManager.Instance.GetLocalizedText(key);
15 }
16
17 "keyword">return dialogueText; "comment">// Fallback to original text
18 }
19
20 "keyword">public "keyword">override DialogueChoice[] GetLocalizedChoices()
21 {
22 "keyword">if (!useLocalization || LocalizationManager.Instance == null)
23 {
24 "keyword">return choices;
25 }
26
27 var localizedChoices = "keyword">new DialogueChoice[choices.Length];
28
29 "keyword">for ("keyword">int i = 0; i < choices.Length; i++)
30 {
31 localizedChoices[i] = choices[i];
32 "keyword">string choiceKey = $"{dialogueKeyPrefix}.choice.{GetNodeId()}_{i}";
33 localizedChoices[i].choiceText = LocalizationManager.Instance.GetLocalizedText(choiceKey);
34 }
35
36 "keyword">return localizedChoices;
37 }
38
39 "keyword">private "keyword">string GetNodeId()
40 {
41 "comment">// Generate unique ID "keyword">for "keyword">this node based on content or explicit ID
42 "keyword">return dialogueText.GetHashCode().ToString();
43 }
44}
45
46"comment">// Localized Quest System
47"keyword">public "keyword">class LocalizedQuest : Quest
48{
49 [Header("Localization")]
50 "keyword">public "keyword">bool useLocalization = true;
51 "keyword">public "keyword">string questKeyPrefix = "quest";
52
53 "keyword">public "keyword">override "keyword">string GetQuestName()
54 {
55 "keyword">if (useLocalization && LocalizationManager.Instance != null)
56 {
57 "keyword">string key = $"{questKeyPrefix}.{questId}.name";
58 "keyword">return LocalizationManager.Instance.GetLocalizedText(key);
59 }
60
61 "keyword">return questName;
62 }
63
64 "keyword">public "keyword">override "keyword">string GetQuestDescription()
65 {
66 "keyword">if (useLocalization && LocalizationManager.Instance != null)
67 {
68 "keyword">string key = $"{questKeyPrefix}.{questId}.description";
69 "keyword">return LocalizationManager.Instance.GetLocalizedText(key);
70 }
71
72 "keyword">return description;
73 }
74
75 "keyword">public "keyword">override "keyword">string GetObjectiveText("keyword">int objectiveIndex)
76 {
77 "keyword">if (useLocalization && LocalizationManager.Instance != null)
78 {
79 "keyword">string key = $"{questKeyPrefix}.{questId}.objective_{objectiveIndex}";
80 "keyword">return LocalizationManager.Instance.GetLocalizedText(key);
81 }
82
83 "keyword">return objectives[objectiveIndex].description;
84 }
85}
Audio Localization
Localized Audio Manager
Handle voice acting and audio for different languages:
1"keyword">public "keyword">class LocalizedAudioManager : MonoBehaviour
2{
3 [Header("Audio Localization")]
4 "keyword">public SystemLanguage currentLanguage;
5 "keyword">public AudioSource voiceSource;
6
7 [Header("Voice Banks")]
8 "keyword">public VoiceBank[] voiceBanks;
9
10 "keyword">private Dictionary<SystemLanguage, VoiceBank> languageVoiceBanks =
11 "keyword">new Dictionary<SystemLanguage, VoiceBank>();
12
13 "keyword">void Start()
14 {
15 "comment">// Initialize voice banks
16 "keyword">foreach (var bank in voiceBanks)
17 {
18 languageVoiceBanks[bank.language] = bank;
19 }
20
21 "comment">// Subscribe to language changes
22 LocalizationManager.OnLanguageChanged += OnLanguageChanged;
23
24 "keyword">if (LocalizationManager.Instance != null)
25 {
26 currentLanguage = LocalizationManager.Instance.GetCurrentLanguage();
27 }
28 }
29
30 "keyword">void OnDestroy()
31 {
32 LocalizationManager.OnLanguageChanged -= OnLanguageChanged;
33 }
34
35 "keyword">void OnLanguageChanged(SystemLanguage newLanguage)
36 {
37 currentLanguage = newLanguage;
38 }
39
40 "keyword">public "keyword">void PlayLocalizedAudio("keyword">string audioKey, AudioSource source = null)
41 {
42 "keyword">if (source == null) source = voiceSource;
43
44 AudioClip clip = GetLocalizedAudioClip(audioKey);
45 "keyword">if (clip != null)
46 {
47 source.clip = clip;
48 source.Play();
49 }
50 "keyword">else
51 {
52 Debug.LogWarning($"Localized audio not found ">for key: {audioKey} in language: {currentLanguage}");
53 }
54 }
55
56 AudioClip GetLocalizedAudioClip("keyword">string audioKey)
57 {
58 "keyword">if (languageVoiceBanks.ContainsKey(currentLanguage))
59 {
60 "keyword">return languageVoiceBanks[currentLanguage].GetAudioClip(audioKey);
61 }
62
63 "comment">// Fallback to English
64 "keyword">if (currentLanguage != SystemLanguage.English &&
65 languageVoiceBanks.ContainsKey(SystemLanguage.English))
66 {
67 "keyword">return languageVoiceBanks[SystemLanguage.English].GetAudioClip(audioKey);
68 }
69
70 "keyword">return null;
71 }
72
73 "keyword">public "keyword">bool HasLocalizedAudio("keyword">string audioKey)
74 {
75 "keyword">return GetLocalizedAudioClip(audioKey) != null;
76 }
77}
78
79[System.Serializable]
80"keyword">public "keyword">class VoiceBank
81{
82 "keyword">public SystemLanguage language;
83 "keyword">public VoiceEntry[] voiceEntries;
84
85 "keyword">private Dictionary<"keyword">string, AudioClip> audioDict;
86
87 "keyword">public "keyword">void Initialize()
88 {
89 audioDict = "keyword">new Dictionary<"keyword">string, AudioClip>();
90 "keyword">foreach (var entry in voiceEntries)
91 {
92 audioDict[entry.key] = entry.audioClip;
93 }
94 }
95
96 "keyword">public AudioClip GetAudioClip("keyword">string key)
97 {
98 "keyword">if (audioDict == null) Initialize();
99
100 "keyword">return audioDict.ContainsKey(key) ? audioDict[key] : null;
101 }
102}
103
104[System.Serializable]
105"keyword">public "keyword">class VoiceEntry
106{
107 "keyword">public "keyword">string key;
108 "keyword">public AudioClip audioClip;
109}
110
111"comment">// Integration with dialogue system
112"keyword">public "keyword">class LocalizedDialogueAudio : MonoBehaviour
113{
114 "keyword">private LocalizedAudioManager audioManager;
115
116 "keyword">void Start()
117 {
118 audioManager = FindObjectOfType<LocalizedAudioManager>();
119 }
120
121 "keyword">public "keyword">void PlayDialogueAudio("keyword">string characterName, "keyword">string dialogueKey)
122 {
123 "keyword">if (audioManager != null)
124 {
125 "keyword">string audioKey = $"dialogue.{characterName}.{dialogueKey}";
126 audioManager.PlayLocalizedAudio(audioKey);
127 }
128 }
129}
UI Adaptation for Different Languages
Dynamic UI Resizing
Handle text length variations across languages:
1"keyword">using UnityEngine;
2"keyword">using UnityEngine.UI;
3
4"keyword">public "keyword">class LocalizedUIAdapter : MonoBehaviour
5{
6 [Header("UI Adaptation")]
7 "keyword">public LayoutGroup[] layoutGroups;
8 "keyword">public ContentSizeFitter[] contentSizeFitters;
9 "keyword">public RectTransform[] dynamicElements;
10
11 [Header("Language-Specific Settings")]
12 "keyword">public LanguageUISettings[] languageSettings;
13
14 "keyword">private Dictionary<SystemLanguage, LanguageUISettings> settingsDict =
15 "keyword">new Dictionary<SystemLanguage, LanguageUISettings>();
16
17 "keyword">void Start()
18 {
19 "comment">// Initialize settings dictionary
20 "keyword">foreach (var setting in languageSettings)
21 {
22 settingsDict[setting.language] = setting;
23 }
24
25 LocalizationManager.OnLanguageChanged += AdaptUIForLanguage;
26
27 "keyword">if (LocalizationManager.Instance != null)
28 {
29 AdaptUIForLanguage(LocalizationManager.Instance.GetCurrentLanguage());
30 }
31 }
32
33 "keyword">void OnDestroy()
34 {
35 LocalizationManager.OnLanguageChanged -= AdaptUIForLanguage;
36 }
37
38 "keyword">void AdaptUIForLanguage(SystemLanguage language)
39 {
40 "comment">// Apply language-specific UI settings
41 "keyword">if (settingsDict.ContainsKey(language))
42 {
43 var settings = settingsDict[language];
44 ApplyUISettings(settings);
45 }
46
47 "comment">// Force layout rebuild
48 StartCoroutine(RebuildLayoutsNextFrame());
49 }
50
51 "keyword">void ApplyUISettings(LanguageUISettings settings)
52 {
53 "comment">// Adjust text alignment "keyword">for RTL languages
54 "keyword">if (settings.isRightToLeft)
55 {
56 SetTextAlignment(TextAnchor.MiddleRight);
57 }
58 "keyword">else
59 {
60 SetTextAlignment(TextAnchor.MiddleLeft);
61 }
62
63 "comment">// Apply font size multiplier
64 "keyword">if (settings.fontSizeMultiplier != 1.0f)
65 {
66 AdjustFontSizes(settings.fontSizeMultiplier);
67 }
68
69 "comment">// Adjust spacing "keyword">for languages that need more space
70 "keyword">if (settings.spacingMultiplier != 1.0f)
71 {
72 AdjustLayoutSpacing(settings.spacingMultiplier);
73 }
74 }
75
76 "keyword">void SetTextAlignment(TextAnchor alignment)
77 {
78 var textComponents = FindObjectsOfType<Text>();
79 "keyword">foreach (var text in textComponents)
80 {
81 "keyword">if (text.GetComponent<LocalizedText>() != null)
82 {
83 text.alignment = alignment;
84 }
85 }
86 }
87
88 "keyword">void AdjustFontSizes("keyword">float multiplier)
89 {
90 var textComponents = FindObjectsOfType<Text>();
91 "keyword">foreach (var text in textComponents)
92 {
93 "keyword">if (text.GetComponent<LocalizedText>() != null)
94 {
95 text.fontSize = Mathf.RoundToInt(text.fontSize * multiplier);
96 }
97 }
98 }
99
100 "keyword">void AdjustLayoutSpacing("keyword">float multiplier)
101 {
102 "keyword">foreach (var layout in layoutGroups)
103 {
104 "keyword">if (layout is HorizontalLayoutGroup hlg)
105 {
106 hlg.spacing *= multiplier;
107 }
108 "keyword">else "keyword">if (layout is VerticalLayoutGroup vlg)
109 {
110 vlg.spacing *= multiplier;
111 }
112 }
113 }
114
115 System.Collections.IEnumerator RebuildLayoutsNextFrame()
116 {
117 yield "keyword">return null; "comment">// Wait one frame
118
119 "comment">// Force content size fitters to recalculate
120 "keyword">foreach (var fitter in contentSizeFitters)
121 {
122 LayoutRebuilder.ForceRebuildLayoutImmediate(fitter.GetComponent<RectTransform>());
123 }
124
125 "comment">// Force layout groups to recalculate
126 "keyword">foreach (var layout in layoutGroups)
127 {
128 LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
129 }
130 }
131}
132
133[System.Serializable]
134"keyword">public "keyword">class LanguageUISettings
135{
136 "keyword">public SystemLanguage language;
137 "keyword">public "keyword">bool isRightToLeft = false;
138 "keyword">public "keyword">float fontSizeMultiplier = 1.0f;
139 "keyword">public "keyword">float spacingMultiplier = 1.0f;
140 "keyword">public Font preferredFont;
141}
142
143"comment">// Component "keyword">for handling text direction
144"keyword">public "keyword">class TextDirectionHandler : MonoBehaviour
145{
146 "keyword">private Text textComponent;
147
148 "keyword">void Start()
149 {
150 textComponent = GetComponent<Text>();
151 LocalizationManager.OnLanguageChanged += HandleTextDirection;
152
153 "keyword">if (LocalizationManager.Instance != null)
154 {
155 HandleTextDirection(LocalizationManager.Instance.GetCurrentLanguage());
156 }
157 }
158
159 "keyword">void OnDestroy()
160 {
161 LocalizationManager.OnLanguageChanged -= HandleTextDirection;
162 }
163
164 "keyword">void HandleTextDirection(SystemLanguage language)
165 {
166 "keyword">bool isRTL = IsRightToLeftLanguage(language);
167
168 "keyword">if (isRTL)
169 {
170 "comment">// For RTL languages, adjust alignment and potentially reverse text
171 textComponent.alignment = TextAnchor.MiddleRight;
172
173 "comment">// Note: Full RTL text support would require a specialized RTL text component
174 "comment">// or Unitys TextMeshPro with RTL support
175 }
176 "keyword">else
177 {
178 textComponent.alignment = TextAnchor.MiddleLeft;
179 }
180 }
181
182 "keyword">bool IsRightToLeftLanguage(SystemLanguage language)
183 {
184 "keyword">return language == SystemLanguage.Arabic ||
185 language == SystemLanguage.Hebrew;
186 }
187
188}
Testing and Validation
Language Testing Tools
Create tools to test localization in the editor:
1#"keyword">if UNITY_EDITOR
2"keyword">using UnityEditor;
3"keyword">using UnityEngine;
4
5"keyword">public "keyword">class LocalizationTester : EditorWindow
6{
7 "keyword">private SystemLanguage testLanguage = SystemLanguage.English;
8 "keyword">private Vector2 scrollPosition;
9 "keyword">private "keyword">bool showMissingKeys = true;
10 "keyword">private "keyword">bool showLongText = true;
11
12 [MenuItem("RPG Tools/Localization Tester")]
13 "keyword">public "keyword">static "keyword">void ShowWindow()
14 {
15 GetWindow<LocalizationTester>("Localization Tester");
16 }
17
18 "keyword">void OnGUI()
19 {
20 GUILayout.Label("Localization Testing", EditorStyles.boldLabel);
21
22 EditorGUILayout.Space();
23
24 "comment">// Language selection
25 testLanguage = (SystemLanguage)EditorGUILayout.EnumPopup("Test Language", testLanguage);
26
27 EditorGUILayout.Space();
28
29 EditorGUILayout.BeginHorizontal();
30 "keyword">if (GUILayout.Button("Test Language"))
31 {
32 TestLanguage();
33 }
34
35 "keyword">if (GUILayout.Button("Generate Pseudo-localization"))
36 {
37 GeneratePseudoLocalization();
38 }
39 EditorGUILayout.EndHorizontal();
40
41 EditorGUILayout.Space();
42
43 "comment">// Testing options
44 showMissingKeys = EditorGUILayout.Toggle("Show Missing Keys", showMissingKeys);
45 showLongText = EditorGUILayout.Toggle("Show Long Text Issues", showLongText);
46
47 EditorGUILayout.Space();
48
49 "keyword">if (GUILayout.Button("Validate All Languages"))
50 {
51 ValidateAllLanguages();
52 }
53
54 EditorGUILayout.Space();
55
56 "comment">// Results area
57 scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
58
59 "keyword">if (Application.isPlaying && LocalizationManager.Instance != null)
60 {
61 DisplayTestResults();
62 }
63 "keyword">else
64 {
65 EditorGUILayout.HelpBox("Enter Play Mode to test localization", MessageType.Info);
66 }
67
68 EditorGUILayout.EndScrollView();
69 }
70
71 "keyword">void TestLanguage()
72 {
73 "keyword">if (Application.isPlaying && LocalizationManager.Instance != null)
74 {
75 LocalizationManager.Instance.SetLanguage(testLanguage);
76 Debug.Log($"Testing language: {testLanguage}");
77 }
78 }
79
80 "keyword">void GeneratePseudoLocalization()
81 {
82 "comment">// Generate pseudo-localized text "keyword">for testing UI layout
83 "keyword">string pseudoText = "Ṫḧïṡ ïṡ ä ṁüċḧ ḷöṅġëṛ ṫëẍṫ ṫö ṫëṡṫ ÜÏ ḷäÿöüṫ äḋäṗṫäṫïöṅ";
84 Debug.Log($"Pseudo-localized text: {pseudoText}");
85 }
86
87 "keyword">void ValidateAllLanguages()
88 {
89 "keyword">if (LocalizationManager.Instance == null) "keyword">return;
90
91 var supportedLanguages = LocalizationManager.Instance.GetSupportedLanguages();
92
93 "keyword">foreach (var language in supportedLanguages)
94 {
95 ValidateLanguage(language);
96 }
97 }
98
99 "keyword">void ValidateLanguage(SystemLanguage language)
100 {
101 Debug.Log($"Validating {language}...");
102
103 "comment">// Check "keyword">for missing keys, formatting issues, etc.
104 "comment">// This would involve checking all localized text components
105 "comment">// and verifying their keys exist in the language files
106 }
107
108 "keyword">void DisplayTestResults()
109 {
110 EditorGUILayout.LabelField("Test Results", EditorStyles.boldLabel);
111
112 "keyword">if (showMissingKeys)
113 {
114 DisplayMissingKeys();
115 }
116
117 "keyword">if (showLongText)
118 {
119 DisplayLongTextIssues();
120 }
121 }
122
123 "keyword">void DisplayMissingKeys()
124 {
125 EditorGUILayout.LabelField("Missing Keys:", EditorStyles.boldLabel);
126
127 var localizedTexts = FindObjectsOfType<LocalizedText>();
128 "keyword">foreach (var localizedText in localizedTexts)
129 {
130 "keyword">if (!LocalizationManager.Instance.HasKey(localizedText.localizationKey))
131 {
132 EditorGUILayout.HelpBox($"Missing key: {localizedText.localizationKey}", MessageType.Warning);
133 }
134 }
135 }
136
137 "keyword">void DisplayLongTextIssues()
138 {
139 EditorGUILayout.LabelField("Potential Layout Issues:", EditorStyles.boldLabel);
140
141 var localizedTexts = FindObjectsOfType<LocalizedText>();
142 "keyword">foreach (var localizedText in localizedTexts)
143 {
144 "keyword">string text = LocalizationManager.Instance.GetLocalizedText(localizedText.localizationKey);
145
146 "keyword">if (text.Length > 50) "comment">// Threshold "keyword">for "long" text
147 {
148 EditorGUILayout.HelpBox($"Long text ({text.Length} chars): {localizedText.name}", MessageType.Info);
149 }
150 }
151 }
152}
153#endif
Localization Checklist
✅ Text Preparation
- All text uses localization keys
- No hardcoded strings in code
- Context provided for translators
- String formatting properly implemented
🎨 UI Adaptation
- Dynamic text sizing implemented
- Layout handles text expansion
- Fonts support target languages
- RTL languages properly handled
🧪 Testing
- All languages tested in-game
- UI layout works with long text
- Audio sync verified for voice acting
- Cultural appropriateness reviewed
📱 Technical
- Performance impact minimized
- Memory usage optimized
- Fallback language implemented
- Build process includes all languages