feat(01-05): implement TranslationSource singleton + EN/FR resx files
- TranslationSource singleton with INotifyPropertyChanged indexer binding - PropertyChanged fires with string.Empty on culture switch (signals all bindings refresh) - Missing key returns [key] placeholder (prevents null in WPF bindings) - Strings.resx with 27 Phase 1 UI string keys (EN) - Strings.fr.resx with same 27 keys stubbed with EN text (FR completeness Phase 5) - Strings.Designer.cs ResourceManager for dotnet build compatibility - SharepointToolbox.csproj updated with EmbeddedResource metadata
This commit is contained in:
60
SharepointToolbox/Localization/Strings.Designer.cs
generated
Normal file
60
SharepointToolbox/Localization/Strings.Designer.cs
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace SharepointToolbox.Localization {
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// Auto-generated designer file for Strings.resx — do not edit manually.
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Strings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Strings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SharepointToolbox.Localization.Strings", typeof(Strings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
178
SharepointToolbox/Localization/Strings.fr.resx
Normal file
178
SharepointToolbox/Localization/Strings.fr.resx
Normal file
@@ -0,0 +1,178 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
<data name="app.title" xml:space="preserve">
|
||||
<value>SharePoint Toolbox</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="toolbar.connect" xml:space="preserve">
|
||||
<value>Connect</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="toolbar.manage" xml:space="preserve">
|
||||
<value>Manage Profiles...</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="toolbar.clear" xml:space="preserve">
|
||||
<value>Clear Session</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.permissions" xml:space="preserve">
|
||||
<value>Permissions</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.storage" xml:space="preserve">
|
||||
<value>Storage</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.search" xml:space="preserve">
|
||||
<value>File Search</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.duplicates" xml:space="preserve">
|
||||
<value>Duplicates</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.templates" xml:space="preserve">
|
||||
<value>Templates</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.bulk" xml:space="preserve">
|
||||
<value>Bulk Operations</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.structure" xml:space="preserve">
|
||||
<value>Folder Structure</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="tab.comingsoon" xml:space="preserve">
|
||||
<value>Coming soon</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="btn.cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="settings.language" xml:space="preserve">
|
||||
<value>Language</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="settings.lang.en" xml:space="preserve">
|
||||
<value>English</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="settings.lang.fr" xml:space="preserve">
|
||||
<value>French</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="settings.folder" xml:space="preserve">
|
||||
<value>Data output folder</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="settings.browse" xml:space="preserve">
|
||||
<value>Browse...</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="profile.name" xml:space="preserve">
|
||||
<value>Profile name</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="profile.url" xml:space="preserve">
|
||||
<value>Tenant URL</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="profile.clientid" xml:space="preserve">
|
||||
<value>Client ID</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="profile.add" xml:space="preserve">
|
||||
<value>Add</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="profile.rename" xml:space="preserve">
|
||||
<value>Rename</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="profile.delete" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="status.ready" xml:space="preserve">
|
||||
<value>Ready</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="status.cancelled" xml:space="preserve">
|
||||
<value>Operation cancelled</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="err.auth.failed" xml:space="preserve">
|
||||
<value>Authentication failed. Check tenant URL and Client ID.</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
<data name="err.generic" xml:space="preserve">
|
||||
<value>An error occurred. See log for details.</value>
|
||||
<!-- FR stub — Phase 5 -->
|
||||
</data>
|
||||
</root>
|
||||
148
SharepointToolbox/Localization/Strings.resx
Normal file
148
SharepointToolbox/Localization/Strings.resx
Normal file
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="app.title" xml:space="preserve">
|
||||
<value>SharePoint Toolbox</value>
|
||||
</data>
|
||||
<data name="toolbar.connect" xml:space="preserve">
|
||||
<value>Connect</value>
|
||||
</data>
|
||||
<data name="toolbar.manage" xml:space="preserve">
|
||||
<value>Manage Profiles...</value>
|
||||
</data>
|
||||
<data name="toolbar.clear" xml:space="preserve">
|
||||
<value>Clear Session</value>
|
||||
</data>
|
||||
<data name="tab.permissions" xml:space="preserve">
|
||||
<value>Permissions</value>
|
||||
</data>
|
||||
<data name="tab.storage" xml:space="preserve">
|
||||
<value>Storage</value>
|
||||
</data>
|
||||
<data name="tab.search" xml:space="preserve">
|
||||
<value>File Search</value>
|
||||
</data>
|
||||
<data name="tab.duplicates" xml:space="preserve">
|
||||
<value>Duplicates</value>
|
||||
</data>
|
||||
<data name="tab.templates" xml:space="preserve">
|
||||
<value>Templates</value>
|
||||
</data>
|
||||
<data name="tab.bulk" xml:space="preserve">
|
||||
<value>Bulk Operations</value>
|
||||
</data>
|
||||
<data name="tab.structure" xml:space="preserve">
|
||||
<value>Folder Structure</value>
|
||||
</data>
|
||||
<data name="tab.settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="tab.comingsoon" xml:space="preserve">
|
||||
<value>Coming soon</value>
|
||||
</data>
|
||||
<data name="btn.cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="settings.language" xml:space="preserve">
|
||||
<value>Language</value>
|
||||
</data>
|
||||
<data name="settings.lang.en" xml:space="preserve">
|
||||
<value>English</value>
|
||||
</data>
|
||||
<data name="settings.lang.fr" xml:space="preserve">
|
||||
<value>French</value>
|
||||
</data>
|
||||
<data name="settings.folder" xml:space="preserve">
|
||||
<value>Data output folder</value>
|
||||
</data>
|
||||
<data name="settings.browse" xml:space="preserve">
|
||||
<value>Browse...</value>
|
||||
</data>
|
||||
<data name="profile.name" xml:space="preserve">
|
||||
<value>Profile name</value>
|
||||
</data>
|
||||
<data name="profile.url" xml:space="preserve">
|
||||
<value>Tenant URL</value>
|
||||
</data>
|
||||
<data name="profile.clientid" xml:space="preserve">
|
||||
<value>Client ID</value>
|
||||
</data>
|
||||
<data name="profile.add" xml:space="preserve">
|
||||
<value>Add</value>
|
||||
</data>
|
||||
<data name="profile.rename" xml:space="preserve">
|
||||
<value>Rename</value>
|
||||
</data>
|
||||
<data name="profile.delete" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="status.ready" xml:space="preserve">
|
||||
<value>Ready</value>
|
||||
</data>
|
||||
<data name="status.cancelled" xml:space="preserve">
|
||||
<value>Operation cancelled</value>
|
||||
</data>
|
||||
<data name="err.auth.failed" xml:space="preserve">
|
||||
<value>Authentication failed. Check tenant URL and Client ID.</value>
|
||||
</data>
|
||||
<data name="err.generic" xml:space="preserve">
|
||||
<value>An error occurred. See log for details.</value>
|
||||
</data>
|
||||
</root>
|
||||
38
SharepointToolbox/Localization/TranslationSource.cs
Normal file
38
SharepointToolbox/Localization/TranslationSource.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Resources;
|
||||
|
||||
namespace SharepointToolbox.Localization;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton INotifyPropertyChanged string lookup enabling runtime culture switching without restart.
|
||||
/// WPF bindings use: Source={x:Static loc:TranslationSource.Instance}, Path=[key]
|
||||
/// On CurrentCulture change, fires PropertyChanged with empty string to signal all properties changed,
|
||||
/// which causes WPF to re-evaluate all bindings to this source.
|
||||
/// </summary>
|
||||
public class TranslationSource : INotifyPropertyChanged
|
||||
{
|
||||
public static readonly TranslationSource Instance = new();
|
||||
|
||||
private ResourceManager _resourceManager = Strings.ResourceManager;
|
||||
private CultureInfo _currentCulture = CultureInfo.CurrentUICulture;
|
||||
|
||||
private TranslationSource() { }
|
||||
|
||||
public string this[string key] =>
|
||||
_resourceManager.GetString(key, _currentCulture) ?? $"[{key}]";
|
||||
|
||||
public CultureInfo CurrentCulture
|
||||
{
|
||||
get => _currentCulture;
|
||||
set
|
||||
{
|
||||
if (Equals(_currentCulture, value)) return;
|
||||
_currentCulture = value;
|
||||
Thread.CurrentThread.CurrentUICulture = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
}
|
||||
@@ -15,6 +15,16 @@
|
||||
<Page Include="App.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Localization\Strings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Localization\Strings.fr.resx">
|
||||
<DependentUpon>Strings.resx</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
|
||||
Reference in New Issue
Block a user