Ava UI: RTL Language Support (#5619)

* Add Hebrew locale files to ItemGroups

* Align all windows RTL for testing

This should be controlled with a binding that selects the appropriate layout based on current language

* Update FlowDirection as Locale changes

* Fix Settings NavigationViewItem FlowDirection

* Fix remaining text

* Fix input menu directionality

* Fix RTL not rendering

* Fix rebase errors
This commit is contained in:
Isaac Marovitz 2024-01-13 00:42:42 +00:00 committed by GitHub
parent f037fcba9a
commit 1a45dc8df8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 60 additions and 29 deletions

View file

@ -16,8 +16,10 @@ namespace Ryujinx.Ava.Common.Locale
private readonly Dictionary<LocaleKeys, string> _localeStrings; private readonly Dictionary<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings; private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues; private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
private string _localeLanguageCode;
public static LocaleManager Instance { get; } = new(); public static LocaleManager Instance { get; } = new();
public event Action LocaleChanged;
public LocaleManager() public LocaleManager()
{ {
@ -104,6 +106,15 @@ namespace Ryujinx.Ava.Common.Locale
} }
} }
public bool IsRTL()
{
return _localeLanguageCode switch
{
"he_IL" => true,
_ => false
};
}
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values) public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
{ {
_dynamicValues[key] = values; _dynamicValues[key] = values;
@ -124,6 +135,9 @@ namespace Ryujinx.Ava.Common.Locale
{ {
this[item.Key] = item.Value; this[item.Key] = item.Value;
} }
_localeLanguageCode = languageCode;
LocaleChanged?.Invoke();
} }
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode) private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)

View file

@ -119,6 +119,7 @@
<None Remove="Assets\Locales\en_US.json" /> <None Remove="Assets\Locales\en_US.json" />
<None Remove="Assets\Locales\es_ES.json" /> <None Remove="Assets\Locales\es_ES.json" />
<None Remove="Assets\Locales\fr_FR.json" /> <None Remove="Assets\Locales\fr_FR.json" />
<None Remove="Assets\Locales\he_IL.json" />
<None Remove="Assets\Locales\de_DE.json" /> <None Remove="Assets\Locales\de_DE.json" />
<None Remove="Assets\Locales\it_IT.json" /> <None Remove="Assets\Locales\it_IT.json" />
<None Remove="Assets\Locales\ja_JP.json" /> <None Remove="Assets\Locales\ja_JP.json" />
@ -143,6 +144,7 @@
<EmbeddedResource Include="Assets\Locales\en_US.json" /> <EmbeddedResource Include="Assets\Locales\en_US.json" />
<EmbeddedResource Include="Assets\Locales\es_ES.json" /> <EmbeddedResource Include="Assets\Locales\es_ES.json" />
<EmbeddedResource Include="Assets\Locales\fr_FR.json" /> <EmbeddedResource Include="Assets\Locales\fr_FR.json" />
<EmbeddedResource Include="Assets\Locales\he_IL.json" />
<EmbeddedResource Include="Assets\Locales\de_DE.json" /> <EmbeddedResource Include="Assets\Locales\de_DE.json" />
<EmbeddedResource Include="Assets\Locales\it_IT.json" /> <EmbeddedResource Include="Assets\Locales\it_IT.json" />
<EmbeddedResource Include="Assets\Locales\ja_JP.json" /> <EmbeddedResource Include="Assets\Locales\ja_JP.json" />

View file

@ -86,17 +86,17 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
FontWeight="Bold" FontWeight="Bold"
Text="{Binding TitleName}" Text="{Binding TitleName}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Developer}" Text="{Binding Developer}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Version}" Text="{Binding Version}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
</Border> </Border>
@ -110,12 +110,12 @@
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding TitleId}" Text="{Binding TitleId}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding FileExtension}" Text="{Binding FileExtension}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
@ -127,17 +127,17 @@
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding TimePlayedString}" Text="{Binding TimePlayedString}"
TextAlignment="Right" TextAlignment="End"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding LastPlayedString, Converter={helpers:LocalizedNeverConverter}}" Text="{Binding LastPlayedString, Converter={helpers:LocalizedNeverConverter}}"
TextAlignment="Right" TextAlignment="End"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding FileSizeString}" Text="{Binding FileSizeString}"
TextAlignment="Right" TextAlignment="End"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<ui:SymbolIcon <ui:SymbolIcon

View file

@ -7,5 +7,6 @@
d:DesignWidth="800" d:DesignWidth="800"
d:DesignHeight="450" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Renderer.RendererHost" x:Class="Ryujinx.Ava.UI.Renderer.RendererHost"
FlowDirection="LeftToRight"
Focusable="True"> Focusable="True">
</UserControl> </UserControl>

View file

