Skip to content

Commit f30d368

Browse files
committed
i18n support
1 parent 4564e21 commit f30d368

19 files changed

+2123
-231
lines changed

CompactGUI/Application.xaml.vb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Imports System.IO
1+
Imports System.IO
22
Imports System.IO.Pipes
33
Imports System.Threading
44
Imports System.Windows.Threading
@@ -31,7 +31,11 @@ Partial Public Class Application
3131

3232
End Sub
3333

34+
Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
35+
' 启动时调用语言配置
36+
LanguageHelper.Initialize()
3437

38+
End Sub
3539
Private Shared Sub InitializeHost()
3640

3741
_host = Host.CreateDefaultBuilder() _

CompactGUI/CompactGUI.vbproj

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
<DebugType>none</DebugType>
3030
</PropertyGroup>
3131

32-
3332
<ItemGroup>
3433
<Import Include="System.Windows" />
3534
<Import Include="System.Windows.Controls" />
@@ -72,6 +71,22 @@
7271
</Reference>
7372
</ItemGroup>
7473

74+
<ItemGroup>
75+
<Compile Update="i18n\i18n.Designer.vb">
76+
<DesignTime>True</DesignTime>
77+
<AutoGen>True</AutoGen>
78+
<DependentUpon>i18n.resx</DependentUpon>
79+
</Compile>
80+
</ItemGroup>
81+
82+
<ItemGroup>
83+
<EmbeddedResource Update="i18n\i18n.resx">
84+
<CustomToolNamespace>i18n</CustomToolNamespace>
85+
<Generator>PublicResXFileCodeGenerator</Generator>
86+
<LastGenOutput>i18n.Designer.vb</LastGenOutput>
87+
</EmbeddedResource>
88+
</ItemGroup>
89+
7590
<Target Name="RenamePublishedExe" AfterTargets="Publish" Condition="'$(IsMonolithic)' == 'true'">
7691
<Move SourceFiles="$(PublishDir)CompactGUI.exe" DestinationFiles="$(PublishDir)CompactGUI.mono.exe" />
7792

CompactGUI/Components/Converters/IValueConverters.vb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Imports System.Globalization
1+
Imports System.Globalization
22

33
Public Class DecimalToPercentageConverter : Implements IValueConverter
44
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
@@ -88,20 +88,24 @@ End Class
8888

8989
Public Class RelativeDateConverter : Implements IValueConverter
9090
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
91+
If value Is Nothing Or Not TypeOf value Is DateTime Then
92+
Return LanguageHelper.GetString("RelativeTimeUnknown")
93+
End If
94+
9195
Dim dt = CType(value, DateTime)
9296
Dim ts As TimeSpan = DateTime.Now - dt
9397

9498
If ts > TimeSpan.FromDays(19000) Then
95-
Return String.Format("Unknown")
99+
Return LanguageHelper.GetString("RelativeTimeUnknown")
96100
End If
97101
If ts > TimeSpan.FromDays(2) Then
98-
Return String.Format("{0:0} days ago", ts.TotalDays)
102+
Return String.Format(LanguageHelper.GetString("RelativeTimeDaysAgo"), ts.TotalDays)
99103
ElseIf ts > TimeSpan.FromHours(2) Then
100-
Return String.Format("{0:0} hours ago", ts.TotalHours)
104+
Return String.Format(LanguageHelper.GetString("RelativeTimeHoursAgo"), ts.TotalHours)
101105
ElseIf ts > TimeSpan.FromMinutes(2) Then
102-
Return String.Format("{0:0} minutes ago", ts.TotalMinutes)
106+
Return String.Format(LanguageHelper.GetString("RelativeTimeMinutesAgo"), ts.TotalMinutes)
103107
Else
104-
Return "just now"
108+
Return LanguageHelper.GetString("RelativeTimeJustNow")
105109
End If
106110
End Function
107111

