Compare commits

...

8 Commits

Author SHA1 Message Date
5917e79635 Fix letters 'q' and 'z' not being in the dictionary
Enfore that all one-grams are present in the dictionary to make sure
it's possible to write any sentence
2025-05-17 00:25:51 +02:00
623888851b Remove Test project 2025-05-16 23:37:59 +02:00
b036cddb07 Add Web UI page title 2025-05-16 23:14:13 +02:00
1b0cfa58be Fix Web UI clear button 2025-05-16 23:10:17 +02:00
218389ae06 Add translator web ui 2025-05-16 22:53:32 +02:00
f39d936024 Add translator from poyo 2025-05-16 14:07:02 +02:00
f83f59c05a Add translator to poyo 2025-05-16 11:27:16 +02:00
871f46b996 Add translator source generator 2025-05-15 21:03:35 +02:00
30 changed files with 1289 additions and 156 deletions

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="60000 Most Frequent Words in American English - Corpus of Contemporary American English.xlsx" />
<EmbeddedResource Include="60000 Most Frequent Words in American English - Corpus of Contemporary American English.xlsx" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ClosedXML" Version="0.104.2" />
</ItemGroup>
</Project>

View File

@@ -1,105 +0,0 @@
// See https://aka.ms/new-console-template for more information
// Load up the file
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using ClosedXML.Excel;
Console.WriteLine("Reading Excel file...");
// Load Excel data file
const string ExcelFilePath = "PoyoLang.Analysis.NGrams.60000 Most Frequent Words in American English - Corpus of Contemporary American English.xlsx";
using var excelFileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ExcelFilePath);
using var workBook = new XLWorkbook(excelFileStream);
var worksheet = workBook.Worksheet("List");
Console.WriteLine("Reading word frequencies");
// Read word frequencies
var wordColumn = "C";
var frequencyColumn = "D";
var wordFrequencies = new List<(string word, long frequency)>();
var row = 2;
while (true)
{
var wordValue = worksheet.Cell(row, wordColumn).Value;
if (wordValue.IsBlank)
{
break;
}
var word = $"{wordValue.GetText().Trim().ToLowerInvariant()} ";
var frequency = (long)worksheet.Cell(row, frequencyColumn).GetDouble();
if (!word.Contains('('))
{
wordFrequencies.Add((word, frequency));
}
row++;
}
Console.WriteLine("Computing ngrams");
// Compute n-grams
var ngrams = new Dictionary<string, long>();
const int MaxLength = 5;
const int MinLength = 1;
foreach (var (word, frequency) in wordFrequencies)
{
var span = word.AsSpan();
while (span.Length >= MinLength)
{
for (int length = MinLength; length <= MaxLength; length++)
{
if (length > span.Length)
{
break;
}
Increment(span[..length]);
}
span = span[1..];
}
continue;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Increment(ReadOnlySpan<char> span)
{
var ngram = span.ToString();
if (ngrams.TryGetValue(ngram, out var count))
{
ngrams[ngram] = count + frequency;
}
else
{
ngrams[ngram] = frequency;
}
}
}
// Order frequencies
var orderedNgrams = ngrams
.OrderByDescending(n => n.Value)
.ToList();
Console.WriteLine($"Found {orderedNgrams.Count} n-grams");
var serializedNgrams = JsonSerializer.Serialize(orderedNgrams, new JsonSerializerOptions() { WriteIndented = true});
await File.WriteAllTextAsync("n-grams.json", serializedNgrams);

View File

