Skip to main content

2 posts tagged with "localization"

View All Tags

· 3 min read
Schartier Isaac

In this article, I explain how I implemented a method to override which gamepad icon set should be used in Common UI Input.
This allows adding a user setting to manually override the detected controller type.

This feature is useful when a player plugs a generic controller but the physical buttons match a PlayStation or Switch layout.
This behavior can also be found in Monster Hunter Wild.

Example from my game:

Screenshot

Useful Resources

Modifying the Common UI Input Plugin

The downside is that this requires editing Common UI Input source files, because the override behavior is not exposed publicly by default.

Updated files:

  • CommonUI/Source/CommonInput/Public/CommonInputSubsystem.h
  • CommonUI/Source/CommonInput/Private/CommonInputSubsystem.cpp
  • CommonUI/Source/CommonInput/Private/CommonInputPreprocessor.cpp

CommonInputSubsystem.h

Screenshot

UFUNCTION(BlueprintCallable, Category = CommonInputSubsystem)
const FName GetCurrentRawGamepadName() const;

UFUNCTION(BlueprintCallable, Category = CommonInputSubsystem)
const bool IsGamepadTypeOverridden() const;

UFUNCTION(BlueprintCallable, Category = CommonInputSubsystem)
void SetGamepadInputType(const FName InGamepadInputType, bool bKeepRaw=true);

UFUNCTION(BlueprintCallable, Category = CommonInputSubsystem)
void SetGamepadRawInputType(const FName InGamepadInputType);

UFUNCTION(BlueprintCallable, Category = CommonInputSubsystem)
void SetGamepadInputTypeOverridden(bool bValue, FName InGamepadInputType);

...

UPROPERTY(Transient)
bool bOverriddenGamepadInputType = false;

UPROPERTY(Transient)
FName RawGamepadInputType;

CommonInputSubsystem.cpp

const FName UCommonInputSubsystem::GetCurrentRawGamepadName() const
{
return RawGamepadInputType;
}

const bool UCommonInputSubsystem::IsGamepadTypeOverridden() const
{
return bOverriddenGamepadInputType;
}

void UCommonInputSubsystem::SetGamepadInputType(const FName InGamepadInputType, bool bKeepRaw)
{
if (ensure(UCommonInputPlatformSettings::Get()->CanChangeGamepadType()))
{
if (!bKeepRaw){
RawGamepadInputType = InGamepadInputType;
}
GamepadInputType = InGamepadInputType;

// Send out notifications so we update our buttons
//BroadcastLastInputDeviceChanged();
BroadcastInputMethodChanged();
}
}

void UCommonInputSubsystem::SetGamepadRawInputType(const FName InGamepadInputType)
{
RawGamepadInputType = InGamepadInputType;
}

void UCommonInputSubsystem::SetGamepadInputTypeOverridden(bool bValue, FName InGamepadInputType)
{
bOverriddenGamepadInputType = bValue;

if (bValue){
GamepadInputType = InGamepadInputType;

UE_LOG(LogCommonInput, Log, TEXT("[CommonInput] Override enabled"));

if (GetCurrentInputType() == ECommonInputType::Gamepad)
{
UE_LOG(LogCommonInput, Log, TEXT("[CommonInput] Current input is Gamepad. Setting overridden GamepadInputType: '%s'"), *InGamepadInputType.ToString());
SetGamepadInputType(InGamepadInputType);
}
} else {
UE_LOG(LogCommonInput, Log, TEXT("[CommonInput] Override disabled. Attempting to fallback to current raw gamepad name."));

if (GetCurrentInputType() == ECommonInputType::Gamepad)
{
// Only if valid and not empty
FName Raw = GetCurrentRawGamepadName();

if (!Raw.IsNone()) {
UE_LOG(LogCommonInput, Log, TEXT("[CommonInput] Fallback active. Setting GamepadInputType to raw gamepad name: '%s'"), *Raw.ToString());
SetGamepadInputType(Raw);
} else {
const UCommonInputPlatformSettings* Settings = UPlatformSettingsManager::Get().GetSettingsForPlatform<UCommonInputPlatformSettings>();
GamepadInputType = Settings->GetDefaultGamepadName();

UE_LOG(LogCommonInput, Log, TEXT("[CommonInput] No valid raw gamepad name found. Restoring default GamepadInputType: '%s'"), *GamepadInputType.ToString());

SetGamepadInputType(GamepadInputType);
}
}
}
}