CompactGUI/LanguageHelper.vb

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
Imports System.Globalization
2+
Imports System.Resources
3+
Imports System.Threading
4+
Imports System.Windows.Markup
5+
Imports System.Windows.Data
6+
Imports System.Reflection
7+
8+
Public Class LanguageHelper
9+
' 支持的语言列表
10+
Private Shared ReadOnly SupportedCultures As String() = {"en-US", "zh-CN"}
11+
Private Shared resourceManager As ResourceManager = i18n.i18n.ResourceManager
12+
Private Shared currentCulture As CultureInfo = Nothing
13+
14+
Public Shared Function GetText(key As String) As String
15+
Return GetString(key)
16+
End Function
17+
18+
Public Shared Sub Initialize()
19+
Dim savedLanguage As String = ReadAppConfig("language")
20+
If Not String.IsNullOrEmpty(savedLanguage) AndAlso SupportedCultures.Contains(savedLanguage) Then
21+
ApplyCulture(savedLanguage)
22+
Else
23+
SetDefaultLanguage()
24+
End If
25+
End Sub
26+
27+
Public Shared Sub ChangeLanguage()
28+
If currentCulture Is Nothing Then
29+
currentCulture = Thread.CurrentThread.CurrentUICulture
30+
End If
31+
Dim currentLang As String = currentCulture.Name
32+
Dim nextLang As String = GetNextLanguage(currentLang)
33+
34+
ApplyCulture(nextLang)
35+
WriteAppConfig("language", nextLang)
36+
End Sub
37+
38+
Public Shared Function GetString(key As String, ParamArray args As Object()) As String
39+
Try
40+
Dim cultureToUse = If(currentCulture, Thread.CurrentThread.CurrentUICulture)
41+
Dim rawValue As String = resourceManager.GetString(key, cultureToUse)
42+
43+
If String.IsNullOrEmpty(rawValue) Then
44+
Return key
45+
ElseIf args IsNot Nothing AndAlso args.Length > 0 Then
46+
Return String.Format(cultureToUse, rawValue, args)
47+
Else
48+
Return rawValue
49+
End If
50+
Catch ex As Exception
51+
Debug.WriteLine($"获取多语言文本失败:{key},错误:{ex.Message}")
52+
Return key
53+
End Try
54+
End Function
55+
56+
Public Shared Sub ApplyCulture(cultureName As String)
57+
Try
58+
Dim culture As New CultureInfo(cultureName)
59+
Thread.CurrentThread.CurrentUICulture = culture
60+
Thread.CurrentThread.CurrentCulture = culture
61+
currentCulture = culture
62+
63+
' 额外:如果是WPF/WinForms,刷新界面(可选)
64+
' WinForms示例:Application.CurrentCulture = culture
65+
' WPF示例:FrameworkElement.LanguageProperty.OverrideMetadata(GetType(FrameworkElement), New FrameworkPropertyMetadata(XmlLanguage.GetLanguage(culture.IetfLanguageTag)))
66+
Catch ex As Exception
67+
Debug.WriteLine($"应用语言失败:{cultureName},错误:{ex.Message}")
68+
SetDefaultLanguage()
69+
End Try
70+
End Sub
71+
72+
Private Shared Function GetNextLanguage(currentLanguage As String) As String
73+
Dim currentTwoLetter = New CultureInfo(currentLanguage).TwoLetterISOLanguageName
74+
For i As Integer = 0 To SupportedCultures.Length - 1
75+
Dim langTwoLetter = New CultureInfo(SupportedCultures(i)).TwoLetterISOLanguageName
76+
If langTwoLetter = currentTwoLetter Then
77+
Return SupportedCultures((i + 1) Mod SupportedCultures.Length)
78+
End If
79+
Next
80+
Return SupportedCultures(0)
81+
End Function
82+
83+
Private Shared Sub SetDefaultLanguage()
84+
' 根据系统语言设置默认语言
85+
Dim langMapping As New Dictionary(Of String, String) From {
86+
{"en", "en-US"},
87+
{"zh", "zh-CN"}
88+
}
89+
'{"ja", "ja-JP"},
90+
'{"ko", "ko-KR"},
91+
'{"fr", "fr-FR"},
92+
'{"de", "de-DE"},
93+
'{"es", "es-ES"},
94+
'{"ru", "ru-RU"}
95+
96+
Dim systemLang As String = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToLower()
97+
Dim defaultLang As String = If(langMapping.ContainsKey(systemLang), langMapping(systemLang), "en-US")
98+
99+
' 特殊处理中文简/繁
100+
'If systemLang = "zh" Then
101+
' If Thread.CurrentThread.CurrentUICulture.Name.StartsWith("zh-TW") Then
102+
' defaultLang = "zh-TW"
103+
' ElseIf Thread.CurrentThread.CurrentUICulture.Name.StartsWith("zh-HK") Then
104+
' defaultLang = "zh-HK"
105+
' End If
106+
'End If
107+
108+
'@@@
109+
'Dim systemLang = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName
110+
'Dim defaultLang As String = "en-US"
111+
112+
''匹配多语言
113+
'Select Case systemLang.ToLower()
114+
' Case "zh" ' 中文
115+
' defaultLang = "zh-CN"
116+
' Case "en" ' 英文
117+
' defaultLang = "en-US"
118+
' Case "ja" ' 日语
119+
' defaultLang = "ja-JP"
120+
' Case "ko" ' 韩语
121+
' defaultLang = "ko-KR"
122+
' Case "fr" ' 法语
123+
' defaultLang = "fr-FR"
124+
' Case "de" ' 德语
125+
' defaultLang = "de-DE"
126+
' Case "es" ' 西班牙语
127+
' defaultLang = "es-ES"
128+
' Case "ru" ' 俄语
129+
' defaultLang = "ru-RU"
130+
' Case Else ' 未匹配语言,默认英文
131+
' defaultLang = "en-US"
132+
'End Select
133+
134+
ApplyCulture(defaultLang)
135+
WriteAppConfig("language", defaultLang)
136+
End Sub
137+
138+
Public Shared Function GetCurrentLanguage() As String
139+
Return If(currentCulture, Thread.CurrentThread.CurrentUICulture).Name
140+
End Function
141+
142+
Public Shared Function ReadAppConfig(key As String) As String
143+
Try
144+
Dim config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None)
145+
Return If(config.AppSettings.Settings(key)?.Value, String.Empty)
146+
Catch ex As Exception
147+
Debug.WriteLine($"读取配置失败:{key},错误:{ex.Message}")
148+
Return String.Empty
149+
End Try
150+
End Function
151+
152+
Public Shared Sub WriteAppConfig(key As String, value As String)
153+
Try
154+
Dim config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None)
155+
If config.AppSettings.Settings(key) IsNot Nothing Then
156+
config.AppSettings.Settings(key).Value = value
157+
Else
158+
config.AppSettings.Settings.Add(key, value)
159+
End If
160+
config.Save(System.Configuration.ConfigurationSaveMode.Modified)
161+
System.Configuration.ConfigurationManager.RefreshSection("appSettings")
162+
Catch ex As Exception
163+
Debug.WriteLine($"写入配置失败:{key},错误:{ex.Message}")
164+
End Try
165+
End Sub
166+
End Class
167+
168+
<MarkupExtensionReturnType(GetType(String))>
169+
Public Class LocalizeExtension
170+
Inherits MarkupExtension
171+
172+
Private _key As String
173+
174+
Public Sub New(key As String)
175+
_key = key
176+
End Sub
177+
178+
Public Overrides Function ProvideValue(serviceProvider As IServiceProvider) As Object
179+
Return LanguageHelper.GetString(_key)
180+
End Function
181+
End Class