@@ -56,7 +56,7 @@ Console.WriteLine("Computing ngrams");
var ngrams = new Dictionary<string, long>(); var ngrams = new Dictionary<string, long>();
const int MaxLength = 8; const int MaxLength = 8;
const int MinLength = 1; const int MinLength = 2;
foreach (var (word, frequency) in wordFrequencies) foreach (var (word, frequency) in wordFrequencies)
{ {
@@ -112,13 +112,29 @@ Console.WriteLine("Generating dictionary...");
var dictionary = new Dictionary<string, string>(); var dictionary = new Dictionary<string, string>();
var ngramIndex = 0; var ngramIndex = 0;
// Prepend base letters to make sure all words are writeable
string[] oneGrams = [
" ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
];
var fullNgrams = oneGrams.Concat(orderedNgrams.Select(p => p.Key)).ToArray();
foreach (var letter in Alphabet.BaseAlphabet) foreach (var letter in Alphabet.BaseAlphabet)
{ {
dictionary[letter] = orderedNgrams[ngramIndex].Key; dictionary[letter] = fullNgrams[ngramIndex];
ngramIndex++; ngramIndex++;
} }
await File.WriteAllTextAsync("dictionary.json", JsonSerializer.Serialize(dictionary, jsonOptions)); await File.WriteAllTextAsync("dictionary.json", JsonSerializer.Serialize(dictionary, jsonOptions));
Console.WriteLine($"Dictionary written to {Path.Combine(Environment.CurrentDirectory, "dictionary.json")}"); Console.WriteLine($"Dictionary written to {Path.Combine(Environment.CurrentDirectory, "dictionary.json")}");
// Also write in simple custom format for source generator
await using var customOutput = File.CreateText("dictionary.txt");
foreach (var pair in dictionary)
{
await customOutput.WriteLineAsync($"{pair.Key}={pair.Value}");
}
Console.WriteLine($"Custom dictionary written to {Path.Combine(Environment.CurrentDirectory, "dictionary.txt")}");

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PoyoLang.Dictionary\PoyoLang.Dictionary.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,9 +0,0 @@
// See https://aka.ms/new-console-template for more information
using PoyoLang.Dictionary;
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine(string.Join(Environment.NewLine, Alphabet.BaseAlphabet));
Console.WriteLine(Alphabet.BaseAlphabet.Length);

View File

@@ -0,0 +1,15 @@
namespace PoyoLang.Translator.SourceGenerator;
public class Node
{
public char Letter { get; }
public string Target { get; }
public List<Node> Nodes { get; } = [];
public Node(char letter, string target)
{
Letter = letter;
Target = target;
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0"/>
<PackageReference Include="PolySharp" Version="1.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,377 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace PoyoLang.Translator.SourceGenerator;
[Generator]
public class PoyoLangTranslatorGenerator : IIncrementalGenerator
{
private const char IndentChar = '\t';
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var texts = context.AdditionalTextsProvider;
// There will be only one of those but incremental generators work as pipelines
var dictionaries = texts
.Where(static text => text.Path.EndsWith("dictionary.txt"))
.Select(static (text, _) => text.GetText());
var parsedDictionaries = dictionaries
.Select(static (dictionary, _) =>
ReadCustomDictionary(dictionary!)
);
var formattedDictionaries = parsedDictionaries
.Select(static (dictionary, _) =>
{
// Return normal and reverse dictionary order to have ngrams first
return (
Normal: dictionary,
Reversed: dictionary!.OrderBy(p => p.Value).ToDictionary(p => p.Value, p => p.Key)
);
});
var prefixTrees = formattedDictionaries
.Select(static (dictionaries, _) => (
Dictionary: dictionaries.Normal,
PrefixTree: BuildPrefixTree(dictionaries.Reversed)
));
context.RegisterSourceOutput(prefixTrees, static (sourceProductionContext, data) =>
{
sourceProductionContext.AddSource("PoyoLangTranslator.g.cs", GenerateSource(data.Dictionary, data.PrefixTree));
});
}
private static Dictionary<string, string> ReadCustomDictionary(SourceText text)
{
var dictionary = new Dictionary<string, string>();
foreach (var line in text.ToString().Split('\n'))
{
var span = line.TrimEnd('\r').AsSpan();
// Reached end of file
if (span.Length < 1)
{
break;
}
var splitIndex = span.IndexOf('=');
dictionary[span[..splitIndex].ToString()] = span[(splitIndex + 1)..].ToString();
}
return dictionary;
}
private static List<Node> BuildPrefixTree(Dictionary<string, string> dictionary)
{
var rootNodes = new List<Node>();
var firstNodes = dictionary.Where(p => p.Key.Length is 1);
foreach (var firstNode in firstNodes)
{
var letter = firstNode.Key[0];
var target = firstNode.Value;
var node = new Node(letter, target);
rootNodes.Add(node);
// Add sub-nodes
ParseNodes(node, letter.ToString());
}
return rootNodes;
void ParseNodes(Node node, string prefix)
{
// Find nodes that have previous node as prefixed
var subNodes = dictionary
.Where(p => p.Key.StartsWith(prefix) && p.Key.Length == prefix.Length + 1);
foreach (var subNode in subNodes)
{
var letter = subNode.Key[prefix.Length];
var target = subNode.Value;
var newPrefix = $"{prefix}{letter}";
var newNode = new Node(letter, target);
node.Nodes.Add(newNode);
// Recursively add sub-nodes
ParseNodes(newNode, newPrefix);
}
}
}
private static string GenerateSource(Dictionary<string, string> dictionary, List<Node> rootNodes)
{
var source = new StringBuilder();
// Usings and namespace
source.Append(
"""
using System;
using System.Text;
namespace PoyoLang.Translator;
"""
);
// Partial class definition
source.Append(
"""
public partial class PoyoLangTranslator
{
"""
);
GenerateNextLetterMethod(rootNodes, source);
GenerateFromPoyoMethod(dictionary, source);
// Partial class end
source.Append(
"""
}
"""
);
return source.ToString();
}
private static void GenerateNextLetterMethod(List<Node> rootNodes, StringBuilder source)
{
// Next letter method definition
source.Append(
"""
private void NextLetter(ref ReadOnlySpan<char> text, StringBuilder output)
{
"""
);
// 0 length case and caps
source.Append(
"""
if (text.Length < 1)
{
return;
}
var isCaps = char.IsUpper(text[0]);
"""
);
GenerateSwitchCases(rootNodes, depth: 0);
// Next letter method end
source.Append(
"""
// Punctuation/Unknown characters case
output.Append(text[0]);
text = text[1..];
}
"""
);
return;
void GenerateSwitchCases(List<Node> nodes, int depth)
{
var indent = Indent(depth * 3);
// Switch-case start
source.Append(
$$"""
{{indent}}switch (text[{{depth}}])
{{indent}}{
"""
);
foreach (var node in nodes)
{
var targetLower = node.Target;
var targetUpper = ToTitleCase(targetLower);
// Case start
source.Append(
$$"""
{{indent}} case '{{node.Letter}}' or '{{char.ToUpper(node.Letter)}}':
"""
);
// Sub nodes handling
if (node.Nodes.Count > 0)
{
source.Append(
$$"""
{{indent}} if (text.Length > {{depth + 1}})
{{indent}} {
"""
);
// Sub nodes
GenerateSwitchCases(node.Nodes, depth + 1);
source.Append(
$$"""
{{indent}} }
"""
);
}
// Current node handling fallback
source.Append(
$$"""
{{indent}}
{{indent}} text = text[{{depth + 1}}..];
{{indent}}
{{indent}} output.Append(isCaps ? "{{targetUpper}}" : "{{targetLower}}");
{{indent}}
{{indent}} return;
"""
);
}
// Switch-case end
source.Append(
$$"""
{{indent}}}
"""
);
}
}
private static void GenerateFromPoyoMethod(Dictionary<string, string> dictionary, StringBuilder source)
{
// From Poyo method definition
source.Append(
"""
private void FromPoyo(ref ReadOnlySpan<char> text, StringBuilder output)
{
"""
);
// Initial cases
source.Append(
"""
if (text.Length < 1)
{
return;
}
// This happens if the end of the text is not a poyo letter (punctuation for ex)
if (text.Length < 4)
{
output.Append(text);
text = text[^0..];
return;
}
var letter = text[..4];
"""
);
GenerateReverseSwitchCases();
// From Poyo method end
source.Append(
"""
// Advance in text
text = text[4..];
}
"""
);
return;
void GenerateReverseSwitchCases()
{
// Switch start
source.Append(
"""
switch (letter)
{
"""
);
foreach (var pair in dictionary)
{
// Non-caps case
source.Append(
$$"""
case "{{pair.Key}}":
output.Append("{{pair.Value}}");
break;
"""
);
// Caps case
source.Append(
$$"""
case "{{ToTitleCase(pair.Key)}}":
output.Append("{{ToTitleCase(pair.Value)}}");
break;
"""
);
}
// Switch end
source.Append(
"""
default:
// Not a poyo letter, only read 1 character (could be punctuation for ex)
output.Append(text[0]);
text = text[1..];
return;
}
"""
);
}
}
private static string ToTitleCase(string text)
{
return $"{char.ToUpper(text[0])}{text[1..]}";
}
private static string Indent(int depth) => new(IndentChar, depth);
}

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"PoyoLang.Translator.SourceGenerator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../PoyoLang.Test/PoyoLang.Test.csproj"
}
}
}

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="/"/>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
<link href=@Assets["_content/MudBlazor/MudBlazor.min.css"] rel="stylesheet"/>
<ImportMap/>
<link rel="icon" type="image/ico" href="favicon.ico"/>
<HeadOutlet @rendermode="InteractiveServer"/>
</head>
<body>
<Routes @rendermode="InteractiveServer"/>
<script src="_framework/blazor.web.js"></script>
<script src=@Assets["_content/MudBlazor/MudBlazor.min.js"]></script>
</body>
</html>

