Files
xtremflow/_bmad/bmgd/gametest/knowledge/localization-testing.md

305 lines
9.3 KiB
Markdown

# 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<LocalizedText>())
{
var rectTransform = textElement.GetComponent<RectTransform>();
var textComponent = textElement.GetComponent<Text>();
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<FString> 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