SHA256
Link-Fixes: Sprachumschalter, Canonical, og:locale, Legal-Platzhalter, serve-Fallback
- Builder.cs: Sprachumschalter-Links verwenden /{lang}/{slug}.html statt
/{lang}/{slug}/ (Dateien liegen flach als lang/slug.html)
- Builder.cs: Canonical/OG-URL enthält jetzt /{lang}/-Präfix
- Builder.cs: Legal-Seiten ersetzen {{intro}}, {{positioning}}, {{publisher}}
mit leerem String (waren zuvor als Artefakte im HTML sichtbar)
- Builder.cs: Sitemap-URLs enthalten Sprach-Präfix + .html
- BuildConfig.cs: JsonNamingPolicy.SnakeCaseLower für korrekte Deserialisierung
von snake_case JSON-Properties (og_locale → OgLocale, base_url → BaseUrl)
- Program.cs: webstatic serve hat .html-Fallback ( /de/slug → de/slug.html)
This commit is contained in:
+5
-1
@@ -99,7 +99,11 @@ public class BuildConfig
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(configPath);
|
var json = File.ReadAllText(configPath);
|
||||||
var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
var opts = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||||
|
};
|
||||||
var fromFile = JsonSerializer.Deserialize<BuildConfig>(json, opts);
|
var fromFile = JsonSerializer.Deserialize<BuildConfig>(json, opts);
|
||||||
if (fromFile != null)
|
if (fromFile != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+20
-9
@@ -105,8 +105,10 @@ public static class Builder
|
|||||||
? bannerHtml + htmlContent
|
? bannerHtml + htmlContent
|
||||||
: htmlContent;
|
: htmlContent;
|
||||||
|
|
||||||
var canonical = page.Href.StartsWith("/") ? page.Href : $"/{page.Href}";
|
var slug = page.Href.Trim('/');
|
||||||
if (!canonical.EndsWith("/")) canonical += "/";
|
var canonical = string.IsNullOrEmpty(slug)
|
||||||
|
? $"/{lang}/"
|
||||||
|
: $"/{lang}/{slug}.html";
|
||||||
|
|
||||||
var defaultTitle = cfg.Site.Title.TryGetValue(lang, out var dt) ? dt : cfg.Site.Name;
|
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 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);
|
var ldir = Path.Combine(Directories.DistDir, lang);
|
||||||
Directory.CreateDirectory(ldir);
|
Directory.CreateDirectory(ldir);
|
||||||
|
|
||||||
var slug = page.Href.Trim('/');
|
var fileSlug = page.Href.Trim('/');
|
||||||
var outputFile = string.IsNullOrEmpty(slug) ? "index.html" : $"{slug}.html";
|
var outputFile = string.IsNullOrEmpty(fileSlug) ? "index.html" : $"{fileSlug}.html";
|
||||||
var outputPath = Path.Combine(ldir, outputFile);
|
var outputPath = Path.Combine(ldir, outputFile);
|
||||||
File.WriteAllText(outputPath, pageHtml);
|
File.WriteAllText(outputPath, pageHtml);
|
||||||
|
|
||||||
@@ -248,7 +250,7 @@ public static class Builder
|
|||||||
var code = (li.HrefLang ?? lang).ToUpperInvariant();
|
var code = (li.HrefLang ?? lang).ToUpperInvariant();
|
||||||
string slug = "";
|
string slug = "";
|
||||||
if (langSlug != null && langSlug.TryGetValue(lang, out var h)) slug = h.Trim('/');
|
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($" <li><a href=\"{href}\" hreflang=\"{li.HrefLang ?? lang}\"><span>{HtmlEncode(label)}</span> <span class=\"lang-dropdown__code\">{code}</span></a></li>");
|
sb.AppendLine($" <li><a href=\"{href}\" hreflang=\"{li.HrefLang ?? lang}\"><span>{HtmlEncode(label)}</span> <span class=\"lang-dropdown__code\">{code}</span></a></li>");
|
||||||
}
|
}
|
||||||
return sb.ToString().Trim();
|
return sb.ToString().Trim();
|
||||||
@@ -403,14 +405,15 @@ public static class Builder
|
|||||||
htmlContent = MarkdownProcessor.TablePostprocess(htmlContent);
|
htmlContent = MarkdownProcessor.TablePostprocess(htmlContent);
|
||||||
|
|
||||||
var baseName = Regex.Replace(bn, @"\.(de|en|vi)\.md$", ".html").Replace(".md", ".html");
|
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 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 defaultDesc = cfg.Site.Description.TryGetValue(lang, out var dd) ? dd : "";
|
||||||
var seoHead = SeoHelper.RenderSeoHead(lang, title, "", $"{env.BaseUrl}{canonical}",
|
var seoHead = SeoHelper.RenderSeoHead(lang, title, "", $"{env.BaseUrl}{canonical}",
|
||||||
env.Cdn, defaultTitle, defaultDesc);
|
env.Cdn, defaultTitle, defaultDesc);
|
||||||
var sidebarTocHtml = BuildSidebarTocHtml(content);
|
var sidebarTocHtml = BuildSidebarTocHtml(content);
|
||||||
var legalSlug = $"/{Path.GetFileNameWithoutExtension(baseName)}/";
|
var legalSlug = $"{legalFileName}.html";
|
||||||
var legalLangSlug = cfg.LanguageCodes().ToDictionary(l => l, l => legalSlug);
|
var legalLangSlug = cfg.LanguageCodes().ToDictionary(l => l, l => $"{l}/{legalFileName}.html");
|
||||||
var langLinksHtml = BuildLangLinksHtml(legalLangSlug);
|
var langLinksHtml = BuildLangLinksHtml(legalLangSlug);
|
||||||
|
|
||||||
var pageHtml = layout
|
var pageHtml = layout
|
||||||
@@ -425,6 +428,9 @@ public static class Builder
|
|||||||
.Replace("{{lang_links}}", langLinksHtml).Replace("{{ lang_links }}", langLinksHtml)
|
.Replace("{{lang_links}}", langLinksHtml).Replace("{{ lang_links }}", langLinksHtml)
|
||||||
.Replace("{{glossary_href}}", GlossaryHref(lang)).Replace("{{ glossary_href }}", GlossaryHref(lang))
|
.Replace("{{glossary_href}}", GlossaryHref(lang)).Replace("{{ glossary_href }}", GlossaryHref(lang))
|
||||||
.Replace("{{glossary_label}}", GlossaryLabel(lang)).Replace("{{ glossary_label }}", GlossaryLabel(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("{{content}}", htmlContent).Replace("{{ content }}", htmlContent)
|
||||||
.Replace("{{body}}", htmlContent).Replace("{{ body }}", htmlContent);
|
.Replace("{{body}}", htmlContent).Replace("{{ body }}", htmlContent);
|
||||||
pageHtml = I18n.Substitute(pageHtml, lang);
|
pageHtml = I18n.Substitute(pageHtml, lang);
|
||||||
@@ -452,7 +458,12 @@ public static class Builder
|
|||||||
urls.Add($"{baseUrl}/{lang}/");
|
urls.Add($"{baseUrl}/{lang}/");
|
||||||
var contents = ContentLoader.LoadContent(Directories.ContentDir, lang);
|
var contents = ContentLoader.LoadContent(Directories.ContentDir, lang);
|
||||||
foreach (var page in contents)
|
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 = @"<?xml version=""1.0"" encoding=""UTF-8""?>
|
var sitemap = @"<?xml version=""1.0"" encoding=""UTF-8""?>
|
||||||
<urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"">
|
<urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"">
|
||||||
|
|||||||
+9
-2
@@ -339,8 +339,15 @@ static async Task ServeAsync(string distDir, int port)
|
|||||||
var file = Path.Combine(distDir, reqPath.TrimStart('/'));
|
var file = Path.Combine(distDir, reqPath.TrimStart('/'));
|
||||||
if (!File.Exists(file))
|
if (!File.Exists(file))
|
||||||
{
|
{
|
||||||
file = Path.Combine(distDir, reqPath.TrimStart('/') + "/index.html");
|
// Try appending .html (e.g. /de/slug → de/slug.html)
|
||||||
if (!File.Exists(file)) file = Path.Combine(distDir, "404.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))
|
if (File.Exists(file))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user