View File

@@ -0,0 +1,101 @@
@inherits LayoutComponentBase
<MudThemeProvider Theme="@_theme" IsDarkMode="_isDarkMode"/>
<MudPopoverProvider/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<MudLayout>
<MudAppBar Elevation="1">
<MudText Typo="Typo.h5" Class="ml-3">PoyoLang</MudText>
<MudSpacer />
<MudIconButton Icon="@(DarkLightModeButtonIcon)" Color="Color.Inherit" OnClick="@DarkModeToggle" />
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit" Edge="Edge.End"
Href="https://git.ilysix.fr/Eveldee/PoyoLang" Target="_blank" />
</MudAppBar>
<MudMainContent Class="pt-16 pa-4">
<MudContainer MaxWidth="MaxWidth.Large">
@Body
</MudContainer>
</MudMainContent>
</MudLayout>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
@code {
private bool _drawerOpen = true;
private bool _isDarkMode = true;
private MudTheme? _theme = null;
protected override void OnInitialized()
{
base.OnInitialized();
_theme = new()
{
PaletteLight = _lightPalette,
PaletteDark = _darkPalette,
LayoutProperties = new LayoutProperties()
};
}
private void DrawerToggle()
{
_drawerOpen = !_drawerOpen;
}
private void DarkModeToggle()
{
_isDarkMode = !_isDarkMode;
}
private readonly PaletteLight _lightPalette = new()
{
Black = "#110e2d",
AppbarText = "#424242",
AppbarBackground = "rgba(255,255,255,0.8)",
DrawerBackground = "#ffffff",
GrayLight = "#e8e8e8",
GrayLighter = "#f9f9f9",
};
private readonly PaletteDark _darkPalette = new()
{
Primary = "#7e6fff",
Surface = "#1e1e2d",
Background = "#1a1a27",
BackgroundGray = "#151521",
AppbarText = "#92929f",
AppbarBackground = "rgba(26,26,39,0.8)",
DrawerBackground = "#1a1a27",
ActionDefault = "#74718e",
ActionDisabled = "#9999994d",
ActionDisabledBackground = "#605f6d4d",
TextPrimary = "#b2b0bf",
TextSecondary = "#92929f",
TextDisabled = "#ffffff33",
DrawerIcon = "#92929f",
DrawerText = "#92929f",
GrayLight = "#2a2833",
GrayLighter = "#1e1e2d",
Info = "#4a86ff",
Success = "#3dcb6c",
Warning = "#ffb545",
Error = "#ff3f5f",
LinesDefault = "#33323e",
TableLines = "#33323e",
Divider = "#292838",
OverlayLight = "#1e1e2d80",
};
public string DarkLightModeButtonIcon => _isDarkMode switch
{
true => Icons.Material.Rounded.AutoMode,
false => Icons.Material.Outlined.DarkMode,
};
}

