mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
Fix the language picker and tidy the graphics tab
Both language combos decided a change was the user's by checking IsKeyboardFocusWithin on the picked item. Selecting with the mouse leaves the focus on the combo, not on the item, so on Windows the check failed and the language never changed. The handlers now compare the picked code with the one already loaded instead of guessing who caused the event: filling the combo in selects what is loaded and stops there, a real pick differs and applies. The settings tab listed a hard-coded Polski / English pair and wrote the language back as "Polski" or "English", so picking Czech, Hungarian or Russian in the top bar was overwritten on save. It is filled from lang/ now, and writes back the selected entry. LocalizationService gains a LanguageChanged event: the main window rebuilds the captions that code builds once (train stats, panel headers, combo items) from there, so a pick in either place refreshes them. Its PropertyChanged also names the indexer "Item[]", which is what bindings listen for. Graphics tab: the track-curve quality slider moves over from Physics, the view angle shows a degree sign instead of the Polish "stopni", and the HDR tone-mapping picker is gone - only Reinhard.glsl ever shipped, so ApplyHdrShader copies that one and the starter.hdr setting is dropped.
This commit is contained in:
@@ -72,8 +72,6 @@ public sealed class Settings
|
||||
public int Width = 1280; // width
|
||||
public int Height = 720; // height
|
||||
public int BufferScalePercent = 100; // starter.bufferscale → gfx.framebuffer.fidelity
|
||||
/// <summary>0 = Reinhard, 1 = ACESFilm (Pascal ChangeHDR).</summary>
|
||||
public int HdrToneMap; // starter.hdr
|
||||
public int MaxTextureSize = 4096; // maxtexturesize
|
||||
public int MaxCabTextureSize = 4096; // maxcabtexturesize
|
||||
public int TextureFiltering = 8; // anisotropicfiltering (1/2/4/8/16)
|
||||
@@ -394,15 +392,14 @@ public sealed class Settings
|
||||
_config.Remove("gfx.framebuffer.fidelity");
|
||||
}
|
||||
|
||||
/// <summary>Copy Reinhard/ACESFilm GLSL into shaders/tonemapping.glsl when present.</summary>
|
||||
/// <summary>Copy the Reinhard GLSL into shaders/tonemapping.glsl when present.</summary>
|
||||
public void ApplyHdrShader()
|
||||
{
|
||||
// Only meaningful for full / legacy / experimental renderers (Pascal).
|
||||
if (RenderEngine is not (0 or 1 or 5))
|
||||
return;
|
||||
|
||||
string name = HdrToneMap == 0 ? "Reinhard.glsl" : "ACESFilm.glsl";
|
||||
string? src = FindBundled(name);
|
||||
string? src = FindBundled("Reinhard.glsl");
|
||||
if (src is null) return;
|
||||
|
||||
try
|
||||
@@ -495,7 +492,6 @@ public sealed class Settings
|
||||
DebugMode = c.GetBool("debugmode", false);
|
||||
VirtualShunting = c.GetBool("ai.trainman", true);
|
||||
LogMissingVehicleFiles = c.GetBool("starter.logmissingvehicles", false);
|
||||
HdrToneMap = Clamp(c.GetInt("starter.hdr", 0), 0, 1);
|
||||
|
||||
// Graphics
|
||||
RenderEngine = IndexOf(RenderEngines, c.GetString("gfxrenderer", "full"), 0);
|
||||
@@ -621,7 +617,6 @@ public sealed class Settings
|
||||
c.SetBool("debugmode", DebugMode);
|
||||
c.SetBool("ai.trainman", VirtualShunting);
|
||||
c.SetBool("starter.logmissingvehicles", LogMissingVehicleFiles);
|
||||
c.SetInt("starter.hdr", Clamp(HdrToneMap, 0, 1));
|
||||
|
||||
// Graphics
|
||||
c.Set("gfxrenderer", RenderEngines[Clamp(RenderEngine, 0, RenderEngines.Length - 1)]);
|
||||
|
||||
@@ -45,6 +45,7 @@ public partial class MainWindow : Window
|
||||
TopBar.Padding = new Thickness(0, TitleBarHeight, 0, 0);
|
||||
|
||||
PopulateLangCombo();
|
||||
App.Loc.LanguageChanged += OnLanguageChanged;
|
||||
|
||||
// Keep Start enabled/caption in sync with selection (Pascal actStartUpdate).
|
||||
AppState.Instance.Changed += UpdateStartButton;
|
||||
@@ -79,39 +80,56 @@ public partial class MainWindow : Window
|
||||
SettingsView.IsVisible = page == "settings";
|
||||
}
|
||||
|
||||
// One entry per file in lang/. The entries are language names, which are not
|
||||
// translated, so this runs once - never from OnLanguageChanged, where it
|
||||
// would clear the combo from inside its own SelectionChanged and throw.
|
||||
private void PopulateLangCombo()
|
||||
{
|
||||
LangCombo.Items.Clear();
|
||||
int selected = 0;
|
||||
var langs = LocalizationService.AvailableLanguages();
|
||||
if (langs.Count == 0)
|
||||
{
|
||||
LangCombo.Items.Add(new ComboBoxItem { Content = "English" });
|
||||
LangCombo.SelectedIndex = 0;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < langs.Count; i++)
|
||||
{
|
||||
LangCombo.Items.Add(new ComboBoxItem { Content = langs[i].Name, Tag = langs[i].Code });
|
||||
if (string.Equals(langs[i].Name, App.Loc.CurrentLanguage, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(langs[i].Code, App.Loc.CurrentLangCode, StringComparison.OrdinalIgnoreCase))
|
||||
selected = i;
|
||||
}
|
||||
LangCombo.SelectedIndex = selected;
|
||||
foreach (var lang in LocalizationService.AvailableLanguages())
|
||||
LangCombo.Items.Add(new ComboBoxItem { Content = lang.Name, Tag = lang.Code });
|
||||
if (LangCombo.ItemCount == 0)
|
||||
LangCombo.Items.Add(new ComboBoxItem { Content = "English", Tag = "en" });
|
||||
|
||||
SelectActiveLanguage();
|
||||
}
|
||||
|
||||
// Points the combo at the loaded language; a no-op when it already shows it,
|
||||
// which is the case for a pick made here.
|
||||
private void SelectActiveLanguage()
|
||||
{
|
||||
var item = LangCombo.Items.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(it => string.Equals(it.Tag as string, App.Loc.CurrentLangCode,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
?? LangCombo.Items.OfType<ComboBoxItem>().FirstOrDefault();
|
||||
|
||||
if (item != null && !ReferenceEquals(LangCombo.SelectedItem, item))
|
||||
LangCombo.SelectedItem = item;
|
||||
}
|
||||
|
||||
private void LangCombo_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ComboBox { SelectedItem: ComboBoxItem item }) return;
|
||||
if (!item.IsKeyboardFocusWithin) return; // ignore the programmatic initial set
|
||||
var lang = item.Tag as string ?? item.Content?.ToString();
|
||||
if (!string.IsNullOrEmpty(lang))
|
||||
{
|
||||
App.ApplyLanguage(lang);
|
||||
ScenariosView?.RebuildAfterLanguageChange();
|
||||
DepotView?.RebuildAfterLanguageChange();
|
||||
UpdateStartButton();
|
||||
}
|
||||
if ((LangCombo.SelectedItem as ComboBoxItem)?.Tag is not string code)
|
||||
return;
|
||||
|
||||
// Filling the combo in selects the language that is already loaded, which
|
||||
// lands here as well - comparing against the loaded one is what tells the
|
||||
// two apart, and it keeps the handler idempotent.
|
||||
if (string.Equals(code, App.Loc.CurrentLangCode, StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
App.ApplyLanguage(code);
|
||||
}
|
||||
|
||||
// Captions built in code (train stats, panel headers, combo items) are read
|
||||
// from App.Loc once, so they have to be built again. Runs for a pick made
|
||||
// here or in the settings tab, hence hanging off Loc rather off the combo.
|
||||
private void OnLanguageChanged()
|
||||
{
|
||||
SelectActiveLanguage();
|
||||
ScenariosView?.RebuildAfterLanguageChange();
|
||||
DepotView?.RebuildAfterLanguageChange();
|
||||
UpdateStartButton();
|
||||
}
|
||||
|
||||
private void HelpButton_OnClick(object? sender, RoutedEventArgs e)
|
||||
|
||||
@@ -29,6 +29,13 @@ public class LocalizationService : INotifyPropertyChanged
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Raised after a language file is loaded. XAML bindings refresh themselves
|
||||
/// through <see cref="PropertyChanged"/>; this is for captions that code
|
||||
/// builds once and has to build again.
|
||||
/// </summary>
|
||||
public event Action? LanguageChanged;
|
||||
|
||||
/// <summary>Display name of the active language (e.g. "English", "Polski").</summary>
|
||||
public string CurrentLanguage { get; private set; } = "English";
|
||||
|
||||
@@ -113,7 +120,11 @@ public class LocalizationService : INotifyPropertyChanged
|
||||
CurrentLanguage = "English";
|
||||
}
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
|
||||
// "Item[]" is the indexer name bindings listen for; the empty name asks
|
||||
// every binding on this object to re-read.
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]"));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
|
||||
LanguageChanged?.Invoke();
|
||||
return CurrentLanguage;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,9 @@
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label Content="{Binding [Language], Source={x:Static starterNg:App.Loc}}" VerticalAlignment="Center" FontSize="14" />
|
||||
<ComboBox x:Name="ChangeLanguageCb" MinWidth="350" MaxWidth="350" SelectedIndex="1" SelectionChanged="LanguageComboBox_OnSelectionChanged">
|
||||
<ComboBoxItem>Polski</ComboBoxItem>
|
||||
<ComboBoxItem>English</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<!-- Filled from the lang/ folder in the constructor. -->
|
||||
<ComboBox x:Name="ChangeLanguageCb" MinWidth="350" MaxWidth="350"
|
||||
SelectionChanged="LanguageComboBox_OnSelectionChanged" />
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="FullscreenCb" Content="{Binding [Fullscreen], Source={x:Static starterNg:App.Loc}}" />
|
||||
@@ -130,13 +129,6 @@
|
||||
<ComboBoxItem Content="{Binding [REExperimental], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [HdrToneMap], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="HdrToneMapCb" MinWidth="150" MaxWidth="150" SelectedIndex="0">
|
||||
<ComboBoxItem Content="Reinhard" />
|
||||
<ComboBoxItem Content="ACES Film" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [Resolution], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="ResolutionCb" MinWidth="150" MaxWidth="150" SelectedIndex="0">
|
||||
@@ -188,6 +180,11 @@
|
||||
<Slider x:Name="RenderRangeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="3" Value="1" />
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [TrackCurvesQuality], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="TrackCurvesSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4" TickFrequency="1" IsSnapToTickEnabled="True" Value="4" />
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<CheckBox x:Name="FpsLimitEnableCb" Content="{Binding [FpsLimitEnable], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="FpsLimitSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
@@ -258,7 +255,7 @@
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [FieldOfView], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="fovSlider" Minimum="5" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="110" TickFrequency="5" IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding #fovSlider.Value, StringFormat='{}{0} stopni'}" />
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding #fovSlider.Value, StringFormat='{}{0}°'}" />
|
||||
</StackPanel>
|
||||
<CheckBox x:Name="RenderScreensCb" Content="{Binding [RenderScreens], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="RenderScreensThreadCb" Content="{Binding [RenderScreensThread], Source={x:Static starterNg:App.Loc}}" />
|
||||
@@ -270,11 +267,6 @@
|
||||
<TabItem Header="{Binding [Physics], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [TrackCurvesQuality], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="TrackCurvesSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4" TickFrequency="1" IsSnapToTickEnabled="True" Value="4" />
|
||||
</StackPanel>
|
||||
<CheckBox x:Name="PhysicsAccuracyCb" Content="{Binding [PhysicsAccuracyIncreased], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="PantographBreakCb" Content="{Binding [BreakingPantographPossibility], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="OverheadOnlyCb" Content="{Binding [PowerOverheadLineOnly], Source={x:Static starterNg:App.Loc}}" />
|
||||
|
||||
@@ -12,6 +12,7 @@ using Avalonia.Layout;
|
||||
using Avalonia.Markup.Xaml.Styling;
|
||||
using Avalonia.Media;
|
||||
using StarterNG.Classes;
|
||||
using StarterNG.Services;
|
||||
// Disambiguate from Avalonia.Input.KeyBinding (a command-input binding) imported above.
|
||||
using KeyBinding = StarterNG.Classes.KeyBinding;
|
||||
|
||||
@@ -34,11 +35,10 @@ public partial class Settings : UserControl
|
||||
shaderResolutionSlider_OnValueChanged(null, null);
|
||||
};
|
||||
|
||||
ChangeLanguageCb.SelectedIndex = App.Loc.CurrentLanguage switch
|
||||
{
|
||||
"Polski" => 0,
|
||||
_ => 1
|
||||
};
|
||||
FillLanguageCombo();
|
||||
// The other picker is in the main window's top bar; both follow whatever
|
||||
// language ends up loaded.
|
||||
App.Loc.LanguageChanged += SelectActiveLanguage;
|
||||
|
||||
FeedbackCb.SelectionChanged += FeedbackCb_OnSelectionChanged;
|
||||
FpsLimitEnableCb.IsCheckedChanged += (_, _) => FpsLimitSlider.IsEnabled = IsChecked(FpsLimitEnableCb);
|
||||
@@ -143,7 +143,7 @@ public partial class Settings : UserControl
|
||||
PopulateExeCombo();
|
||||
|
||||
// General
|
||||
ChangeLanguageCb.SelectedIndex = s.Language == "Polski" ? 0 : 1;
|
||||
SelectActiveLanguage();
|
||||
FullscreenCb.IsChecked = s.Fullscreen;
|
||||
PauseInactiveCb.IsChecked = s.PauseWhenInactive;
|
||||
PauseStartCb.IsChecked = s.PauseOnStart;
|
||||
@@ -177,7 +177,6 @@ public partial class Settings : UserControl
|
||||
|
||||
// Graphics
|
||||
RenderEngineCb.SelectedIndex = s.RenderEngine;
|
||||
HdrToneMapCb.SelectedIndex = Clamp(s.HdrToneMap, 0, 1);
|
||||
SelectResolution(s.Width, s.Height);
|
||||
bufferScale.Value = s.BufferScalePercent;
|
||||
textureResolutionSlider.Value = Log2(s.MaxTextureSize, 12);
|
||||
@@ -264,7 +263,10 @@ public partial class Settings : UserControl
|
||||
var s = StarterNG.Classes.Settings.Instance;
|
||||
|
||||
// General
|
||||
s.Language = ChangeLanguageCb.SelectedIndex == 0 ? "Polski" : "English";
|
||||
// Keep the loaded language rather than writing a wrong one over it when
|
||||
// the combo has no valid pick.
|
||||
s.Language = (ChangeLanguageCb.SelectedItem as ComboBoxItem)?.Content?.ToString()
|
||||
?? App.Loc.CurrentLanguage;
|
||||
s.Fullscreen = IsChecked(FullscreenCb);
|
||||
s.PauseWhenInactive = IsChecked(PauseInactiveCb);
|
||||
s.PauseOnStart = IsChecked(PauseStartCb);
|
||||
@@ -297,7 +299,6 @@ public partial class Settings : UserControl
|
||||
|
||||
// Graphics
|
||||
s.RenderEngine = Math.Max(0, RenderEngineCb.SelectedIndex);
|
||||
s.HdrToneMap = Clamp(HdrToneMapCb.SelectedIndex, 0, 1);
|
||||
ReadResolution(s);
|
||||
s.BufferScalePercent = (int)bufferScale.Value;
|
||||
s.MaxTextureSize = 1 << (int)textureResolutionSlider.Value;
|
||||
@@ -454,14 +455,39 @@ public partial class Settings : UserControl
|
||||
shaderResolution.Text = $"{resolution} px";
|
||||
}
|
||||
|
||||
// One entry per file in the lang/ folder, so every translation is reachable
|
||||
// (the list used to be a hard-coded Polski / English pair).
|
||||
private void FillLanguageCombo()
|
||||
{
|
||||
ChangeLanguageCb.Items.Clear();
|
||||
foreach (var lang in LocalizationService.AvailableLanguages())
|
||||
ChangeLanguageCb.Items.Add(new ComboBoxItem { Content = lang.Name, Tag = lang.Code });
|
||||
if (ChangeLanguageCb.ItemCount == 0)
|
||||
ChangeLanguageCb.Items.Add(new ComboBoxItem { Content = "English", Tag = "en" });
|
||||
SelectActiveLanguage();
|
||||
}
|
||||
|
||||
// Points the combo at the language currently loaded. The selection change
|
||||
// lands in the handler below, which sees it is already loaded and stops.
|
||||
private void SelectActiveLanguage()
|
||||
{
|
||||
var item = ChangeLanguageCb.Items.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(it => string.Equals(it.Tag as string, App.Loc.CurrentLangCode,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
?? ChangeLanguageCb.Items.OfType<ComboBoxItem>().FirstOrDefault();
|
||||
|
||||
if (item != null && !ReferenceEquals(ChangeLanguageCb.SelectedItem, item))
|
||||
ChangeLanguageCb.SelectedItem = item;
|
||||
}
|
||||
|
||||
private void LanguageComboBox_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not ComboBox { SelectedItem: ComboBoxItem item }) return;
|
||||
if (!item.IsKeyboardFocusWithin) return; // ignore programmatic (ApplyToUi) changes
|
||||
var lang = item.Content?.ToString();
|
||||
if (string.IsNullOrEmpty(lang)) return;
|
||||
if ((ChangeLanguageCb.SelectedItem as ComboBoxItem)?.Tag is not string code)
|
||||
return;
|
||||
if (string.Equals(code, App.Loc.CurrentLangCode, StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
App.ApplyLanguage(lang);
|
||||
App.ApplyLanguage(code);
|
||||
}
|
||||
|
||||
private void FeedbackCb_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) =>
|
||||
|
||||
@@ -223,7 +223,6 @@
|
||||
<String key="Yes">Ano</String>
|
||||
<String key="No">Ne</String>
|
||||
<String key="TabInfo">Informace</String>
|
||||
<String key="HdrToneMap">Mapování tónů HDR</String>
|
||||
<String key="SettingsFileChangedTitle">Nastavení</String>
|
||||
<String key="SettingsFileChanged">Soubor eu07.ini byl změněn mimo starter. Načíst jej znovu?</String>
|
||||
<String key="AddAllCategory">Přidat celou kategorii</String>
|
||||
|
||||
@@ -223,7 +223,6 @@
|
||||
<String key="Yes">Yes</String>
|
||||
<String key="No">No</String>
|
||||
<String key="TabInfo">Info</String>
|
||||
<String key="HdrToneMap">HDR tone mapping</String>
|
||||
<String key="SettingsFileChangedTitle">Settings</String>
|
||||
<String key="SettingsFileChanged">eu07.ini was changed outside the starter. Reload it?</String>
|
||||
<String key="AddAllCategory">Add whole category</String>
|
||||
|
||||
@@ -223,7 +223,6 @@
|
||||
<String key="Yes">Igen</String>
|
||||
<String key="No">Nem</String>
|
||||
<String key="TabInfo">Információ</String>
|
||||
<String key="HdrToneMap">HDR tónusleképezés</String>
|
||||
<String key="SettingsFileChangedTitle">Beállítások</String>
|
||||
<String key="SettingsFileChanged">Az eu07.ini fájl a starteren kívül módosult. Újratölti?</String>
|
||||
<String key="AddAllCategory">Teljes kategória hozzáadása</String>
|
||||
|
||||
@@ -223,7 +223,6 @@
|
||||
<String key="Yes">Tak</String>
|
||||
<String key="No">Nie</String>
|
||||
<String key="TabInfo">Informacje</String>
|
||||
<String key="HdrToneMap">Tone mapping HDR</String>
|
||||
<String key="SettingsFileChangedTitle">Ustawienia</String>
|
||||
<String key="SettingsFileChanged">Plik eu07.ini został zmieniony poza starterem. Wczytać ponownie?</String>
|
||||
<String key="AddAllCategory">Dodaj całą kategorię</String>
|
||||
|
||||
@@ -223,7 +223,6 @@
|
||||
<String key="Yes">Да</String>
|
||||
<String key="No">Нет</String>
|
||||
<String key="TabInfo">Информация</String>
|
||||
<String key="HdrToneMap">Тональная компрессия HDR</String>
|
||||
<String key="SettingsFileChangedTitle">Настройки</String>
|
||||
<String key="SettingsFileChanged">Файл eu07.ini изменён вне стартера. Загрузить заново?</String>
|
||||
<String key="AddAllCategory">Добавить всю категорию</String>
|
||||
|
||||
Reference in New Issue
Block a user