CompactGUI/Views/Components/CompressionMode_Radio.xaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<RadioButton x:Class="CompressionMode_Radio"
1+
<RadioButton x:Class="CompressionMode_Radio"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -41,7 +41,8 @@
4141

4242

4343
<ui:TextBlock x:Name="estimatedSize_Text"
44-
Text="Estimated size"
44+
Text="{local:Localize CompressionModeRadioEstimatedSize}"
45+
d:Text="Estimated size"
4546
Grid.Row="0"
4647
Margin="-10,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"
4748
Foreground="{StaticResource AccentFillColorDisabledBrush}"
@@ -53,7 +54,8 @@
5354
</ui:TextBlock>
5455

5556
<ui:TextBlock x:Name="savings_Text"
56-
Text="Savings"
57+
Text="{local:Localize CompressionModeRadioSavings}"
58+
d:Text="Savings"
5759
Grid.Row="0"
5860
Margin="0,40,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"
5961
Foreground="{StaticResource AccentFillColorDisabledBrush}"
@@ -66,7 +68,7 @@
6668

6769

6870
<Grid Grid.Column="1" Visibility="{Binding IsEstimating, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToInverseVisibilityConverter}}">
69-
<ui:TextBlock Text="unknown"
71+
<ui:TextBlock Text="{local:Localize CompressionModeRadioUnknown}" d:Text="unknown"
7072
HorizontalAlignment="Right" VerticalAlignment="Center"
7173
FontSize="13" Foreground="#30FFFFFF"
7274
Visibility="{Binding BytesAfter, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ZeroCountToVisibilityConverter}, ConverterParameter=invert}" />