View File

@@ -0,0 +1,19 @@
@page "/counter"
<PageTitle>Counter</PageTitle>
<MudText Typo="Typo.h3" GutterBottom="true">Counter</MudText>
<MudText Typo="Typo.body1" Class="mb-4">Current count: @currentCount</MudText>
<MudButton Color="Color.Primary" Variant="Variant.Filled" @onclick="IncrementCount">Click me</MudButton>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@@ -0,0 +1,38 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@@ -0,0 +1,146 @@
@page "/"
@using PoyoLang.Translator
@inject PoyoLangTranslator Translator
@inject IJSRuntime JsRuntime
@inject ISnackbar Snackbar
<PageTitle>PoyoLang Translator</PageTitle>
<MudText Typo="Typo.h3" GutterBottom="true" Class="mt-5">PoyoLang Translator</MudText>
<MudText Class="mb-5">Using this website you can freely translate from and to the Poyo language.
This language only use variations of the word "poyo" (320 of them!) to write sentences using the latin alphabet.
If you want more information about how this language works and how it is possible to translate to and from it,
check out the <MudLink href="https://git.ilysix.fr/Eveldee/PoyoLang" Target="_blank">source code</MudLink> of this whole project.</MudText>
<MudToggleGroup T="string" @bind-Value="_sourceLanguage" @bind-Value:after="SourceLanguageUpdated"
SelectionMode="SelectionMode.SingleSelection" Color="Color.Primary" CheckMark="true" FixedContent="true"
Class="mb-1" Style="max-width: 30em;">
<MudToggleItem Value="EnglishSourceLanguage" Text="English" />
<MudToggleItem Value="PoyoSourceLanguage" Text="Poyo" />
</MudToggleGroup>
@* Wide screen display *@
<MudHidden Breakpoint="Breakpoint.MdAndUp" Invert="true">
<MudStack Row="true" StretchItems="StretchItems.StartAndEnd" Class=".d-none .d-md-flex">
<MudStack>
<MudTextField @bind-Value="_sourceText" @bind-Value:after="UpdateTranslatedText" DebounceInterval="500"
Variant="Variant.Outlined" Label="Source text" Lines="15" />
</MudStack>
<MudStack Spacing="1" Row="false">
<MudTooltip Text="Swap languages">
<MudIconButton Icon="@Icons.Material.Filled.SwapHoriz" Color="Color.Primary" OnClick="SwapArrowClicked"/>
</MudTooltip>
<MudTooltip Text="Copy translation result">
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Color="Color.Primary" OnClick="CopyResultClicked"/>
</MudTooltip>
<MudTooltip Text="Clear source text">
<MudIconButton Icon="@Icons.Material.Filled.Clear" Color="Color.Error" OnClick="ClearButtonClicked"/>
</MudTooltip>
</MudStack>
<MudStack>
<MudTextField @bind-Value="_translatedText" ReadOnly="true"
Variant="Variant.Outlined" Label="Translation result" Lines="15" />
</MudStack>
</MudStack>
</MudHidden>
@* Mobile display *@
<MudHidden Breakpoint="Breakpoint.MdAndUp">
<MudStack Row="false" StretchItems="StretchItems.StartAndEnd">
<MudStack>
<MudTextField @bind-Value="_sourceText" @bind-Value:after="UpdateTranslatedText" DebounceInterval="500"
Variant="Variant.Outlined" Label="Source text" Lines="15" />
</MudStack>
<MudStack Spacing="1" Row="true">
<MudTooltip Text="Swap languages">
<MudIconButton Icon="@Icons.Material.Filled.SwapHoriz" Color="Color.Primary" OnClick="SwapArrowClicked"/>
</MudTooltip>
<MudTooltip Text="Copy translation result">
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Color="Color.Primary" OnClick="CopyResultClicked"/>
</MudTooltip>
<MudTooltip Text="Clear source text">
<MudIconButton Icon="@Icons.Material.Filled.Clear" Color="Color.Error" OnClick="ClearButtonClicked"/>
</MudTooltip>
</MudStack>
<MudStack>
<MudTextField @bind-Value="_translatedText" ReadOnly="true"
Variant="Variant.Outlined" Label="Translation result" Lines="15" />
</MudStack>
</MudStack>
</MudHidden>
@code {
private const string EnglishSourceLanguage = "English";
private const string PoyoSourceLanguage = "PoyoLang";
private string _sourceText = "";
private string _translatedText = "";
private string _sourceLanguage = EnglishSourceLanguage;
protected override void OnInitialized()
{
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.BottomCenter;
}
private void SourceLanguageUpdated()
{
UpdateTranslatedText();
}
private void SwapArrowClicked()
{
_sourceLanguage = _sourceLanguage switch
{
PoyoSourceLanguage => EnglishSourceLanguage,
_ => PoyoSourceLanguage
};
SwapLanguages();
}
private void SwapLanguages()
{
_sourceText = _translatedText;
UpdateTranslatedText();
}
private void UpdateTranslatedText()
{
string result;
if (_sourceLanguage is PoyoSourceLanguage)
{
result = Translator.TranslateFromPoyo(_sourceText);
}
else
{
result = Translator.TranslateToPoyo(_sourceText);
}
_translatedText = result;
}
private async Task CopyResultClicked()
{
await JsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", _translatedText);
Snackbar.Add("Translation result copied to clipboard!", Severity.Info);
}
private void ClearButtonClicked()
{
_sourceText = string.Empty;
_translatedText = string.Empty;
}
}

