-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Add support for generating Windows Store listing data. #12
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
steam/ | ||
/winstore/obj | ||
/winstore/bin | ||
.vs | ||
winstore-output* |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
##plural 0 | ||
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
using CsvHelper; | ||
using System.Globalization; | ||
using System.IO.Compression; | ||
using System.Text; | ||
|
||
namespace convert_to_winstore | ||
{ | ||
/// <summary> | ||
/// Class representing an OpenTTD language. | ||
/// </summary> | ||
internal class Language | ||
{ | ||
/// <summary> | ||
/// Language filename base (e.g. "english"); | ||
/// </summary> | ||
public string LangFile { get; } | ||
|
||
/// <summary> | ||
/// Windows Store language code (e.g. "en-gb"); | ||
/// </summary> | ||
public string StoreCode { get; } | ||
|
||
/// <summary> | ||
/// Loaded strings for substitution. | ||
/// </summary> | ||
private Dictionary<string, string> strings; | ||
|
||
/// <summary> | ||
/// Creates a new instance of the Language class. | ||
/// </summary> | ||
/// <param name="langFile">Language filename base.</param> | ||
/// <param name="storeCode">Windows Store language code.</param> | ||
public Language(string langFile, string storeCode) | ||
{ | ||
LangFile = langFile; | ||
StoreCode = storeCode; | ||
} | ||
|
||
/// <summary> | ||
/// Replaces $VARIABLES$ within the specified string with translated strings. | ||
/// </summary> | ||
/// <param name="langPath">Path to the 'lang' folder.</param> | ||
/// <param name="stringToSubstitute">String to work on.</param> | ||
/// <returns>The substituted string.</returns> | ||
public string Substitute(string langPath, string stringToSubstitute) | ||
{ | ||
if (strings == null) | ||
{ | ||
strings = new Dictionary<string, string>(StringComparer.Ordinal); | ||
string path = Path.Combine(langPath, Path.ChangeExtension(LangFile, ".txt")); | ||
|
||
if (!File.Exists(path)) | ||
{ | ||
// TODO: For now, substitute the English version. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The downside of So maybe the question .. is it actually a TODO? |
||
path = Path.Combine(langPath, "english.txt"); | ||
} | ||
|
||
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||
{ | ||
using (StreamReader sr = new StreamReader(fs, Encoding.UTF8)) | ||
{ | ||
while (!sr.EndOfStream) | ||
{ | ||
string? line = sr.ReadLine(); | ||
|
||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith('#')) | ||
{ | ||
continue; | ||
} | ||
|
||
int firstColon = line.IndexOf(':'); | ||
|
||
if (firstColon < 0) | ||
{ | ||
continue; | ||
} | ||
|
||
string key = $"${line.Substring(0, firstColon).Trim()}$"; | ||
string value = line.Substring(firstColon + 1).Trim(); | ||
|
||
strings[key] = value; | ||
} | ||
} | ||
} | ||
} | ||
|
||
foreach (KeyValuePair<string, string> kvp in strings) | ||
{ | ||
stringToSubstitute = stringToSubstitute.Replace(kvp.Key, kvp.Value, StringComparison.Ordinal); | ||
} | ||
|
||
return stringToSubstitute; | ||
} | ||
} | ||
|
||
internal class Program | ||
{ | ||
/// <summary> | ||
/// List of supported languages. | ||
/// </summary> | ||
private static readonly List<Language> languageMappings = | ||
[ | ||
new Language("arabic_egypt", "ar-eg"), | ||
new Language("bulgarian", "bg-bg"), | ||
new Language("simplified_chinese", "zh-cn"), | ||
new Language("traditional_chinese", "zh-tw"), | ||
new Language("bulgarian", "bg-bg"), | ||
new Language("czech", "cs-cz"), | ||
new Language("danish", "da-dk"), | ||
new Language("dutch", "nl-nl"), | ||
new Language("finnish", "fi-fi"), // Not currently selected for OpenTTD on the Windows Store | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If that comment is true, why is it in here? Or might the comment become untrue at some point? Or should we disable the line in total? :) |
||
new Language("french", "fr-fr"), | ||
new Language("german", "de-de"), | ||
new Language("greek", "el-gr"), | ||
new Language("hungarian", "hu-hu"), | ||
new Language("italian", "it-it"), | ||
new Language("japanese", "ja-jp"), | ||
new Language("korean", "ko-kr"), | ||
new Language("norwegian_bokmal", "nb-no"), | ||
new Language("polish", "pl-pl"), | ||
new Language("portuguese", "pt-pt"), | ||
new Language("brazilian_portuguese", "pt-br"), | ||
new Language("romanian", "ro-ro"), | ||
new Language("russian", "ru-ru"), | ||
new Language("spanish", "es-es"), | ||
new Language("spanish_MX", "es-mx"), | ||
new Language("swedish", "sv-se"), | ||
new Language("thai", "th-th"), | ||
new Language("turkish", "tr-tr"), | ||
new Language("ukrainian", "uk-ua"), | ||
new Language("vietnamese", "vi-vn"), | ||
|
||
// The following languages are supported by the Store, but not currently translated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this PR, that is no longer true, is it? So maybe already remove the comment, I would think. |
||
new Language("afrikaans", "af-za"), | ||
new Language("basque", "eu-es"), | ||
new Language("belarusian", "be-by"), | ||
new Language("catalan", "ca-es"), | ||
new Language("croatian", "hr-hr"), | ||
new Language("estonian", "et-ee"), | ||
new Language("gaelic", "gd-gb"), | ||
new Language("galician", "gl-es"), | ||
new Language("hebrew", "he-il"), | ||
new Language("icelandic", "is-is"), | ||
new Language("indonesian", "id-id"), | ||
new Language("irish", "ga-ie"), | ||
new Language("latvian", "lv-lv"), | ||
new Language("lithuanian", "lt-lt"), | ||
new Language("luxembourgish", "lb-lu"), | ||
new Language("malay", "ms-my"), | ||
new Language("norwegian_nynorsk", "no-no"), | ||
new Language("serbian", "sr-latn-rs"), | ||
new Language("slovak", "sk-sk"), | ||
new Language("slovenian", "sl-si"), | ||
new Language("tamil", "ta-in"), | ||
new Language("welsh", "cy-gb") | ||
]; | ||
|
||
/// <summary> | ||
/// English language variants (except "en-gb", which is included in the template). | ||
/// </summary> | ||
private static readonly string[] languageEnglishVariants = | ||
{ | ||
"en-au", | ||
"en-us" | ||
}; | ||
|
||
/// <summary> | ||
/// The English (UK) base translation. | ||
/// </summary> | ||
private static readonly Language englishGb = new Language("english", "en-gb"); | ||
|
||
static void Main(string[] args) | ||
{ | ||
// Either search the current directory for our data, or allow the path to be passed on the command line | ||
string rootPath = (args.Length > 0) ? args[0] : Directory.GetCurrentDirectory(); | ||
|
||
string templatePath = Path.Combine(rootPath, "winstore-template.csv"); | ||
string langPath = Path.Combine(rootPath, "lang"); | ||
string outputPath = Path.Combine(rootPath, "winstore-output.csv"); | ||
|
||
using (StreamWriter writer = new StreamWriter(outputPath, false, Encoding.UTF8)) | ||
{ | ||
using (CsvWriter csvOut = new CsvWriter(writer, new CultureInfo("en-US"))) | ||
{ | ||
using (StreamReader reader = new StreamReader(templatePath, true)) | ||
{ | ||
using (CsvReader csv = new CsvReader(reader, new CultureInfo("en-US"))) | ||
{ | ||
// Read the header row | ||
csv.Read(); | ||
|
||
if (!csv.ReadHeader() || csv.HeaderRecord == null) | ||
{ | ||
Console.Error.WriteLine("Unable to read header from input CSV."); | ||
return; | ||
} | ||
|
||
// Create output headers - one for each language | ||
string[] headers = new string[csv.HeaderRecord.Length + languageMappings.Count + languageEnglishVariants.Length]; | ||
|
||
csv.HeaderRecord.CopyTo(headers, 0); | ||
languageMappings.Select(l => l.StoreCode).ToArray().CopyTo(headers, csv.HeaderRecord.Length); | ||
languageEnglishVariants.CopyTo(headers, csv.HeaderRecord.Length + languageMappings.Count); | ||
|
||
foreach (string value in headers) | ||
{ | ||
csvOut.WriteField(value); | ||
} | ||
|
||
csvOut.NextRecord(); | ||
|
||
string[] values = (string[])headers.Clone(); | ||
|
||
while (csv.Read()) | ||
{ | ||
string fieldName = csv.GetField(0) ?? string.Empty; | ||
string fieldId = csv.GetField(1) ?? string.Empty; | ||
string fieldType = csv.GetField(2) ?? string.Empty; | ||
string fieldDefault = csv.GetField(3) ?? string.Empty; | ||
string enGbValue = csv.GetField(4) ?? string.Empty; | ||
|
||
values[0] = fieldName; | ||
values[1] = fieldId; | ||
values[2] = fieldType; | ||
values[3] = fieldDefault; | ||
|
||
for (int i = 4; i < headers.Length; i++) | ||
{ | ||
values[i] = enGbValue; | ||
} | ||
|
||
if (fieldType.Equals("Text", StringComparison.Ordinal) && enGbValue.Contains('$', StringComparison.Ordinal)) | ||
{ | ||
// Perform a language lookup and substitution | ||
values[4] = englishGb.Substitute(langPath, values[4]); | ||
|
||
int curCol = 5; | ||
|
||
foreach (Language l in languageMappings) | ||
{ | ||
values[curCol] = l.Substitute(langPath, enGbValue); | ||
|
||
// If the string still contains unsubstituted values, use the English version instead | ||
if (values[curCol].Contains('$')) | ||
values[curCol] = values[4]; | ||
|
||
curCol++; | ||
} | ||
|
||
// Manually set the English variants to the same as British English | ||
foreach (string variant in languageEnglishVariants) | ||
{ | ||
values[curCol] = values[4]; | ||
curCol++; | ||
} | ||
} | ||
|
||
foreach (string value in values) | ||
{ | ||
csvOut.WriteField(value); | ||
} | ||
|
||
csvOut.NextRecord(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"profiles": { | ||
"convert-to-winstore": { | ||
"commandName": "Project", | ||
"commandLineArgs": "../../../../" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<RootNamespace>convert_to_winstore</RootNamespace> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="CsvHelper" Version="33.0.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all these languages actually
plural 0
? Or is it just too much copy/paste?