CompactGUI/Views/Components/FolderWatcherCard.xaml

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<UserControl x:Class="FolderWatcherCard"
1+
<UserControl x:Class="FolderWatcherCard"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -36,7 +36,8 @@
3636

3737

3838
<StackPanel Orientation="Horizontal" Grid.Row="0">
39-
<TextBlock Text="Watched Folders"
39+
<TextBlock Text="{local:Localize PageFolderWatcherCardWatchedFoldersName}"
40+
d:Text="Watched Folders"
4041
Grid.Row="0"
4142
VerticalAlignment="Top"
4243
FontSize="26"
@@ -70,16 +71,23 @@
7071
FontSize="18" FontWeight="SemiBold"
7172
Foreground="#40FFFFFF">
7273
<Run Text="{Binding Watcher.TotalSaved, Mode=OneWay, Converter={StaticResource BytesToReadableConverter}}" d:Text="51.8GB" />
73-
<Run Text="saved" />
74+
<Run Text="{local:Localize FolderWatcherCardSaved}" d:Text="saved" />
7475
</TextBlock>
7576

7677
<StackPanel Grid.Row="1" Grid.RowSpan="2"
7778
Margin="0,20,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"
7879
Orientation="Horizontal">
7980

80-
<TextBlock Text="{Binding Watcher.LastAnalysed, StringFormat=Last analysed {0}, Converter={StaticResource RelativeDateConverter}}"
81-
Margin="0,-2,0,0" VerticalAlignment="Center"
82-
d:Text="Last analysed: just now" FontSize="14" FontWeight="SemiBold" Foreground="#40FFFFFF" />
81+
<TextBlock Margin="0,-2,0,0" VerticalAlignment="Center"
82+
d:Text="Last analysed: just now"
83+
FontSize="14" FontWeight="SemiBold" Foreground="#40FFFFFF">
84+
<TextBlock.Text>
85+
<MultiBinding StringFormat="{}{0} {1}">
86+
<Binding Source="{local:Localize FolderWatcherCardLastAnalysed}" />
87+
<Binding Path="Watcher.LastAnalysed" Converter="{StaticResource RelativeDateConverter}" />
88+
</MultiBinding>
89+
</TextBlock.Text>
90+
</TextBlock>
8391
<Grid Width="45" Height="25"
8492
VerticalAlignment="Center">
8593
<ui:Button Background="Transparent" BorderThickness="0" Margin="2 0"
@@ -104,8 +112,8 @@
104112

105113
<StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" HorizontalAlignment="Right">
106114

107-
<Button Content="Cancel Background Compressor" Command="{Binding CancelBackgroundingCommand}" Visibility="{Binding Watcher.IsRunning, Converter={StaticResource BooleanToVisibilityConverter}}"/>
108-
<Button Content="Compress All Now" Command="{Binding RunWatcherCommand}" Visibility="{Binding Watcher.IsRunning, Converter={StaticResource BooleanToInverseVisibilityConverter}}"/>
115+
<Button Content="{local:Localize FolderWatcherCardCancelBackgroundCompressor}" d:Content="Cancel Background Compressor" Command="{Binding CancelBackgroundingCommand}" Visibility="{Binding Watcher.IsRunning, Converter={StaticResource BooleanToVisibilityConverter}}"/>
116+
<Button Content="{local:Localize FolderWatcherCardCompressAllNow}" d:Content="Compress All Now" Command="{Binding RunWatcherCommand}" Visibility="{Binding Watcher.IsRunning, Converter={StaticResource BooleanToInverseVisibilityConverter}}"/>
109117
</StackPanel>
110118

111119

CompactGUI/Views/Components/FolderWatcherCard.xaml.vb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Imports System.Windows.Media.Animation
1+
Imports System.Windows.Media.Animation
22

33
Public Class FolderWatcherCard : Inherits UserControl
44
Private currentlyExpandedBorder As Border = Nothing
@@ -108,4 +108,7 @@ Public Class FolderWatcherCard : Inherits UserControl
108108
End If
109109
End Sub
110110

111+
Private Sub UiWatcherListView_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
112+
113+
End Sub
111114
End Class

0 commit comments

Comments
 (0)