View File

@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
</Found>
</Router>

View File

@@ -0,0 +1,12 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MudBlazor
@using MudBlazor.Services
@using PoyoLang.Translator.Web
@using PoyoLang.Translator.Web.Components

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>PoyoLang.Translator.Web</RootNamespace>
<Version>0.1.2</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MudBlazor" Version="8.*"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PoyoLang.Translator\PoyoLang.Translator.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,34 @@
using MudBlazor.Services;
using PoyoLang.Translator;
using PoyoLang.Translator.Web.Components;
var builder = WebApplication.CreateBuilder(args);
// Add MudBlazor services
builder.Services.AddMudServices();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddSingleton<PoyoLangTranslator>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5083",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7137;http://localhost:5083",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PoyoLang.Translator.SourceGenerator\PoyoLang.Translator.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="dictionary.txt" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,39 @@
using System.Text;
namespace PoyoLang.Translator;
public partial class PoyoLangTranslator
{
public string TranslateToPoyo(ReadOnlySpan<char> text)
{
var output = new StringBuilder(text.Length);
while (text.Length > 0)
{
NextLetter(ref text, output);
// Add space if not reached the end
if (text.Length > 0)
{
output.Append(' ');
}
}
return output.ToString();
}
public string TranslateFromPoyo(ReadOnlySpan<char> text)
{
var output = new StringBuilder(text.Length);
while (text.Length > 0)
{
// Skip spaces (those are not used in this language)
text = text.TrimStart(' ');
FromPoyo(ref text, output);
}
return output.ToString();
}
}

