| 1 | # Script to generate Localization.h from resources.resw |
| 2 | |
| 3 | |
| 4 | param( [Parameter(Mandatory=$true)] $OutFile) |
| 5 | |
| 6 | $ErrorActionPreference = "Stop" |
| 7 | |
| 8 | $resourceFolder = "$($PSScriptRoot)\..\localization\strings" |
| 9 | $defaultLanguage = "en-US" |
| 10 | $languages = @($defaultLanguage) + (Get-ChildItem -Path $resourceFolder | % { $_.name} | Where { $_ -Ne $defaultLanguage }) |
| 11 | |
| 12 | $content = @" |
| 13 | // Copyright (C) Microsoft Corporation. All rights reserved. |
| 14 | // |
| 15 | // This file is generated by generateLocalizationHeader.ps1, do not edit it manually. |
| 16 | |
| 17 | #pragma once |
| 18 | |
| 19 | #include <vector> |
| 20 | #include <stringshared.h> |
| 21 | |
| 22 | namespace wsl::shared { |
| 23 | |
| 24 | class Localization |
| 25 | { |
| 26 | public: |
| 27 | |
| 28 | #ifdef WIN32 |
| 29 | |
| 30 | #define ARGS std::make_wformat_args |
| 31 | using TChar = wchar_t; |
| 32 | |
| 33 | #else |
| 34 | |
| 35 | #define ARGS std::make_format_args |
| 36 | using TChar = char; |
| 37 | |
| 38 | #define TEXT(X) X |
| 39 | |
| 40 | #endif |
| 41 | |
| 42 | using TString = std::basic_string<TChar>; |
| 43 | |
| 44 | enum class Options |
| 45 | { |
| 46 | Default = 0, |
| 47 | DontImpersonate = 1 |
| 48 | }; |
| 49 | "@ |
| 50 | |
| 51 | function getArgumentsFromInserts |
| 52 | { |
| 53 | param( |
| 54 | [string]$Name, |
| 55 | [string]$Content) |
| 56 | |
| 57 | [System.Collections.ArrayList]$Arguments = @() |
| 58 | |
| 59 | $Content = $Content -Replace '{{', '' |
| 60 | |
| 61 | return ([regex]::Matches($Content, "{" )).count |
| 62 | } |
| 63 | |
| 64 | function generateEntry |
| 65 | { |
| 66 | param( |
| 67 | [string]$Name, |
| 68 | [hashtable]$AllStrings) |
| 69 | |
| 70 | $content = $AllStrings[$defaultLanguage][$Name] |
| 71 | $map = "static const std::vector<std::pair<TString, const TChar*>> strings {`r`n {TEXT(`"$defaultLanguage`"), TEXT(R`"($content)`")}" |
| 72 | |
| 73 | foreach ($language in $AllStrings.Keys) |
| 74 | { |
| 75 | if ($language -eq $defaultLanguage) |
| 76 | { |
| 77 | continue |
| 78 | } |
| 79 | |
| 80 | $strings = $AllStrings[$language] |
| 81 | |
| 82 | if (!$strings.ContainsKey($Name)) |
| 83 | { |
| 84 | Write-Host "Warning: string $Name not found in language: $language" |
| 85 | continue |
| 86 | } |
| 87 | |
| 88 | $map += ",`r`n {TEXT(`"$language`"), TEXT(R`"($($strings[$Name]))`")}" |
| 89 | } |
| 90 | |
| 91 | $map += "}" |
| 92 | |
| 93 | |
| 94 | $ArgumentsCount = getArgumentsFromInserts -Name $Name -Content $Content |
| 95 | if ($ArgumentsCount -eq 0) |
| 96 | { |
| 97 | return ' |
| 98 | /* Message: {1}*/ |
| 99 | static inline TString {0}(Options options = Options::Default) |
| 100 | {{ |
| 101 | {2}; |
| 102 | |
| 103 | return LookupString(strings, options); |
| 104 | }} |
| 105 | ' -f $Name, $Content.split("`n")[0], $map |
| 106 | } |
| 107 | else |
| 108 | { |
| 109 | $ArgumentTypes = [string]::Join(', ', ((1..$ArgumentsCount) |% {"typename T$_"})) |
| 110 | $ArgumentHeaders = [string]::Join(', ', ((1..$ArgumentsCount) |% {"const T$_& arg$_"})) |
| 111 | $Arguments = [string]::Join(', ', ((1..$ArgumentsCount) |% {"arg$_"})) |
| 112 | return ' |
| 113 | /* Message: {4}*/ |
| 114 | |
| 115 | template <{1}> |
| 116 | static TString {0}({2}, Options options = Options::Default) |
| 117 | {{ |
| 118 | {5}; |
| 119 | |
| 120 | auto message = LookupString(strings, options); |
| 121 | return std::vformat(message, ARGS({3})); |
| 122 | }} |
| 123 | ' -f $Name, $ArgumentTypes, $ArgumentHeaders, $Arguments, $Content.split("`n")[0], $map |
| 124 | |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | function loadStrings |
| 129 | { |
| 130 | param([string]$language) |
| 131 | $xml = [xml](Get-Content "$resourceFolder/$language/Resources.resw" -raw) |
| 132 | |
| 133 | $strings = @{} |
| 134 | |
| 135 | foreach($entry in $xml.root.data) |
| 136 | { |
| 137 | if ($strings.ContainsKey($entry.name)) |
| 138 | { |
| 139 | throw "Entry $($entry.name) duplicated in resource for: $language" |
| 140 | } |
| 141 | |
| 142 | # Skip generating strings intended for WSL Settings. These strings are |
| 143 | # unused in native C++ code. Further, their naming is incompatible with |
| 144 | # the function names generated for the localization header (contain '.'). |
| 145 | if ($entry.name.StartsWith("Settings_")) |
| 146 | { |
| 147 | continue |
| 148 | } |
| 149 | |
| 150 | $strings[$entry.name] = $entry.value |
| 151 | } |
| 152 | |
| 153 | return $strings |
| 154 | } |
| 155 | |
| 156 | $allStrings = @{} |
| 157 | |
| 158 | foreach ($language in $languages) |
| 159 | { |
| 160 | $allStrings[$language] = loadStrings -language $language |
| 161 | } |
| 162 | |
| 163 | foreach($entry in $allStrings[$defaultLanguage].Keys) |
| 164 | { |
| 165 | $content += generateEntry -Name $entry -allStrings $allStrings |
| 166 | } |
| 167 | |
| 168 | $content += @" |
| 169 | public: |
| 170 | #ifdef WIN32 |
| 171 | static bool IsCurrentLanguageEnglish(Options options = Options::Default); |
| 172 | #endif |
| 173 | private: |
| 174 | static const TChar* LookupString(const std::vector<std::pair<TString, const TChar*>>& strings, Options options); |
| 175 | }; |
| 176 | |
| 177 | } // namespace wsl::windows::common |
| 178 | |
| 179 | #undef ARGS |
| 180 | "@ |
| 181 | |
| 182 | |
| 183 | Set-Content -Path $OutFile -Encoding UTF8 $content |