diff --git a/src/BuildConfig.cs b/src/BuildConfig.cs index 033aa75..f10ce4d 100644 --- a/src/BuildConfig.cs +++ b/src/BuildConfig.cs @@ -99,7 +99,11 @@ public class BuildConfig try { var json = File.ReadAllText(configPath); - var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + var opts = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + }; var fromFile = JsonSerializer.Deserialize(json, opts); if (fromFile != null) { diff --git a/src/Builder.cs b/src/Builder.cs index 24a68e2..1e1b1ac 100644 --- a/src/Builder.cs +++ b/src/Builder.cs @@ -105,8 +105,10 @@ public static class Builder ? bannerHtml + htmlContent : htmlContent; - var canonical = page.Href.StartsWith("/") ? page.Href : $"/{page.Href}"; - if (!canonical.EndsWith("/")) canonical += "/"; + var slug = page.Href.Trim('/'); + var canonical = string.IsNullOrEmpty(slug) + ? $"/{lang}/" + : $"/{lang}/{slug}.html"; var defaultTitle = cfg.Site.Title.TryGetValue(lang, out var dt) ? dt : cfg.Site.Name; var defaultDesc = cfg.Site.Description.TryGetValue(lang, out var dd) ? dd : ""; @@ -157,8 +159,8 @@ public static class Builder var ldir = Path.Combine(Directories.DistDir, lang); Directory.CreateDirectory(ldir); - var slug = page.Href.Trim('/'); - var outputFile = string.IsNullOrEmpty(slug) ? "index.html" : $"{slug}.html"; + var fileSlug = page.Href.Trim('/'); + var outputFile = string.IsNullOrEmpty(fileSlug) ? "index.html" : $"{fileSlug}.html"; var outputPath = Path.Combine(ldir, outputFile); File.WriteAllText(outputPath, pageHtml); @@ -248,7 +250,7 @@ public static class Builder var code = (li.HrefLang ?? lang).ToUpperInvariant(); string slug = ""; if (langSlug != null && langSlug.TryGetValue(lang, out var h)) slug = h.Trim('/'); - var href = string.IsNullOrEmpty(slug) ? $"/{lang}/" : $"/{lang}/{slug}/"; + var href = string.IsNullOrEmpty(slug) ? $"/{lang}/" : $"/{lang}/{slug}.html"; sb.AppendLine($"
  • {HtmlEncode(label)} {code}
  • "); } return sb.ToString().Trim(); @@ -403,14 +405,15 @@ public static class Builder htmlContent = MarkdownProcessor.TablePostprocess(htmlContent); var baseName = Regex.Replace(bn, @"\.(de|en|vi)\.md$", ".html").Replace(".md", ".html"); - var canonical = $"/{Path.GetFileNameWithoutExtension(baseName)}/"; + var legalFileName = Path.GetFileNameWithoutExtension(baseName); + var canonical = $"/{lang}/{legalFileName}.html"; var defaultTitle = cfg.Site.Title.TryGetValue(lang, out var dt) ? dt : cfg.Site.Name; var defaultDesc = cfg.Site.Description.TryGetValue(lang, out var dd) ? dd : ""; var seoHead = SeoHelper.RenderSeoHead(lang, title, "", $"{env.BaseUrl}{canonical}", env.Cdn, defaultTitle, defaultDesc); var sidebarTocHtml = BuildSidebarTocHtml(content); - var legalSlug = $"/{Path.GetFileNameWithoutExtension(baseName)}/"; - var legalLangSlug = cfg.LanguageCodes().ToDictionary(l => l, l => legalSlug); + var legalSlug = $"{legalFileName}.html"; + var legalLangSlug = cfg.LanguageCodes().ToDictionary(l => l, l => $"{l}/{legalFileName}.html"); var langLinksHtml = BuildLangLinksHtml(legalLangSlug); var pageHtml = layout @@ -425,6 +428,9 @@ public static class Builder .Replace("{{lang_links}}", langLinksHtml).Replace("{{ lang_links }}", langLinksHtml) .Replace("{{glossary_href}}", GlossaryHref(lang)).Replace("{{ glossary_href }}", GlossaryHref(lang)) .Replace("{{glossary_label}}", GlossaryLabel(lang)).Replace("{{ glossary_label }}", GlossaryLabel(lang)) + .Replace("{{intro}}", "").Replace("{{ intro }}", "") + .Replace("{{positioning}}", "").Replace("{{ positioning }}", "") + .Replace("{{publisher}}", "").Replace("{{ publisher }}", "") .Replace("{{content}}", htmlContent).Replace("{{ content }}", htmlContent) .Replace("{{body}}", htmlContent).Replace("{{ body }}", htmlContent); pageHtml = I18n.Substitute(pageHtml, lang); @@ -452,7 +458,12 @@ public static class Builder urls.Add($"{baseUrl}/{lang}/"); var contents = ContentLoader.LoadContent(Directories.ContentDir, lang); foreach (var page in contents) - if (page.Meta.Render) urls.Add($"{baseUrl}{page.Href}"); + { + if (!page.Meta.Render) continue; + var slug = page.Href.Trim('/'); + if (!string.IsNullOrEmpty(slug)) + urls.Add($"{baseUrl}/{lang}/{slug}.html"); + } } var sitemap = @" diff --git a/src/Program.cs b/src/Program.cs index f7565d6..6a1caa8 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -339,8 +339,15 @@ static async Task ServeAsync(string distDir, int port) var file = Path.Combine(distDir, reqPath.TrimStart('/')); if (!File.Exists(file)) { - file = Path.Combine(distDir, reqPath.TrimStart('/') + "/index.html"); - if (!File.Exists(file)) file = Path.Combine(distDir, "404.html"); + // Try appending .html (e.g. /de/slug → de/slug.html) + var htmlFile = file.TrimEnd('/') + ".html"; + if (File.Exists(htmlFile)) + file = htmlFile; + else + { + file = Path.Combine(distDir, reqPath.TrimStart('/') + "/index.html"); + if (!File.Exists(file)) file = Path.Combine(distDir, "404.html"); + } } if (File.Exists(file)) {