View File

@@ -0,0 +1,320 @@
poyo=
poyó=a
poyò=b
poyô=c
poyö=d
poyõ=e
poyō=f
poyǒ=g
póyo=h
póyó=i
póyò=j
póyô=k
póyö=l
póyõ=m
póyō=n
póyǒ=o
pòyo=p
pòyó=q
pòyò=r
pòyô=s
pòyö=t
pòyõ=u
pòyō=v
pòyǒ=w
pôyo=x
pôyó=y
pôyò=z
pôyô=e
pôyö=th
pôyõ=t
pôyō=he
pôyǒ=the
pöyo=n
pöyó=he
pöyò=in
pöyô=y
pöyö=d
pöyõ=r
pöyō=an
pöyǒ=er
põyo=the
põyó=be
põyò=at
põyô=re
põyö=on
põyõ=o
põyō=nd
põyǒ=or
pōyo=be
pōyó=ha
pōyò=en
pōyô=to
pōyö=ve
pōyõ=ou
pōyō=nd
pōyǒ=it
pǒyo=st
pǒyó=l
pǒyò=te
pǒyô=al
pǒyö=ti
pǒyõ=f
pǒyō=and
pǒyǒ=s
payo=er
payó=nt
payò=and
payô=of
payö=ar
payõ=a
payō=se
payǒ=to
páyo=ea
páyó=hi
páyò=of
páyô=me
páyö=le
páyõ=on
páyō=h
páyǒ=co
pàyo=is
pàyó=in
pàyò=at
pàyô=ro
pàyö=ll
pàyõ=ve
pàyō=de
pàyǒ=es
pâyo=ng
pâyó=io
pâyò=om
pâyô=ne
pâyö=ic
pâyõ=li
pâyō=ri
pâyǒ=ra
päyo=as
päyó=ce
päyò=g
päyô=ho
päyö=ion
päyõ=ca
päyō=or
päyǒ=ta
pãyo=ut
pãyó=el
pãyò=ch
pãyô=m
pãyö=hat
pãyõ=ma
pãyō=hat
pãyǒ=ur
pāyo=k
pāyó=ng
pāyò=fo
pāyô=re
pāyö=no
pāyõ=si
pāyō=her
pāyǒ=av
pǎyo=nt
pǎyó=tha
pǎyò=ion
pǎyô=il
pǎyö=ent
pǎyõ=et
pǎyō=la
pǎyǒ=us
piyo=ac
piyó=ly
piyò=ing
piyô=wh
piyö=ow
piyõ=ave
piyō=pe
piyǒ=ec
píyo=ly
píyó=ot
píyò=tio
píyô=ll
píyö=tion
píyõ=wi
píyō=ave
píyǒ=se
pìyo=al
pìyó=ing
pìyò=ge
pìyô=it
pìyö=so
pìyõ=that
pìyō=that
pìyǒ=for
pîyo=ay
pîyó=st
pîyò=lo
pîyô=pr
pîyö=ee
pîyõ=hav
pîyō=have
pîyǒ=have
pïyo=tr
pïyó=sh
pïyò=le
pïyô=w
pïyö=mo
pïyõ=an
pïyō=tion
pïyǒ=ut
pĩyo=un
pĩyó=ce
pĩyò=ct
pĩyô=ay
pĩyö=me
pĩyõ=di
pĩyō=ss
pĩyǒ=ed
pīyo=i
pīyó=we
pīyò=ol
pīyô=yo
pīyö=ul
pīyõ=rt
pīyō=te
pīyǒ=em
pǐyo=th
pǐyó=ter
pǐyò=do
pǐyô=ke
pǐyö=po
pǐyõ=ir
pǐyō=thi
pǐyǒ=nc
puyo=you
puyó=his
puyò=im
puyô=is
puyö=oo
puyõ=all
puyō=ent
puyǒ=ig
púyo=pa
púyó=ate
púyò=p
púyô=ati
púyö=ld
púyõ=fi
púyō=his
púyǒ=en
pùyo=ver
pùyó=na
pùyò=mi
pùyô=ry
pùyö=ai
pùyõ=pl
pùyō=ow
pùyǒ=gh
pûyo=wo
pûyó=sa
pûyò=ad
pûyô=her
pûyö=ld
pûyõ=ev
pûyō=su
pûyǒ=os
püyo=iv
püyó=for
püyò=ther
püyô=wa
püyö=ni
püyõ=ry
püyō=ith
püyǒ=am
pũyo=bo
pũyó=u
pũyò=ch
pũyô=ab
pũyö=ou
pũyõ=you
pũyō=op
pũyǒ=id
pūyo=wit
pūyó=ne
pūyò=bu
pūyô=with
pūyö=fe
pūyõ=tu
pūyō=bl
pūyǒ=ere
pǔyo=atio
pǔyó=ed
pǔyò=ation
pǔyô=ome
pǔyö=out
pǔyõ=con
pǔyō=ke
pǔyǒ=ns
peyo=rea
peyó=eve
peyò=ci
peyô=ie
peyö=com
peyõ=ar
peyō=et
peyǒ=ith
péyo=vi
péyó=ty
péyò=with
péyô=ear
péyö=fr
péyõ=if
péyō=ag
péyǒ=res
pèyo=ate
pèyó=do
pèyò=mp
pèyô=ey
pèyö=ive
pèyõ=ia
pèyō=pro
pèyǒ=ba
pêyo=ov
pêyó=nce
pêyò=as
pêyô=ck
pêyö=sta
pêyõ=sp
pêyō=ty
pêyǒ=gr
pëyo=ter
pëyó=ation
pëyò=hin
pëyô=ess
pëyö=ak
pëyõ=ge
pëyō=ill
pëyǒ=go
pẽyo=out
pẽyó=our
pẽyò=ot
pẽyô=ey
pẽyö=fa
pẽyõ=ss
pẽyō=igh
pẽyǒ=not
pēyo=int
pēyó=ex
pēyò=om
pēyô=one
pēyö=ap
pēyõ=men
pēyō=all
pēyǒ=od
pěyo=here
pěyó=est
pěyò=up
pěyô=ive
pěyö=rs
pěyõ=ere
pěyō=ove
pěyǒ=nce