@ -218,6 +218,7 @@
<Grid <Grid
Name="SettingButtons" Name="SettingButtons"
MinHeight="450" MinHeight="450"
FlowDirection="LeftToRight"
IsVisible="{Binding ShowSettings}"> IsVisible="{Binding ShowSettings}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />

View file

@ -88,7 +88,7 @@
IsVisible="{Binding !ShowLoadProgress}" IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="VsyncStatus_PointerReleased" PointerReleased="VsyncStatus_PointerReleased"
Text="VSync" Text="VSync"
TextAlignment="Left" /> TextAlignment="Start" />
<Border <Border
Width="2" Width="2"
Height="12" Height="12"
@ -105,7 +105,7 @@
IsVisible="{Binding !ShowLoadProgress}" IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="DockedStatus_PointerReleased" PointerReleased="DockedStatus_PointerReleased"
Text="{Binding DockedStatusText}" Text="{Binding DockedStatusText}"
TextAlignment="Left" /> TextAlignment="Start" />
<Border <Border
Width="2" Width="2"
Height="12" Height="12"
@ -225,7 +225,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}" IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GameStatusText}" Text="{Binding GameStatusText}"
TextAlignment="Left" /> TextAlignment="Start" />
<Border <Border
Width="2" Width="2"
Height="12" Height="12"
@ -240,7 +240,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}" IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding FifoStatusText}" Text="{Binding FifoStatusText}"
TextAlignment="Left" /> TextAlignment="Start" />
<Border <Border
Width="2" Width="2"
Height="12" Height="12"
@ -255,7 +255,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}" IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding BackendText}" Text="{Binding BackendText}"
TextAlignment="Left" /> TextAlignment="Start" />
<Border <Border
Width="2" Width="2"
Height="12" Height="12"
@ -270,7 +270,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}" IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GpuNameText}" Text="{Binding GpuNameText}"
TextAlignment="Left" /> TextAlignment="Start" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Grid.Column="3" Grid.Column="3"

View file

@ -27,7 +27,7 @@
Grid.Row="0" Grid.Row="0"
TextWrapping="Wrap" TextWrapping="Wrap"
HorizontalAlignment="Left" HorizontalAlignment="Left"
TextAlignment="Left" TextAlignment="Start"
Text="{locale:Locale ProfileImageSelectionNote}" /> Text="{locale:Locale ProfileImageSelectionNote}" />
<StackPanel <StackPanel
Grid.Row="2" Grid.Row="2"

View file

@ -49,7 +49,7 @@
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding UserId}" Text="{Binding UserId}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<Button Grid.Column="1" <Button Grid.Column="1"
HorizontalAlignment="Right" HorizontalAlignment="Right"

View file

@ -238,7 +238,7 @@
<TextBlock <TextBlock
FontSize="10" FontSize="10"
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}" Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
TextAlignment="Right" TextAlignment="End"
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" /> ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
</Button> </Button>
</StackPanel> </StackPanel>

View file

@ -158,7 +158,7 @@
FontWeight="Bold" FontWeight="Bold"
IsVisible="{Binding ShowLoadProgress}" IsVisible="{Binding ShowLoadProgress}"
Text="{Binding LoadHeading}" Text="{Binding LoadHeading}"
TextAlignment="Left" TextAlignment="Start"
TextWrapping="Wrap" TextWrapping="Wrap"
MaxWidth="500" /> MaxWidth="500" />
<Border <Border
@ -192,7 +192,7 @@
FontSize="18" FontSize="18"
IsVisible="{Binding ShowLoadProgress}" IsVisible="{Binding ShowLoadProgress}"
Text="{Binding CacheLoadStatus}" Text="{Binding CacheLoadStatus}"
TextAlignment="Left" TextAlignment="Start"
MaxWidth="500" /> MaxWidth="500" />
</Grid> </Grid>
</Grid> </Grid>

View file

@ -101,6 +101,9 @@
<Style Selector="Grid#PlaceholderGrid"> <Style Selector="Grid#PlaceholderGrid">
<Setter Property="Height" Value="40" /> <Setter Property="Height" Value="40" />
</Style> </Style>
<Style Selector="ui|NavigationViewItem ui|SymbolIcon">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
</ui:NavigationView.Styles> </ui:NavigationView.Styles>
</ui:NavigationView> </ui:NavigationView>
<ReversibleStackPanel <ReversibleStackPanel

View file

@ -1,7 +1,9 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -22,6 +24,14 @@ namespace Ryujinx.Ava.UI.Windows
Icon = new WindowIcon(stream); Icon = new WindowIcon(stream);
stream.Position = 0; stream.Position = 0;
IconImage = new Bitmap(stream); IconImage = new Bitmap(stream);
LocaleManager.Instance.LocaleChanged += LocaleChanged;
LocaleChanged();
}
private void LocaleChanged()
{
FlowDirection = LocaleManager.Instance.IsRTL() ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
} }
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)