CommonInputPreprocessor.cpp

In the method RefreshCurrentInputMethod, right after:

cpp LastSeenGamepadHardwareDeviceIdentifier = DeviceScope->HardwareDeviceIdentifier;

J'ai remplacé par:

const FName GamepadInputType = InputSubsystem.GetCurrentRawGamepadName();
const FName BestGamepadType = UCommonInputPlatformSettings::Get()->GetBestGamepadNameForHardware(GamepadInputType, DeviceScope->InputDeviceName, DeviceScope->HardwareDeviceIdentifier);
if (BestGamepadType != GamepadInputType)
{
UE_LOG(LogCommonInput, Log, TEXT("UCommonInputSubsystem: Autodetect changed GamepadInputType to %s"), *BestGamepadType.ToString());

// Update raw
InputSubsystem.SetGamepadRawInputType(BestGamepadType);

const bool bGamepadTypeOverridden = InputSubsystem.IsGamepadTypeOverridden();
if (!bGamepadTypeOverridden){
InputSubsystem.SetGamepadInputType(BestGamepadType);
OnGamepadChangeDetected.Broadcast(BestGamepadType);
}
}

· 3 min read
Schartier Isaac

Why I Created the Somndus Voice Culture Plugin

Unreal Engine 5 offers a powerful localization pipeline for text, UI, and subtitles, but it lacks a dedicated and flexible workflow for localized voice-over, especially when voice languages do not match the game's primary text language.

For developers who need multi-language voice acting or separate text/voice localization, the default UE5 tools quickly become restrictive.

Here is a screenshot of my free plugin usage for voice over :

Screenshot

The Limitations of Native UE5 Voice Handling

UE5 allows audio localization, but it is tightly coupled to the engine’s global localization pipeline.
This means:

  • You cannot easily manage voice lines in languages that differ from the main game language.
  • Audio localization is not treated as a standalone system.
  • Every voice language must follow the same localization routes used for text, which is not ideal for projects that separate spoken dialogue from written content.

Technically, it is possible to build a custom workflow manually, but it becomes a heavy process involving:

  • A manually maintained directory structure
  • A custom asset or database system for each language
  • Repetitive verification work to ensure each line exists in every language

This approach works, but it is tedious and scales poorly with large dialogues or multiple languages.

Why I Built Voice Culture

To experiment with a better solution, I created Voice Culture, a free plugin designed to give developers a clear and unified pipeline for multilingual voice-over inside UE5’s editor.

Some examples with my plugin

VoiceOver audio asset

Screenshot

Editor Dashboard

Screenshot

Change VoiceOver culture in game

Screenshot

The plugin introduces:

• A custom voice-over asset

A single asset can contain all voice languages at once, properly organized and easy to preview.

• Editor tools for coverage checking

Developers can inspect:

  • Which lines exist in each language
  • If a language is missing a recording
  • Whether naming conventions are respected

This removes the guesswork and manual auditing typically required in UE5 voice pipelines.

• An independent, lightweight workflow

Voice Culture does not depend on UE5’s global text localization system, allowing teams to:

  • Use different languages for text and voice
  • Replace or add audio languages without touching the localization pipeline
  • Keep multilingual audio cleanly separated from subtitles or UI text

Why It Helps Developers

The plugin’s goal is simple:
make multilingual voice-over easier, cleaner, and more scalable inside UE5.

With Voice Culture:

  • You centralize all audio languages in organized assets
  • You avoid building your own database system
  • You can track voice-over coverage directly in the editor
  • You keep text and voice pipelines independent if needed
  • You reduce the risk of missing, misnamed, or unsynchronized lines

This tool was built out of necessity, and shared freely so that other indie and professional studios don’t have to reinvent the wheel.

For the full documentation and the actual getting-started guide, visit: