Skip to content

Commit

Permalink
[General]Support language selection (#34971)
Browse files Browse the repository at this point in the history
* Language setting

* spellcheck

* Set FileLocksmithContextMenu package version in AppManifest.xml

* Fix ambigious symbol build error

* Fix ambigious symbol build error #2

* Revert unneeded changes

* Improve perf

* try fix ci build
  • Loading branch information
stefansjfw authored Sep 25, 2024
1 parent 2b4b55c commit 5b616c9
Show file tree
Hide file tree
Showing 38 changed files with 754 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,7 @@ Stubless
STYLECHANGED
STYLECHANGING
subkeys
SUBLANG
sublang
subquery
Superbar
sut
Expand Down
9 changes: 9 additions & 0 deletions .pipelines/versionSetting.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,12 @@ $imageResizerContextMenuAppManifestReadFileLocation = $imageResizerContextMenuAp
$imageResizerContextMenuAppManifest.Package.Identity.Version = $versionNumber + '.0'
Write-Host "ImageResizerContextMenu version" $imageResizerContextMenuAppManifest.Package.Identity.Version
$imageResizerContextMenuAppManifest.Save($imageResizerContextMenuAppManifestWriteFileLocation);

# Set FileLocksmithContextMenu package version in AppManifest.xml
$fileLocksmithContextMenuAppManifestWriteFileLocation = $PSScriptRoot + '/../src/modules/FileLocksmith/FileLocksmithContextMenu/AppxManifest.xml';
$fileLocksmithContextMenuAppManifestReadFileLocation = $fileLocksmithContextMenuAppManifestWriteFileLocation;

[XML]$fileLocksmithContextMenuAppManifest = Get-Content $fileLocksmithContextMenuAppManifestReadFileLocation
$fileLocksmithContextMenuAppManifest.Package.Identity.Version = $versionNumber + '.0'
Write-Host "FileLocksmithContextMenu version" $fileLocksmithContextMenuAppManifest.Package.Identity.Version
$fileLocksmithContextMenuAppManifest.Save($fileLocksmithContextMenuAppManifestWriteFileLocation);
1 change: 1 addition & 0 deletions PowerToys.sln
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643
src\common\utils\HDropIterator.h = src\common\utils\HDropIterator.h
src\common\utils\HttpClient.h = src\common\utils\HttpClient.h
src\common\utils\json.h = src\common\utils\json.h
src\common\utils\language_helper.h = src\common\utils\language_helper.h
src\common\utils\logger_helper.h = src\common\utils\logger_helper.h
src\common\utils\modulesRegistry.h = src\common\utils\modulesRegistry.h
src\common\utils\MsiUtils.h = src\common\utils\MsiUtils.h
Expand Down
50 changes: 50 additions & 0 deletions src/common/ManagedCommon/LanguageHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.IO;
using System.IO.Abstractions;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ManagedCommon
{
public static class LanguageHelper
{
public const string SettingsFilePath = "\\Microsoft\\PowerToys\\";
public const string SettingsFile = "language.json";

internal sealed class OutGoingLanguageSettings
{
[JsonPropertyName("language")]
public string LanguageTag { get; set; }
}

public static string LoadLanguage()
{
FileSystem fileSystem = new FileSystem();
var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var file = localAppDataDir + SettingsFilePath + SettingsFile;

if (fileSystem.File.Exists(file))
{
try
{
Stream inputStream = fileSystem.File.Open(file, FileMode.Open);
StreamReader reader = new StreamReader(inputStream);
string data = reader.ReadToEnd();
inputStream.Close();
reader.Dispose();

return JsonSerializer.Deserialize<OutGoingLanguageSettings>(data).LanguageTag;
}
catch (Exception)
{
}
}

return string.Empty;
}
}
}
5 changes: 5 additions & 0 deletions src/common/interop/PowerToys.Interop.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@
<Midl Include="LayoutMapManaged.idl" />
<Midl Include="TwoWayPipeMessageIPCManaged.idl" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
</ItemGroup>
<ImportGroup Label="ExtensionTargets" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
Expand Down
22 changes: 22 additions & 0 deletions src/common/utils/language_helper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <filesystem>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/json.h>

namespace LanguageHelpers
{
inline std::wstring load_language()
{
std::filesystem::path languageJsonFilePath(PTSettingsHelper::get_root_save_folder_location() + L"\\language.json");

auto langJson = json::from_file(languageJsonFilePath.c_str());
if (!langJson.has_value())
{
return {};
}

std::wstring language = langJson->GetNamedString(L"language", L"").c_str();
return language;
}
}
197 changes: 184 additions & 13 deletions src/common/utils/resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,204 @@
#include <string>
#include <atlstr.h>

// Get a string from the resource file
inline std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wchar_t* fallback)
#include <common/utils/language_helper.h>