View File

@@ -2,10 +2,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Dictionary", "PoyoLang.Dictionary\PoyoLang.Dictionary.csproj", "{2D875AAD-BE17-4D15-A876-19DF1DCC57F5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Dictionary", "PoyoLang.Dictionary\PoyoLang.Dictionary.csproj", "{2D875AAD-BE17-4D15-A876-19DF1DCC57F5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Test", "PoyoLang.Test\PoyoLang.Test.csproj", "{4CB193B2-44F2-4926-A56E-9A0CDCBC828C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Dictionary.Generation", "PoyoLang.Dictionary.Generation\PoyoLang.Dictionary.Generation.csproj", "{43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Dictionary.Generation", "PoyoLang.Dictionary.Generation\PoyoLang.Dictionary.Generation.csproj", "{43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Translator.SourceGenerator", "PoyoLang.Translator.SourceGenerator\PoyoLang.Translator.SourceGenerator.csproj", "{0411CE3E-B80E-4AC3-839F-307AD0A16774}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Translator", "PoyoLang.Translator\PoyoLang.Translator.csproj", "{079808D0-16FB-4D01-A502-5366018312CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoyoLang.Translator.Web", "PoyoLang.Translator.Web\PoyoLang.Translator.Web.csproj", "{4620AA2D-D39E-4393-980C-E4DD25E624C3}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -16,13 +20,21 @@ Global
{2D875AAD-BE17-4D15-A876-19DF1DCC57F5}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D875AAD-BE17-4D15-A876-19DF1DCC57F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D875AAD-BE17-4D15-A876-19DF1DCC57F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D875AAD-BE17-4D15-A876-19DF1DCC57F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D875AAD-BE17-4D15-A876-19DF1DCC57F5}.Release|Any CPU.Build.0 = Release|Any CPU {2D875AAD-BE17-4D15-A876-19DF1DCC57F5}.Release|Any CPU.Build.0 = Release|Any CPU
{4CB193B2-44F2-4926-A56E-9A0CDCBC828C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CB193B2-44F2-4926-A56E-9A0CDCBC828C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CB193B2-44F2-4926-A56E-9A0CDCBC828C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CB193B2-44F2-4926-A56E-9A0CDCBC828C}.Release|Any CPU.Build.0 = Release|Any CPU
{43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Debug|Any CPU.Build.0 = Debug|Any CPU {43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Release|Any CPU.Build.0 = Release|Any CPU {43FFCEF2-A4AA-49A1-9731-CB6DAD9863F2}.Release|Any CPU.Build.0 = Release|Any CPU
{0411CE3E-B80E-4AC3-839F-307AD0A16774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0411CE3E-B80E-4AC3-839F-307AD0A16774}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0411CE3E-B80E-4AC3-839F-307AD0A16774}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0411CE3E-B80E-4AC3-839F-307AD0A16774}.Release|Any CPU.Build.0 = Release|Any CPU
{079808D0-16FB-4D01-A502-5366018312CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{079808D0-16FB-4D01-A502-5366018312CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{079808D0-16FB-4D01-A502-5366018312CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{079808D0-16FB-4D01-A502-5366018312CB}.Release|Any CPU.Build.0 = Release|Any CPU
{4620AA2D-D39E-4393-980C-E4DD25E624C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4620AA2D-D39E-4393-980C-E4DD25E624C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4620AA2D-D39E-4393-980C-E4DD25E624C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4620AA2D-D39E-4393-980C-E4DD25E624C3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

12
docker-publish.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
version=$(grep -oPm1 "(?<=<Version>)[^<]+" PoyoLang.Translator.Web/PoyoLang.Translator.Web.csproj)
dotnet.exe publish PoyoLang.Translator.Web \
-c Release \
-r linux-x64 \
-p:PublishProfile=DefaultContainer \
-p:InvariantGlobalization=true \
-p:ContainerFamily=alpine \
-p:ContainerRegistry=git.ilysix.fr \
-p:ContainerRepository=Eveldee/PoyoLang \
-p:ContainerImageTags="\"latest;$version\""