# Localization Testing Guide ## Overview Localization testing ensures games work correctly across languages, regions, and cultures. Beyond translation, it validates text display, cultural appropriateness, and regional compliance. ## Test Categories ### Linguistic Testing | Category | Focus | Examples | | -------------------- | ----------------------- | ------------------------------ | | Translation accuracy | Meaning preserved | Idioms, game terminology | | Grammar/spelling | Language correctness | Verb tense, punctuation | | Consistency | Same terms throughout | "Health" vs "HP" vs "Life" | | Context | Meaning in game context | Item names, skill descriptions | ### Functional Testing | Category | Focus | Examples | | -------------- | ----------------------- | --------------------------- | | Text display | Fits in UI | Button labels, dialog boxes | | Font support | Characters render | CJK, Cyrillic, Arabic | | Text expansion | Longer translations | German is ~30% longer | | RTL support | Right-to-left languages | Arabic, Hebrew layouts | ### Cultural Testing | Category | Focus | Examples | | -------------------- | ------------------ | ------------------------- | | Cultural sensitivity | Offensive content | Gestures, symbols, colors | | Regional compliance | Legal requirements | Ratings, gambling laws | | Date/time formats | Local conventions | DD/MM/YYYY vs MM/DD/YYYY | | Number formats | Decimal separators | 1,000.00 vs 1.000,00 | ## Test Scenarios ### Text Display ``` SCENARIO: Text Fits UI Elements GIVEN all localized strings WHEN displayed in target language THEN text fits within UI boundaries AND no truncation or overflow occurs AND text remains readable SCENARIO: Dynamic Text Insertion GIVEN template "Player {name} scored {points} points" WHEN name="Alexander" and points=1000 THEN German: "Spieler Alexander hat 1.000 Punkte erzielt" AND text fits UI element AND variables are correctly formatted for locale SCENARIO: Plural Forms GIVEN English "1 coin" / "5 coins" WHEN displaying in Polish (4 plural forms) THEN correct plural form is used AND all plural forms are translated ``` ### Character Support ``` SCENARIO: CJK Character Rendering GIVEN Japanese localization WHEN displaying text with kanji/hiragana/katakana THEN all characters render correctly AND no missing glyphs (tofu boxes) AND line breaks respect CJK rules SCENARIO: Special Characters GIVEN text with accented characters (é, ñ, ü) WHEN displayed in-game THEN all characters render correctly AND sorting works correctly SCENARIO: User-Generated Content GIVEN player can name character WHEN name includes non-Latin characters THEN name displays correctly AND name saves/loads correctly AND name appears correctly to other players ``` ### Layout and Direction ``` SCENARIO: Right-to-Left Layout GIVEN Arabic localization WHEN viewing UI THEN text reads right-to-left AND UI elements mirror appropriately AND numbers remain left-to-right AND mixed content (Arabic + English) displays correctly SCENARIO: Text Expansion Accommodation GIVEN English UI "OK" / "Cancel" buttons WHEN localized to German "OK" / "Abbrechen" THEN button expands or text size adjusts AND button remains clickable AND layout doesn't break ``` ## Locale-Specific Formatting ### Date and Time | Locale | Date Format | Time Format | | ------ | -------------- | ----------- | | en-US | 12/25/2024 | 3:30 PM | | en-GB | 25/12/2024 | 15:30 | | de-DE | 25.12.2024 | 15:30 Uhr | | ja-JP | 2024年12月25日 | 15時30分 | ### Numbers and Currency | Locale | Number | Currency | | ------ | -------- | ---------- | | en-US | 1,234.56 | $1,234.56 | | de-DE | 1.234,56 | 1.234,56 € | | fr-FR | 1 234,56 | 1 234,56 € | | ja-JP | 1,234.56 | ¥1,235 | ## Automated Test Examples ### Unity ```csharp using UnityEngine.Localization; [Test] public void Localization_AllKeysHaveTranslations([Values("en", "de", "ja", "zh-CN")] string locale) { var stringTable = LocalizationSettings.StringDatabase .GetTable("GameStrings", new Locale(locale)); foreach (var entry in stringTable) { Assert.IsFalse(string.IsNullOrEmpty(entry.Value.LocalizedValue), $"Missing translation for '{entry.Key}' in {locale}"); } } [Test] public void TextFits_AllUIElements() { var languages = new[] { "en", "de", "fr", "ja" }; foreach (var lang in languages) { LocalizationSettings.SelectedLocale = new Locale(lang); foreach (var textElement in FindObjectsOfType()) { var rectTransform = textElement.GetComponent(); var textComponent = textElement.GetComponent(); Assert.LessOrEqual( textComponent.preferredWidth, rectTransform.rect.width, $"Text overflows in {lang}: {textElement.name}"); } } } [TestCase("en", 1, "1 coin")] [TestCase("en", 5, "5 coins")] [TestCase("ru", 1, "1 монета")] [TestCase("ru", 2, "2 монеты")] [TestCase("ru", 5, "5 монет")] public void Pluralization_ReturnsCorrectForm(string locale, int count, string expected) { var result = Localization.GetPlural("coin", count, locale); Assert.AreEqual(expected, result); } ``` ### Unreal ```cpp bool FLocalizationTest::RunTest(const FString& Parameters) { TArray Cultures = {"en", "de", "ja", "ko"}; for (const FString& Culture : Cultures) { FInternationalization::Get().SetCurrentCulture(Culture); // Test critical strings exist FText LocalizedText = NSLOCTEXT("Game", "StartButton", "Start"); TestFalse( FString::Printf(TEXT("Missing StartButton in %s"), *Culture), LocalizedText.IsEmpty()); // Test number formatting FText NumberText = FText::AsNumber(1234567); TestTrue( TEXT("Number should be formatted"), NumberText.ToString().Len() > 7); // Has separators } return true; } ``` ### Godot ```gdscript func test_all_translations_complete(): var locales = ["en", "de", "ja", "es"] var keys = TranslationServer.get_all_keys() for locale in locales: TranslationServer.set_locale(locale) for key in keys: var translated = tr(key) assert_ne(translated, key, "Missing translation for '%s' in %s" % [key, locale]) func test_plural_forms(): TranslationServer.set_locale("ru") assert_eq(tr_n("coin", "coins", 1), "1 монета") assert_eq(tr_n("coin", "coins", 2), "2 монеты") assert_eq(tr_n("coin", "coins", 5), "5 монет") assert_eq(tr_n("coin", "coins", 21), "21 монета") func test_text_fits_buttons(): var locales = ["en", "de", "fr"] for locale in locales: TranslationServer.set_locale(locale) await get_tree().process_frame # Allow UI update for button in get_tree().get_nodes_in_group("localized_buttons"): var label = button.get_node("Label") assert_lt(label.size.x, button.size.x, "Button text overflows in %s: %s" % [locale, button.name]) ``` ## Visual Verification Checklist ### Text Display - [ ] No truncation in any language - [ ] Consistent font sizing - [ ] Proper line breaks - [ ] No overlapping text ### UI Layout - [ ] Buttons accommodate longer text - [ ] Dialog boxes resize appropriately - [ ] Menu items align correctly - [ ] Scrollbars appear when needed ### Cultural Elements - [ ] Icons are culturally appropriate - [ ] Colors don't have negative connotations - [ ] Gestures are region-appropriate - [ ] No unintended political references ## Regional Compliance ### Ratings Requirements | Region | Rating Board | Special Requirements | | ------------- | ------------ | ------------------------- | | North America | ESRB | Content descriptors | | Europe | PEGI | Age-appropriate icons | | Japan | CERO | Strict content guidelines | | Germany | USK | Violence restrictions | | China | GRAC | Approval process | ### Common Regional Issues | Issue | Regions Affected | Solution | | ---------------- | ---------------- | ------------------------ | | Blood color | Japan, Germany | Option for green/disable | | Gambling imagery | Many regions | Remove or modify | | Skulls/bones | China | Alternative designs | | Nazi imagery | Germany | Remove entirely | ## Best Practices ### DO - Test with native speakers - Plan for text expansion (reserve 30% extra space) - Use placeholder text during development (Lorem ipsum-style) - Support multiple input methods (IME for CJK) - Test all language combinations (UI language + audio language) - Validate string format parameters ### DON'T - Hard-code strings in source code - Assume left-to-right layout - Concatenate translated strings - Use machine translation without review - Forget about date/time/number formatting - Ignore cultural context of images and icons