inline std::wstring get_english_fallback_string(UINT resource_id, HINSTANCE instance)
{
wchar_t* text_ptr;
auto length = LoadStringW(instance, resource_id, reinterpret_cast<wchar_t*>(&text_ptr), 0);
if (length == 0)
// Try to load en-us string as the first fallback.
WORD english_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);

ATL::CStringW english_string;
try
{
// Try to load en-us string as the first fallback.
WORD english_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
ATL::CStringW english_string;
if (!english_string.LoadStringW(instance, resource_id, english_language))
{
return {};
}
}
catch (...)
{
return {};
}

return std::wstring(english_string);
}

inline std::wstring get_resource_string_language_override(UINT resource_id, HINSTANCE instance)
{
static std::wstring language = LanguageHelpers::load_language();
unsigned lang = LANG_ENGLISH;
unsigned sublang = SUBLANG_ENGLISH_US;

if (!language.empty())
{
// Language list taken from Resources.wxs
if (language == L"ar-SA")
{
lang = LANG_ARABIC;
sublang = SUBLANG_ARABIC_SAUDI_ARABIA;
}
else if (language == L"cs-CZ")
{
lang = LANG_CZECH;
sublang = SUBLANG_CZECH_CZECH_REPUBLIC;
}
else if (language == L"de-DE")
{
lang = LANG_GERMAN;
sublang = SUBLANG_GERMAN;
}
else if (language == L"en-US")
{
lang = LANG_ENGLISH;
sublang = SUBLANG_ENGLISH_US;
}
else if (language == L"es-ES")
{
lang = LANG_SPANISH;
sublang = SUBLANG_SPANISH;
}
else if (language == L"fa-IR")
{
lang = LANG_PERSIAN;
sublang = SUBLANG_PERSIAN_IRAN;
}
else if (language == L"fr-FR")
{
lang = LANG_FRENCH;
sublang = SUBLANG_FRENCH;
}
else if (language == L"he-IL")
{
lang = LANG_HEBREW;
sublang = SUBLANG_HEBREW_ISRAEL;
}
else if (language == L"hu-HU")
{
lang = LANG_HUNGARIAN;
sublang = SUBLANG_HUNGARIAN_HUNGARY;
}
else if (language == L"it-IT")
{
lang = LANG_ITALIAN;
sublang = SUBLANG_ITALIAN;
}
else if (language == L"ja-JP")
{
lang = LANG_JAPANESE;
sublang = SUBLANG_JAPANESE_JAPAN;
}
else if (language == L"ko-KR")
{
lang = LANG_KOREAN;
sublang = SUBLANG_KOREAN;
}
else if (language == L"nl-NL")
{
lang = LANG_DUTCH;
sublang = SUBLANG_DUTCH;
}
else if (language == L"pl-PL")
{
lang = LANG_POLISH;
sublang = SUBLANG_POLISH_POLAND;
}
else if (language == L"pt-BR")
{
lang = LANG_PORTUGUESE;
sublang = SUBLANG_PORTUGUESE_BRAZILIAN;
}
else if (language == L"pt-PT")
{
lang = LANG_PORTUGUESE;
sublang = SUBLANG_PORTUGUESE;
}
else if (language == L"ru-RU")
{
lang = LANG_RUSSIAN;
sublang = SUBLANG_RUSSIAN_RUSSIA;
}
else if (language == L"sv-SE")
{
lang = LANG_SWEDISH;
sublang = SUBLANG_SWEDISH;
}
else if (language == L"tr-TR")
{
lang = LANG_TURKISH;
sublang = SUBLANG_TURKISH_TURKEY;
}
else if (language == L"uk-UA")
{
lang = LANG_UKRAINIAN;
sublang = SUBLANG_UKRAINIAN_UKRAINE;
}
else if (language == L"zh-CN")
{
lang = LANG_CHINESE_SIMPLIFIED;
sublang = SUBLANG_CHINESE_SIMPLIFIED;
}
else if (language == L"zh-TW")
{
lang = LANG_CHINESE_TRADITIONAL;
sublang = SUBLANG_CHINESE_TRADITIONAL;
}

WORD languageID = MAKELANGID(lang, sublang);
ATL::CStringW result;
try
{
if (!english_string.LoadStringW(instance, resource_id, english_language))
if (!result.LoadStringW(instance, resource_id, languageID))
{
return fallback;
return {};
}
}
catch (...)
{
return fallback;
return {};
}

return std::wstring(english_string);
if (!result.IsEmpty())
{
return std::wstring(result);
}
}

return {};
}

