Popcorning Guinea Pig
Search Results for

    Localization System

    The Popcorning Guinea Pig engine provides comprehensive localization support through the ILocalizationService, enabling games to support multiple languages and cultures.

    Overview

    The localization system allows you to:

    • Support multiple languages and regions
    • Store translations in JSON or XML files
    • Use parameterized text with placeholders
    • Handle pluralization rules
    • Support right-to-left (RTL) languages
    • Automatically detect system language
    • Fallback to default language for missing translations

    ILocalizationService Interface

    public interface ILocalizationService
    {
        void LoadLanguage(string languageCode);
        string GetText(string key, params object[] parameters);
        string GetText(string key, string defaultText, params object[] parameters);
        bool HasText(string key);
        string CurrentLanguage { get; }
        string[] AvailableLanguages { get; }
        CultureInfo CurrentCulture { get; }
        void SetLanguage(string languageCode);
    }
    

    Setting Up Localization

    1. Create Language Files

    Create JSON files for each supported language in your assets directory:

    assets/localization/
    ├── en.json          # English (default)
    ├── es.json          # Spanish
    ├── fr.json          # French
    ├── de.json          # German
    ├── ja.json          # Japanese
    └── zh.json          # Chinese
    

    2. English Language File (en.json)

    {
      "ui": {
        "mainMenu": {
          "title": "Popcorning Guinea Pig",
          "playButton": "Play Game",
          "settingsButton": "Settings",
          "exitButton": "Exit Game"
        },
        "settings": {
          "title": "Settings",
          "audio": "Audio",
          "graphics": "Graphics",
          "language": "Language",
          "back": "Back"
        },
        "gameplay": {
          "score": "Score: {0}",
          "level": "Level {0}",
          "lives": "Lives: {0}",
          "gameOver": "Game Over",
          "wellDone": "Well Done!",
          "timeRemaining": "{0} seconds remaining"
        }
      },
      "messages": {
        "welcome": "Welcome to Popcorning Guinea Pig!",
        "playerJoined": "{0} has joined the game",
        "playerLeft": "{0} has left the game",
        "itemCollected": "You collected {0} {1}",
        "achievements": {
          "firstWin": "Congratulations on your first victory!",
          "speedRunner": "You completed the level in record time!",
          "collector": "You found all the hidden items!"
        }
      },
      "plurals": {
        "itemCount": {
          "zero": "No items",
          "one": "1 item",
          "other": "{0} items"
        },
        "playerCount": {
          "zero": "No players",
          "one": "1 player",
          "other": "{0} players"
        }
      }
    }
    

    3. Spanish Language File (es.json)

    {
      "ui": {
        "mainMenu": {
          "title": "Conejillo de Indias Saltarín",
          "playButton": "Jugar",
          "settingsButton": "Configuración",
          "exitButton": "Salir"
        },
        "settings": {
          "title": "Configuración",
          "audio": "Audio",
          "graphics": "Gráficos",
          "language": "Idioma",
          "back": "Atrás"
        },
        "gameplay": {
          "score": "Puntuación: {0}",
          "level": "Nivel {0}",
          "lives": "Vidas: {0}",
          "gameOver": "Fin del Juego",
          "wellDone": "¡Bien Hecho!",
          "timeRemaining": "{0} segundos restantes"
        }
      },
      "messages": {
        "welcome": "¡Bienvenido a Conejillo de Indias Saltarín!",
        "playerJoined": "{0} se ha unido al juego",
        "playerLeft": "{0} ha abandonado el juego",
        "itemCollected": "Has recogido {0} {1}",
        "achievements": {
          "firstWin": "¡Felicitaciones por tu primera victoria!",
          "speedRunner": "¡Completaste el nivel en tiempo récord!",
          "collector": "¡Encontraste todos los objetos ocultos!"
        }
      }
    }
    

    4. Initialize Localization Service

    public class MyGame : Game
    {
        private readonly ILocalizationService _localization;
        
        public MyGame()
        {
            _localization = GetService<ILocalizationService>();
            
            // Load default language or system language
            var systemLanguage = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
            _localization.LoadLanguage(systemLanguage);
            
            // Fallback to English if system language not supported
            if (!_localization.AvailableLanguages.Contains(systemLanguage))
            {
                _localization.LoadLanguage("en");
            }
        }
    }
    

    Basic Usage

    Getting Localized Text

    // Simple text lookup
    string title = _localization.GetText("ui.mainMenu.title");
    string playButton = _localization.GetText("ui.mainMenu.playButton");
    
    // Text with parameters
    string score = _localization.GetText("ui.gameplay.score", playerScore);
    string level = _localization.GetText("ui.gameplay.level", currentLevel);
    
    // Text with default fallback
    string customText = _localization.GetText("custom.key", "Default Text");
    

    Using Localized Text in UI

    public class MainMenuScene : Scene
    {
        private readonly ILocalizationService _localization;
        
        public MainMenuScene()
        {
            _localization = GetService<ILocalizationService>();
            CreateUI();
        }
        
        private void CreateUI()
        {
            // Title label
            var titleLabel = new Label
            {
                Text = _localization.GetText("ui.mainMenu.title"),
                Position = new Vector2(400, 100)
            };
            
            // Play button
            var playButton = new Button
            {
                Text = _localization.GetText("ui.mainMenu.playButton"),
                Position = new Vector2(400, 200),
                OnClick = StartGame
            };
            
            // Settings button
            var settingsButton = new Button
            {
                Text = _localization.GetText("ui.mainMenu.settingsButton"),
                Position = new Vector2(400, 250),
                OnClick = ShowSettings
            };
            
            AddElement(titleLabel);
            AddElement(playButton);
            AddElement(settingsButton);
        }
    }
    

    Advanced Features

    Language Selection UI

    Create a language selection dropdown:

    public class LanguageSelector : UIElement
    {
        private readonly ILocalizationService _localization;
        private readonly Dropdown _languageDropdown;
        
        public LanguageSelector()
        {
            _localization = GetService<ILocalizationService>();
            
            _languageDropdown = new Dropdown
            {
                Items = GetLanguageDisplayNames(),
                SelectedIndex = GetCurrentLanguageIndex(),
                OnSelectionChanged = OnLanguageChanged
            };
            
            AddChild(_languageDropdown);
        }
        
        private string[] GetLanguageDisplayNames()
        {
            var languages = _localization.AvailableLanguages;
            var displayNames = new string[languages.Length];
            
            for (int i = 0; i < languages.Length; i++)
            {
                displayNames[i] = GetLanguageDisplayName(languages[i]);
            }
            
            return displayNames;
        }
        
        private string GetLanguageDisplayName(string languageCode)
        {
            return languageCode switch
            {
                "en" => "English",
                "es" => "Español",
                "fr" => "Français",
                "de" => "Deutsch",
                "ja" => "日本語",
                "zh" => "中文",
                _ => languageCode.ToUpper()
            };
        }
        
        private void OnLanguageChanged(int selectedIndex)
        {
            var languages = _localization.AvailableLanguages;
            if (selectedIndex >= 0 && selectedIndex < languages.Length)
            {
                _localization.SetLanguage(languages[selectedIndex]);
                RefreshUI();
            }
        }
        
        private void RefreshUI()
        {
            // Notify UI elements to refresh their text
            GameEvents.LocalizationChanged?.Invoke();
        }
    }
    

    Auto-Refreshing UI Elements

    Create UI elements that automatically update when language changes:

    public class LocalizedLabel : Label
    {
        private readonly string _textKey;
        private readonly object[] _parameters;
        private readonly ILocalizationService _localization;
        
        public LocalizedLabel(string textKey, params object[] parameters)
        {
            _textKey = textKey;
            _parameters = parameters;
            _localization = GetService<ILocalizationService>();
            
            UpdateText();
            
            // Subscribe to language changes
            GameEvents.LocalizationChanged += UpdateText;
        }
        
        private void UpdateText()
        {
            Text = _localization.GetText(_textKey, _parameters);
        }
        
        public void UpdateParameters(params object[] newParameters)
        {
            _parameters = newParameters;
            UpdateText();
        }
        
        protected override void Dispose()
        {
            GameEvents.LocalizationChanged -= UpdateText;
            base.Dispose();
        }
    }
    
    // Usage
    var scoreLabel = new LocalizedLabel("ui.gameplay.score", 0);
    var levelLabel = new LocalizedLabel("ui.gameplay.level", 1);
    
    // Update parameters when values change
    scoreLabel.UpdateParameters(newScore);
    levelLabel.UpdateParameters(newLevel);
    

    Pluralization Support

    Handle pluralization rules for different languages:

    public static class PluralRules
    {
        public static string GetPluralForm(ILocalizationService localization, string key, int count)
        {
            var currentLang = localization.CurrentLanguage;
            
            // Determine plural form based on language rules
            string pluralKey = currentLang switch
            {
                "en" => count == 0 ? "zero" : (count == 1 ? "one" : "other"),
                "es" => count == 0 ? "zero" : (count == 1 ? "one" : "other"),
                "fr" => count <= 1 ? "one" : "other",
                "ru" => GetRussianPluralForm(count),
                _ => count == 1 ? "one" : "other"
            };
            
            var fullKey = $"{key}.{pluralKey}";
            return localization.HasText(fullKey) 
                ? localization.GetText(fullKey, count)
                : localization.GetText($"{key}.other", count);
        }
        
        private static string GetRussianPluralForm(int count)
        {
            if (count % 10 == 1 && count % 100 != 11) return "one";
            if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20)) return "few";
            return "other";
        }
    }
    
    // Usage
    string itemText = PluralRules.GetPluralForm(_localization, "plurals.itemCount", itemCount);
    

    Date and Number Formatting

    Format numbers and dates according to current culture:

    public static class LocalizedFormatting
    {
        public static string FormatNumber(ILocalizationService localization, double number)
        {
            return number.ToString("N0", localization.CurrentCulture);
        }
        
        public static string FormatCurrency(ILocalizationService localization, decimal amount)
        {
            return amount.ToString("C", localization.CurrentCulture);
        }
        
        public static string FormatDate(ILocalizationService localization, DateTime date)
        {
            return date.ToString("d", localization.CurrentCulture);
        }
        
        public static string FormatTime(ILocalizationService localization, DateTime time)
        {
            return time.ToString("t", localization.CurrentCulture);
        }
    }
    
    // Usage
    string score = LocalizedFormatting.FormatNumber(_localization, playerScore);
    string price = LocalizedFormatting.FormatCurrency(_localization, itemPrice);
    string date = LocalizedFormatting.FormatDate(_localization, DateTime.Now);
    

    Text Direction Support

    Handle right-to-left (RTL) languages:

    public static class TextDirectionHelper
    {
        private static readonly HashSet<string> RtlLanguages = new()
        {
            "ar", "he", "fa", "ur"
        };
        
        public static bool IsRightToLeft(string languageCode)
        {
            return RtlLanguages.Contains(languageCode);
        }
        
        public static void ApplyTextDirection(UIElement element, string languageCode)
        {
            if (IsRightToLeft(languageCode))
            {
                element.TextAlignment = TextAlignment.Right;
                element.FlowDirection = FlowDirection.RightToLeft;
            }
            else
            {
                element.TextAlignment = TextAlignment.Left;
                element.FlowDirection = FlowDirection.LeftToRight;
            }
        }
    }
    

    Best Practices

    Organization

    1. Hierarchical keys: Use dot notation for nested organization
    2. Consistent naming: Follow a standard naming convention across all files
    3. Context grouping: Group related text by UI section or feature

    Translation Management

    1. Key validation: Ensure all languages have the same keys
    2. Parameter consistency: Use consistent parameter ordering across languages
    3. Translation notes: Add comments for translators when context is important

    Performance

    1. Preload languages: Load language files at startup, not on-demand
    2. Cache lookups: Cache frequently accessed translations
    3. Lazy loading: Only load active language, not all languages

    User Experience

    1. Language persistence: Save user's language preference
    2. Immediate updates: Refresh UI immediately when language changes
    3. Fallback handling: Always provide fallback text for missing keys

    Example: Complete Localization Setup

    public class GameLocalizationManager
    {
        private readonly ILocalizationService _localization;
        private readonly ISettingsService _settings;
        
        public event Action LanguageChanged;
        
        public GameLocalizationManager(ILocalizationService localization, ISettingsService settings)
        {
            _localization = localization;
            _settings = settings;
            
            Initialize();
        }
        
        private void Initialize()
        {
            // Load saved language preference or detect system language
            var savedLanguage = _settings.GetSetting<string>("Language");
            if (!string.IsNullOrEmpty(savedLanguage))
            {
                _localization.SetLanguage(savedLanguage);
            }
            else
            {
                DetectAndSetSystemLanguage();
            }
        }
        
        private void DetectAndSetSystemLanguage()
        {
            var systemLang = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
            var availableLanguages = _localization.AvailableLanguages;
            
            if (availableLanguages.Contains(systemLang))
            {
                _localization.SetLanguage(systemLang);
            }
            else
            {
                _localization.SetLanguage("en"); // Fallback to English
            }
        }
        
        public void ChangeLanguage(string languageCode)
        {
            if (_localization.AvailableLanguages.Contains(languageCode))
            {
                _localization.SetLanguage(languageCode);
                _settings.SetSetting("Language", languageCode);
                _settings.SaveSettings();
                
                LanguageChanged?.Invoke();
            }
        }
        
        public string GetLocalizedText(string key, params object[] parameters)
        {
            return _localization.GetText(key, parameters);
        }
        
        public string GetLocalizedTextWithFallback(string key, string fallback, params object[] parameters)
        {
            return _localization.HasText(key) 
                ? _localization.GetText(key, parameters) 
                : fallback;
        }
    }
    
    // Usage in game
    public class MyGame : Game
    {
        private GameLocalizationManager _localizationManager;
        
        public MyGame()
        {
            var localization = GetService<ILocalizationService>();
            var settings = GetService<ISettingsService>();
            
            _localizationManager = new GameLocalizationManager(localization, settings);
            _localizationManager.LanguageChanged += OnLanguageChanged;
        }
        
        private void OnLanguageChanged()
        {
            // Refresh all UI elements
            RefreshAllScenes();
        }
    }
    

    The localization system enables your Popcorning Guinea Pig games to reach a global audience with proper language and cultural support.

    • Edit this page
    In this article
    Back to top © 2024 Popcorning Guinea Pig Game Engine. A custom C# game engine for indie developers.