Skip to content
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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
steam/
/winstore/obj
/winstore/bin
.vs
winstore-output*
1 change: 1 addition & 0 deletions lang/afrikaans.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/basque.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/belarusian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 6
1 change: 1 addition & 0 deletions lang/catalan.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/croatian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/estonian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/gaelic.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 13
1 change: 1 addition & 0 deletions lang/galician.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/hebrew.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/icelandic.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/indonesian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/irish.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 4
1 change: 1 addition & 0 deletions lang/latvian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 3
1 change: 1 addition & 0 deletions lang/lithuanian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 5
1 change: 1 addition & 0 deletions lang/luxembourgish.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/malay.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/norwegian_nynorsk.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/serbian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 6
1 change: 1 addition & 0 deletions lang/slovak.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 10
1 change: 1 addition & 0 deletions lang/slovenian.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 8
1 change: 1 addition & 0 deletions lang/tamil.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
1 change: 1 addition & 0 deletions lang/welsh.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
##plural 0
TrueBrain marked this conversation as resolved.
Show resolved Hide resolved
430 changes: 430 additions & 0 deletions winstore-template.csv

Large diffs are not rendered by default.

269 changes: 269 additions & 0 deletions winstore/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downside of TODOs: who is going to pick it up when?

So maybe the question .. is it actually a TODO?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the hope is that once the translations are in EINTS, people translate them, and we no longer need this. :) So I suppose it depends on how long the translations take.

Copy link
Member

@TrueBrain TrueBrain Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it always be better to use the english sentence for those translations that do not have a translated string? That is more why I meant if this is actually a TODO. As I don't think you want to do it "for now", but in all cases where the translation is without an actual translation :)

Copy link
Member

@TrueBrain TrueBrain Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Owh, I was misreading the if-statement .. now you created all the language files, is this code that is actually executed? I think it would be better if the tool crashes (or exits with a warning) at this point, not? As that means you didn't create a placeholder file where one is expected?

In other words, eints is not going to create any new files when things are translated; it will only update the ones that currently exist.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, forgot about this over Christmas! In theory, yes, you're correct - we shouldn't have any more files appearing, unless we add more languages in the Windows Store. So that could well be removed, though maybe having it display a warning would be safest.

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("afrikaans", "af-za"),
new Language("arabic_egypt", "ar-eg"),
new Language("basque", "eu-es"),
new Language("belarusian", "be-by"),
new Language("brazilian_portuguese", "pt-br"),
new Language("bulgarian", "bg-bg"),
new Language("bulgarian", "bg-bg"),
new Language("catalan", "ca-es"),
new Language("croatian", "hr-hr"),
new Language("czech", "cs-cz"),
new Language("danish", "da-dk"),
new Language("dutch", "nl-nl"),
new Language("estonian", "et-ee"),
new Language("finnish", "fi-fi"),
new Language("french", "fr-fr"),
new Language("gaelic", "gd-gb"),
new Language("galician", "gl-es"),
new Language("german", "de-de"),
new Language("greek", "el-gr"),
new Language("hebrew", "he-il"),
new Language("hungarian", "hu-hu"),
new Language("icelandic", "is-is"),
new Language("indonesian", "id-id"),
new Language("irish", "ga-ie"),
new Language("italian", "it-it"),
new Language("japanese", "ja-jp"),
new Language("korean", "ko-kr"),
new Language("latvian", "lv-lv"),
new Language("lithuanian", "lt-lt"),
new Language("luxembourgish", "lb-lu"),
new Language("malay", "ms-my"),
new Language("norwegian_bokmal", "nb-no"),
new Language("norwegian_nynorsk", "no-no"),
new Language("polish", "pl-pl"),
new Language("portuguese", "pt-pt"),
new Language("romanian", "ro-ro"),
new Language("russian", "ru-ru"),
new Language("serbian", "sr-latn-rs"),
new Language("simplified_chinese", "zh-cn"),
new Language("slovak", "sk-sk"),
new Language("slovenian", "sl-si"),
new Language("spanish", "es-es"),
new Language("spanish_MX", "es-mx"),
new Language("swedish", "sv-se"),
new Language("tamil", "ta-in"),
new Language("thai", "th-th"),
new Language("traditional_chinese", "zh-tw"),
new Language("turkish", "tr-tr"),
new Language("ukrainian", "uk-ua"),
new Language("vietnamese", "vi-vn"),
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();
}
}
}
}
}
}
}
}
8 changes: 8 additions & 0 deletions winstore/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"convert-to-winstore": {
"commandName": "Project",
"commandLineArgs": "../../../../"
}
}
}
15 changes: 15 additions & 0 deletions winstore/convert-to-winstore.csproj
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>