// Get a string from the resource file
inline std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wchar_t* fallback)
{
// Try to load en-us string as the first fallback.
std::wstring english_string = get_english_fallback_string(resource_id, instance);

std::wstring language_override_resource = get_resource_string_language_override(resource_id, instance);

if (!language_override_resource.empty())
{
return language_override_resource;
}
else
{
return { text_ptr, static_cast<std::size_t>(length) };
wchar_t* text_ptr;
auto length = LoadStringW(instance, resource_id, reinterpret_cast<wchar_t*>(&text_ptr), 0);
if (length == 0)
{
if (!english_string.empty())
{
return std::wstring(english_string);
}
else
{
return fallback;
}
}
else
{
return { text_ptr, static_cast<std::size_t>(length) };
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public partial class App : Application, IDisposable
/// </summary>
public App()
{
string appLanguage = LanguageHelper.LoadLanguage();
if (!string.IsNullOrEmpty(appLanguage))
{
Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage;
}

this.InitializeComponent();

Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public static T GetService<T>()
/// </summary>
public App()
{
string appLanguage = LanguageHelper.LoadLanguage();
if (!string.IsNullOrEmpty(appLanguage))
{
Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage;
}

this.InitializeComponent();

Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
Expand Down
2 changes: 2 additions & 0 deletions src/modules/FileLocksmith/FileLocksmithContextMenu/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#ifndef PCH_H
#define PCH_H

#include <atlbase.h>

// add headers that you want to pre-compile here
#include "framework.h"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public partial class App : Application
/// </summary>
public App()
{
string appLanguage = LanguageHelper.LoadLanguage();
if (!string.IsNullOrEmpty(appLanguage))
{
Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage;
}

Logger.InitializeLogger("\\File Locksmith\\FileLocksmithUI\\Logs");

this.InitializeComponent();
Expand Down
6 changes: 6 additions & 0 deletions src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public partial class App : Application
/// </summary>
public App()
{
string appLanguage = LanguageHelper.LoadLanguage();
if (!string.IsNullOrEmpty(appLanguage))
{
Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage;
}

InitializeComponent();

Host.HostInstance = Microsoft.Extensions.Hosting.Host.
Expand Down
Loading

0 comments on commit 5b616c9

Please sign in to comment.