620 lines
27 KiB
C++
620 lines
27 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
#include "pch.h"
|
|
#include "LocalizationService.h"
|
|
#include "LocalizationSettings.h"
|
|
#include "AppResourceProvider.h"
|
|
|
|
using namespace CalculatorApp::Common;
|
|
using namespace CalculatorApp::Common::LocalizationServiceProperties;
|
|
using namespace Concurrency;
|
|
using namespace Platform;
|
|
using namespace Platform::Collections;
|
|
using namespace std;
|
|
using namespace Windows::ApplicationModel::Resources;
|
|
using namespace Windows::ApplicationModel::Resources::Core;
|
|
using namespace Windows::Foundation;
|
|
using namespace Windows::Foundation::Collections;
|
|
using namespace Windows::Globalization;
|
|
using namespace Windows::Globalization::DateTimeFormatting;
|
|
using namespace Windows::Globalization::Fonts;
|
|
using namespace Windows::Globalization::NumberFormatting;
|
|
using namespace Windows::System::UserProfile;
|
|
using namespace Windows::UI::Text;
|
|
using namespace Windows::UI::Xaml;
|
|
using namespace Windows::UI::Xaml::Controls;
|
|
using namespace Windows::UI::Xaml::Controls::Primitives;
|
|
using namespace Windows::UI::Xaml::Documents;
|
|
using namespace Windows::UI::Xaml::Media;
|
|
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontType);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontSize);
|
|
|
|
static reader_writer_lock s_locServiceInstanceLock;
|
|
|
|
LocalizationService ^ LocalizationService::s_singletonInstance = nullptr;
|
|
|
|
// Resources for the engine use numbers as keys. It's inconvenient, but also difficult to
|
|
// change given that the engine heavily relies on perfect ordering of certain elements.
|
|
// The key for open parenthesis, '(', is "48".
|
|
static constexpr auto s_openParenResourceKey = L"48";
|
|
|
|
LocalizationService ^ LocalizationService::GetInstance()
|
|
{
|
|
if (s_singletonInstance == nullptr)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_locServiceInstanceLock);
|
|
|
|
if (s_singletonInstance == nullptr)
|
|
{
|
|
s_singletonInstance = ref new LocalizationService(nullptr);
|
|
}
|
|
}
|
|
return s_singletonInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replace (or create) the single instance of this singleton class by one with the language passed as parameter
|
|
/// </summary>
|
|
/// <param name="language">RFC-5646 identifier of the language to use</param>
|
|
/// <remarks>
|
|
/// Should only be used for test purpose
|
|
/// </remarks>
|
|
void LocalizationService::OverrideWithLanguage(_In_ const wchar_t * const language)
|
|
{
|
|
s_singletonInstance = ref new LocalizationService(language);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="overridedLanguage">RFC-5646 identifier of the language to use, if null, will use the current language of the system</param>
|
|
LocalizationService::LocalizationService(_In_ const wchar_t * const overridedLanguage)
|
|
{
|
|
m_isLanguageOverrided = overridedLanguage != nullptr;
|
|
m_language = m_isLanguageOverrided ? ref new Platform::String(overridedLanguage) : ApplicationLanguages::Languages->GetAt(0);
|
|
m_flowDirection = ResourceContext::GetForViewIndependentUse()->QualifierValues->Lookup(L"LayoutDirection")
|
|
!= L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight;
|
|
wstring localeName = wstring(m_language->Data());
|
|
localeName += L".UTF8";
|
|
|
|
try
|
|
{
|
|
// Convert wstring to string for locale
|
|
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), NULL, 0, NULL, NULL);
|
|
string localeNameStr(size_needed, 0);
|
|
WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), &localeNameStr[0], size_needed, NULL, NULL);
|
|
|
|
m_locale = locale(localeNameStr.data());
|
|
}
|
|
catch (...)
|
|
{
|
|
m_locale = locale("");
|
|
}
|
|
auto resourceLoader = AppResourceProvider::GetInstance();
|
|
m_fontFamilyOverride = resourceLoader->GetResourceString(L"LocalizedFontFamilyOverride");
|
|
|
|
String ^ reserved = L"RESERVED_FOR_FONTLOC";
|
|
|
|
m_overrideFontApiValues = ((m_fontFamilyOverride != nullptr) && (m_fontFamilyOverride != reserved));
|
|
if (m_overrideFontApiValues)
|
|
{
|
|
String ^ localizedUICaptionFontSizeFactorOverride = resourceLoader->GetResourceString(L"LocalizedUICaptionFontSizeFactorOverride");
|
|
String ^ localizedUITextFontSizeFactorOverride = resourceLoader->GetResourceString(L"LocalizedUITextFontSizeFactorOverride");
|
|
String ^ localizedFontWeightOverride = resourceLoader->GetResourceString(L"LocalizedFontWeightOverride");
|
|
|
|
// If any of the font overrides are modified then all of them need to be modified
|
|
assert(localizedFontWeightOverride != reserved);
|
|
assert(localizedUITextFontSizeFactorOverride != reserved);
|
|
assert(localizedUICaptionFontSizeFactorOverride != reserved);
|
|
|
|
m_fontWeightOverride = ParseFontWeight(localizedFontWeightOverride);
|
|
m_uiTextFontScaleFactorOverride = _wtof(localizedUITextFontSizeFactorOverride->Data());
|
|
m_uiCaptionFontScaleFactorOverride = _wtof(localizedUICaptionFontSizeFactorOverride->Data());
|
|
}
|
|
|
|
m_fontGroup = ref new LanguageFontGroup(m_language);
|
|
}
|
|
|
|
FontWeight LocalizationService::ParseFontWeight(String ^ fontWeight)
|
|
{
|
|
wstring weight = fontWeight->Data();
|
|
transform(weight.begin(), weight.end(), weight.begin(), towlower);
|
|
fontWeight = ref new String(weight.c_str());
|
|
|
|
if (fontWeight == "black")
|
|
{
|
|
return FontWeights::Black;
|
|
}
|
|
else if (fontWeight == "bold")
|
|
{
|
|
return FontWeights::Bold;
|
|
}
|
|
else if (fontWeight == "extrablack")
|
|
{
|
|
return FontWeights::ExtraBlack;
|
|
}
|
|
else if (fontWeight == "extrabold")
|
|
{
|
|
return FontWeights::ExtraBold;
|
|
}
|
|
else if (fontWeight == "extralight")
|
|
{
|
|
return FontWeights::ExtraLight;
|
|
}
|
|
else if (fontWeight == "light")
|
|
{
|
|
return FontWeights::Light;
|
|
}
|
|
else if (fontWeight == "medium")
|
|
{
|
|
return FontWeights::Medium;
|
|
}
|
|
else if (fontWeight == "normal")
|
|
{
|
|
return FontWeights::Normal;
|
|
}
|
|
else if (fontWeight == "semibold")
|
|
{
|
|
return FontWeights::SemiBold;
|
|
}
|
|
else if (fontWeight == "semilight")
|
|
{
|
|
return FontWeights::SemiLight;
|
|
}
|
|
else if (fontWeight == "thin")
|
|
{
|
|
return FontWeights::Thin;
|
|
}
|
|
else
|
|
{
|
|
throw invalid_argument("Invalid argument: fontWeight");
|
|
}
|
|
}
|
|
|
|
FlowDirection LocalizationService::GetFlowDirection()
|
|
{
|
|
return m_flowDirection;
|
|
}
|
|
|
|
bool LocalizationService::IsRtlLayout()
|
|
{
|
|
return m_flowDirection == FlowDirection::RightToLeft;
|
|
}
|
|
|
|
String ^ LocalizationService::GetLanguage()
|
|
{
|
|
return m_language;
|
|
}
|
|
|
|
bool LocalizationService::GetOverrideFontApiValues()
|
|
{
|
|
return m_overrideFontApiValues;
|
|
}
|
|
|
|
FontFamily ^ LocalizationService::GetLanguageFontFamilyForType(LanguageFontType fontType)
|
|
{
|
|
if (m_overrideFontApiValues)
|
|
{
|
|
return ref new FontFamily(m_fontFamilyOverride);
|
|
}
|
|
else
|
|
{
|
|
return ref new FontFamily(GetLanguageFont(fontType)->FontFamily);
|
|
}
|
|
}
|
|
|
|
LanguageFont ^ LocalizationService::GetLanguageFont(LanguageFontType fontType)
|
|
{
|
|
assert(!m_overrideFontApiValues);
|
|
assert(m_fontGroup);
|
|
|
|
switch (fontType)
|
|
{
|
|
case LanguageFontType::UIText:
|
|
return m_fontGroup->UITextFont;
|
|
case LanguageFontType::UICaption:
|
|
return m_fontGroup->UICaptionFont;
|
|
default:
|
|
throw std::invalid_argument("fontType");
|
|
}
|
|
}
|
|
|
|
String ^ LocalizationService::GetFontFamilyOverride()
|
|
{
|
|
assert(m_overrideFontApiValues);
|
|
return m_fontFamilyOverride;
|
|
}
|
|
|
|
FontWeight LocalizationService::GetFontWeightOverride()
|
|
{
|
|
assert(m_overrideFontApiValues);
|
|
return m_fontWeightOverride;
|
|
}
|
|
|
|
double LocalizationService::GetFontScaleFactorOverride(LanguageFontType fontType)
|
|
{
|
|
assert(m_overrideFontApiValues);
|
|
|
|
switch (fontType)
|
|
{
|
|
case LanguageFontType::UIText:
|
|
return m_uiTextFontScaleFactorOverride;
|
|
case LanguageFontType::UICaption:
|
|
return m_uiCaptionFontScaleFactorOverride;
|
|
default:
|
|
throw invalid_argument("Invalid argument: fontType");
|
|
}
|
|
}
|
|
|
|
void LocalizationService::OnFontTypePropertyChanged(DependencyObject ^ target, LanguageFontType /*oldValue*/, LanguageFontType /*newValue*/)
|
|
{
|
|
UpdateFontFamilyAndSize(target);
|
|
}
|
|
|
|
void LocalizationService::OnFontWeightPropertyChanged(DependencyObject ^ target, FontWeight /*oldValue*/, FontWeight /*newValue*/)
|
|
{
|
|
UpdateFontFamilyAndSize(target);
|
|
}
|
|
|
|
void LocalizationService::OnFontSizePropertyChanged(DependencyObject ^ target, double /*oldValue*/, double /*newValue*/)
|
|
{
|
|
UpdateFontFamilyAndSize(target);
|
|
}
|
|
|
|
void LocalizationService::UpdateFontFamilyAndSize(DependencyObject ^ target)
|
|
{
|
|
FontFamily ^ fontFamily;
|
|
FontWeight fontWeight;
|
|
bool fOverrideFontWeight = false;
|
|
double scaleFactor;
|
|
|
|
auto service = LocalizationService::GetInstance();
|
|
auto fontType = LocalizationService::GetFontType(target);
|
|
|
|
if (service->GetOverrideFontApiValues())
|
|
{
|
|
fontFamily = ref new FontFamily(service->GetFontFamilyOverride());
|
|
scaleFactor = service->GetFontScaleFactorOverride(fontType) / 100.0;
|
|
fontWeight = service->GetFontWeightOverride();
|
|
fOverrideFontWeight = true;
|
|
}
|
|
else
|
|
{
|
|
auto languageFont = service->GetLanguageFont(fontType);
|
|
fontFamily = ref new FontFamily(languageFont->FontFamily);
|
|
scaleFactor = languageFont->ScaleFactor / 100.0;
|
|
}
|
|
|
|
double sizeToUse = LocalizationService::GetFontSize(target) * scaleFactor;
|
|
|
|
auto control = dynamic_cast<Control ^>(target);
|
|
if (control)
|
|
{
|
|
control->FontFamily = fontFamily;
|
|
if (fOverrideFontWeight)
|
|
{
|
|
control->FontWeight = fontWeight;
|
|
}
|
|
if (sizeToUse != 0.0)
|
|
{
|
|
control->FontSize = sizeToUse;
|
|
}
|
|
else
|
|
{
|
|
control->ClearValue(Control::FontSizeProperty);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto textBlock = dynamic_cast<TextBlock ^>(target);
|
|
if (textBlock)
|
|
{
|
|
textBlock->FontFamily = fontFamily;
|
|
if (fOverrideFontWeight)
|
|
{
|
|
textBlock->FontWeight = fontWeight;
|
|
}
|
|
if (sizeToUse != 0.0)
|
|
{
|
|
textBlock->FontSize = sizeToUse;
|
|
}
|
|
else
|
|
{
|
|
textBlock->ClearValue(TextBlock::FontSizeProperty);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RichTextBlock ^ richTextBlock = dynamic_cast<RichTextBlock ^>(target);
|
|
if (richTextBlock)
|
|
{
|
|
richTextBlock->FontFamily = fontFamily;
|
|
if (fOverrideFontWeight)
|
|
{
|
|
richTextBlock->FontWeight = fontWeight;
|
|
}
|
|
if (sizeToUse != 0.0)
|
|
{
|
|
richTextBlock->FontSize = sizeToUse;
|
|
}
|
|
else
|
|
{
|
|
richTextBlock->ClearValue(RichTextBlock::FontSizeProperty);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TextElement ^ textElement = dynamic_cast<TextElement ^>(target);
|
|
if (textElement)
|
|
{
|
|
textElement->FontFamily = fontFamily;
|
|
if (fOverrideFontWeight)
|
|
{
|
|
textElement->FontWeight = fontWeight;
|
|
}
|
|
if (sizeToUse != 0.0)
|
|
{
|
|
textElement->FontSize = sizeToUse;
|
|
}
|
|
else
|
|
{
|
|
textElement->ClearValue(TextElement::FontSizeProperty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If successful, returns a formatter that respects the user's regional format settings,
|
|
// as configured by running intl.cpl.
|
|
DecimalFormatter ^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter() const
|
|
{
|
|
IIterable<String ^> ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
|
if (languageIdentifiers != nullptr)
|
|
{
|
|
return ref new DecimalFormatter(languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion);
|
|
}
|
|
|
|
return ref new DecimalFormatter();
|
|
}
|
|
|
|
// If successful, returns a formatter that respects the user's regional format settings,
|
|
// as configured by running intl.cpl.
|
|
//
|
|
// This helper function creates a DateTimeFormatter with a TwentyFour hour clock
|
|
DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format) const
|
|
{
|
|
IIterable<String ^> ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
|
if (languageIdentifiers == nullptr)
|
|
{
|
|
languageIdentifiers = ApplicationLanguages::Languages;
|
|
}
|
|
|
|
return ref new DateTimeFormatter(format, languageIdentifiers);
|
|
}
|
|
|
|
// If successful, returns a formatter that respects the user's regional format settings,
|
|
// as configured by running intl.cpl.
|
|
DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format, _In_ String ^ calendarIdentifier, _In_ String ^ clockIdentifier) const
|
|
{
|
|
IIterable<String ^> ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
|
if (languageIdentifiers == nullptr)
|
|
{
|
|
languageIdentifiers = ApplicationLanguages::Languages;
|
|
}
|
|
|
|
return ref new DateTimeFormatter(format, languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion, calendarIdentifier, clockIdentifier);
|
|
}
|
|
|
|
CurrencyFormatter ^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter() const
|
|
{
|
|
String ^ userCurrency =
|
|
(GlobalizationPreferences::Currencies->Size > 0) ? GlobalizationPreferences::Currencies->GetAt(0) : StringReference(DefaultCurrencyCode.data());
|
|
|
|
IIterable<String ^> ^ languageIdentifiers = GetLanguageIdentifiers();
|
|
if (languageIdentifiers == nullptr)
|
|
{
|
|
languageIdentifiers = ApplicationLanguages::Languages;
|
|
}
|
|
|
|
auto currencyFormatter = ref new CurrencyFormatter(userCurrency, languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion);
|
|
|
|
int fractionDigits = LocalizationSettings::GetInstance().GetCurrencyTrailingDigits();
|
|
currencyFormatter->FractionDigits = fractionDigits;
|
|
|
|
return currencyFormatter;
|
|
}
|
|
|
|
IIterable<String ^> ^ LocalizationService::GetLanguageIdentifiers() const
|
|
{
|
|
WCHAR currentLocale[LOCALE_NAME_MAX_LENGTH] = {};
|
|
int result = GetUserDefaultLocaleName(currentLocale, LOCALE_NAME_MAX_LENGTH);
|
|
|
|
if (m_isLanguageOverrided)
|
|
{
|
|
auto overridedLanguageList = ref new Vector<String ^>();
|
|
overridedLanguageList->Append(m_language);
|
|
return overridedLanguageList;
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
// GetUserDefaultLocaleName may return an invalid bcp47 language tag with trailing non-BCP47 friendly characters,
|
|
// which if present would start with an underscore, for example sort order
|
|
// (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx).
|
|
// Therefore, if there is an underscore in the locale name, trim all characters from the underscore onwards.
|
|
WCHAR* underscore = wcschr(currentLocale, L'_');
|
|
if (underscore != nullptr)
|
|
{
|
|
*underscore = L'\0';
|
|
}
|
|
|
|
String ^ localeString = ref new String(currentLocale);
|
|
// validate if the locale we have is valid
|
|
// otherwise we fallback to the default.
|
|
if (Language::IsWellFormed(localeString))
|
|
{
|
|
auto languageList = ref new Vector<String ^>();
|
|
languageList->Append(localeString);
|
|
return languageList;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
unordered_map<wstring, wstring> LocalizationService::GetTokenToReadableNameMap()
|
|
{
|
|
// Resources for the engine use numbers as keys. It's inconvenient, but also difficult to
|
|
// change given that the engine heavily relies on perfect ordering of certain elements.
|
|
// To compromise, we'll declare a map from engine resource key to automation name from the
|
|
// standard project resources.
|
|
static vector<pair<wstring, wstring>> s_parenEngineKeyResourceMap = { // Sine permutations
|
|
make_pair<wstring, wstring>(L"67", L"SineDegrees"),
|
|
make_pair<wstring, wstring>(L"73", L"SineRadians"),
|
|
make_pair<wstring, wstring>(L"79", L"SineGradians"),
|
|
make_pair<wstring, wstring>(L"70", L"InverseSineDegrees"),
|
|
make_pair<wstring, wstring>(L"76", L"InverseSineRadians"),
|
|
make_pair<wstring, wstring>(L"82", L"InverseSineGradians"),
|
|
make_pair<wstring, wstring>(L"25", L"HyperbolicSine"),
|
|
make_pair<wstring, wstring>(L"85", L"InverseHyperbolicSine"),
|
|
|
|
// Cosine permutations
|
|
make_pair<wstring, wstring>(L"68", L"CosineDegrees"),
|
|
make_pair<wstring, wstring>(L"74", L"CosineRadians"),
|
|
make_pair<wstring, wstring>(L"80", L"CosineGradians"),
|
|
make_pair<wstring, wstring>(L"71", L"InverseCosineDegrees"),
|
|
make_pair<wstring, wstring>(L"77", L"InverseCosineRadians"),
|
|
make_pair<wstring, wstring>(L"83", L"InverseCosineGradians"),
|
|
make_pair<wstring, wstring>(L"26", L"HyperbolicCosine"),
|
|
make_pair<wstring, wstring>(L"86", L"InverseHyperbolicCosine"),
|
|
|
|
// Tangent permutations
|
|
make_pair<wstring, wstring>(L"69", L"TangentDegrees"),
|
|
make_pair<wstring, wstring>(L"75", L"TangentRadians"),
|
|
make_pair<wstring, wstring>(L"81", L"TangentGradians"),
|
|
make_pair<wstring, wstring>(L"72", L"InverseTangentDegrees"),
|
|
make_pair<wstring, wstring>(L"78", L"InverseTangentRadians"),
|
|
make_pair<wstring, wstring>(L"84", L"InverseTangentGradians"),
|
|
make_pair<wstring, wstring>(L"27", L"HyperbolicTangent"),
|
|
make_pair<wstring, wstring>(L"87", L"InverseHyperbolicTangent"),
|
|
|
|
// Secant permutations
|
|
make_pair<wstring, wstring>(L"SecDeg", L"SecantDegrees"),
|
|
make_pair<wstring, wstring>(L"SecRad", L"SecantRadians"),
|
|
make_pair<wstring, wstring>(L"SecGrad", L"SecantGradians"),
|
|
make_pair<wstring, wstring>(L"InverseSecDeg", L"InverseSecantDegrees"),
|
|
make_pair<wstring, wstring>(L"InverseSecRad", L"InverseSecantRadians"),
|
|
make_pair<wstring, wstring>(L"InverseSecGrad", L"InverseSecantGradians"),
|
|
make_pair<wstring, wstring>(L"Sech", L"HyperbolicSecant"),
|
|
make_pair<wstring, wstring>(L"InverseSech", L"InverseHyperbolicSecant"),
|
|
|
|
// Cosecant permutations
|
|
make_pair<wstring, wstring>(L"CscDeg", L"CosecantDegrees"),
|
|
make_pair<wstring, wstring>(L"CscRad", L"CosecantRadians"),
|
|
make_pair<wstring, wstring>(L"CscGrad", L"CosecantGradians"),
|
|
make_pair<wstring, wstring>(L"InverseCscDeg", L"InverseCosecantDegrees"),
|
|
make_pair<wstring, wstring>(L"InverseCscRad", L"InverseCosecantRadians"),
|
|
make_pair<wstring, wstring>(L"InverseCscGrad", L"InverseCosecantGradians"),
|
|
make_pair<wstring, wstring>(L"Csch", L"HyperbolicCosecant"),
|
|
make_pair<wstring, wstring>(L"InverseCsch", L"InverseHyperbolicCosecant"),
|
|
|
|
// Cotangent permutations
|
|
make_pair<wstring, wstring>(L"CotDeg", L"CotangentDegrees"),
|
|
make_pair<wstring, wstring>(L"CotRad", L"CotangentRadians"),
|
|
make_pair<wstring, wstring>(L"CotGrad", L"CotangentGradians"),
|
|
make_pair<wstring, wstring>(L"InverseCotDeg", L"InverseCotangentDegrees"),
|
|
make_pair<wstring, wstring>(L"InverseCotRad", L"InverseCotangentRadians"),
|
|
make_pair<wstring, wstring>(L"InverseCotGrad", L"InverseCotangentGradians"),
|
|
make_pair<wstring, wstring>(L"Coth", L"HyperbolicCotangent"),
|
|
make_pair<wstring, wstring>(L"InverseCoth", L"InverseHyperbolicCotangent"),
|
|
|
|
// Miscellaneous Scientific functions
|
|
make_pair<wstring, wstring>(L"94", L"Factorial"),
|
|
make_pair<wstring, wstring>(L"35", L"DegreeMinuteSecond"),
|
|
make_pair<wstring, wstring>(L"28", L"NaturalLog"),
|
|
make_pair<wstring, wstring>(L"91", L"Square"),
|
|
make_pair<wstring, wstring>(L"CubeRoot", L"CubeRoot"),
|
|
make_pair<wstring, wstring>(L"Abs", L"AbsoluteValue")
|
|
};
|
|
|
|
static vector<pair<wstring, wstring>> s_noParenEngineKeyResourceMap = { // Programmer mode functions
|
|
make_pair<wstring, wstring>(L"9", L"LeftShift"),
|
|
make_pair<wstring, wstring>(L"10", L"RightShift"),
|
|
make_pair<wstring, wstring>(L"LogBaseX", L"Logx"),
|
|
|
|
// Y Root scientific function
|
|
make_pair<wstring, wstring>(L"16", L"YRoot")
|
|
};
|
|
|
|
unordered_map<wstring, wstring> tokenToReadableNameMap{};
|
|
auto resProvider = AppResourceProvider::GetInstance();
|
|
|
|
static const wstring openParen = resProvider->GetCEngineString(StringReference(s_openParenResourceKey))->Data();
|
|
|
|
for (const auto& keyPair : s_parenEngineKeyResourceMap)
|
|
{
|
|
wstring engineStr = resProvider->GetCEngineString(StringReference(keyPair.first.c_str()))->Data();
|
|
wstring automationName = resProvider->GetResourceString(StringReference(keyPair.second.c_str()))->Data();
|
|
|
|
tokenToReadableNameMap.emplace(engineStr + openParen, automationName);
|
|
}
|
|
s_parenEngineKeyResourceMap.clear();
|
|
|
|
for (const auto& keyPair : s_noParenEngineKeyResourceMap)
|
|
{
|
|
wstring engineStr = resProvider->GetCEngineString(StringReference(keyPair.first.c_str()))->Data();
|
|
wstring automationName = resProvider->GetResourceString(StringReference(keyPair.second.c_str()))->Data();
|
|
|
|
tokenToReadableNameMap.emplace(engineStr, automationName);
|
|
}
|
|
s_noParenEngineKeyResourceMap.clear();
|
|
|
|
// Also replace hyphens with "minus"
|
|
wstring minusText = resProvider->GetResourceString(L"minus")->Data();
|
|
tokenToReadableNameMap.emplace(L"-", minusText);
|
|
|
|
return tokenToReadableNameMap;
|
|
}
|
|
|
|
String ^ LocalizationService::GetNarratorReadableToken(String ^ rawToken)
|
|
{
|
|
static unordered_map<wstring, wstring> s_tokenToReadableNameMap = GetTokenToReadableNameMap();
|
|
|
|
auto itr = s_tokenToReadableNameMap.find(rawToken->Data());
|
|
if (itr == s_tokenToReadableNameMap.end())
|
|
{
|
|
return rawToken;
|
|
}
|
|
else
|
|
{
|
|
static const String ^ openParen = AppResourceProvider::GetInstance()->GetCEngineString(StringReference(s_openParenResourceKey));
|
|
return ref new String(itr->second.c_str()) + L" " + openParen;
|
|
}
|
|
}
|
|
|
|
String ^ LocalizationService::GetNarratorReadableString(String ^ rawString)
|
|
{
|
|
wstring readableString{};
|
|
|
|
wstring asWstring = rawString->Data();
|
|
for (const auto& c : asWstring)
|
|
{
|
|
readableString += LocalizationService::GetNarratorReadableToken(ref new String(&c, 1))->Data();
|
|
}
|
|
|
|
return ref new String(readableString.c_str());
|
|
}
|
|
|
|
void LocalizationService::Sort(std::vector<Platform::String ^>& source)
|
|
{
|
|
const collate<wchar_t>& coll = use_facet<collate<wchar_t>>(m_locale);
|
|
sort(source.begin(), source.end(), [&coll](Platform::String ^ str1, Platform::String ^ str2) {
|
|
return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0;
|
|
});
|
|
}
|