fix: issue #35 validation expected unrendered .lang translation variant routes; main-language lists now render main-language translations

This commit is contained in:
2026-07-17 22:12:23 +02:00
parent dd53ca3fbc
commit 23f05b72f7
6 changed files with 180 additions and 129 deletions

View File

@@ -178,7 +178,7 @@ defmodule BDS.Generation do
build_core_outputs(
plan,
data.published_list_posts,
render_data.main_list_posts,
render_data.localized_list_posts_by_language,
on_output
) ++
@@ -274,7 +274,7 @@ defmodule BDS.Generation do
end
defp full_localized_archive_outputs(plan, render_data, builder) do
main_outputs = builder.(plan, render_data.data.post_index, [plan.language])
main_outputs = builder.(plan, render_data.main_post_index, [plan.language])
language_outputs =
Enum.flat_map(additional_languages(plan), fn language ->
@@ -286,11 +286,8 @@ defmodule BDS.Generation do
end
defp core_sitemap_outputs(plan, data) do
published_route_posts =
suppress_subtree_translation_variants(data.published_route_posts, additional_languages(plan))
{_sitemap_content, sitemap_to_write, _expected_paths, _timestamp_checks} =
build_validation_sitemap_artifacts(plan, data, published_route_posts, %{}, nil)
build_validation_sitemap_artifacts(plan, data, data.published_posts, %{}, nil)
[{"sitemap.xml", sitemap_to_write}]
end
@@ -321,17 +318,13 @@ defmodule BDS.Generation do
{:ok, generated_files_list} = list_generated_files(project_id)
generated_file_updated_at = generated_file_updated_at_map(generated_files_list)
additional_languages = additional_languages(plan)
published_route_posts =
suppress_subtree_translation_variants(data.published_route_posts, additional_languages)
{sitemap_content, sitemap_to_write, additional_expected_paths,
additional_post_timestamp_checks} =
build_validation_sitemap_artifacts(
plan,
data,
published_route_posts,
data.published_posts,
generated_file_updated_at,
on_progress
)
@@ -350,7 +343,7 @@ defmodule BDS.Generation do
post_timestamp_checks:
build_post_timestamp_checks(
data.project_data_dir,
published_route_posts,
data.published_posts,
generated_file_updated_at
) ++ additional_post_timestamp_checks,
additional_expected_paths: additional_expected_paths
@@ -647,53 +640,15 @@ defmodule BDS.Generation do
defp build_outputs(plan, on_output \\ nil) do
plan = with_render_context(plan)
data = generation_data(plan)
published_translations = flattened_generation_translations(data.translations_by_post)
translations_by_post_language = translation_lookup_map(published_translations)
translatable_published_posts =
Enum.reject(data.published_posts, &truthy_flag?(Map.get(&1, :do_not_translate)))
translatable_published_list_posts =
Enum.reject(data.published_list_posts, &truthy_flag?(Map.get(&1, :do_not_translate)))
localized_posts_by_language =
additional_languages(plan)
|> Enum.map(fn language ->
{language,
resolve_posts_for_language(
translatable_published_posts,
language,
translations_by_post_language,
plan.language
)}
end)
|> Map.new()
localized_list_posts_by_language =
additional_languages(plan)
|> Enum.map(fn language ->
{language,
resolve_posts_for_language(
translatable_published_list_posts,
language,
translations_by_post_language,
plan.language
)}
end)
|> Map.new()
localized_post_indexes =
localized_list_posts_by_language
|> Enum.map(fn {language, posts} -> {language, build_generation_post_index(posts)} end)
|> Map.new()
render_data = validation_render_data(plan)
data = render_data.data
core_outputs =
if :core in plan.sections do
build_core_outputs(
plan,
data.published_list_posts,
localized_list_posts_by_language,
render_data.main_list_posts,
render_data.localized_list_posts_by_language,
on_output
)
else
@@ -706,8 +661,8 @@ defmodule BDS.Generation do
plan_render_target(plan),
plan.language,
data.published_posts,
translations_by_post_language,
localized_posts_by_language,
render_data.translations_by_post_language,
render_data.localized_posts_by_language,
on_output
)
else
@@ -720,8 +675,8 @@ defmodule BDS.Generation do
plan_render_target(plan),
plan.language,
data.published_posts,
translations_by_post_language,
localized_posts_by_language,
render_data.translations_by_post_language,
render_data.localized_posts_by_language,
on_output
)
else
@@ -729,7 +684,12 @@ defmodule BDS.Generation do
end
archive_outputs =
build_archive_outputs(plan, data.post_index, localized_post_indexes, on_output)
build_archive_outputs(
plan,
render_data.main_post_index,
render_data.localized_post_indexes,
on_output
)
urls =
(core_outputs ++ page_outputs ++ single_outputs ++ archive_outputs)
@@ -770,14 +730,14 @@ defmodule BDS.Generation do
defp build_validation_sitemap_artifacts(
plan,
data,
published_route_posts,
published_posts,
generated_file_updated_at,
on_progress
) do
main_paths =
build_validation_route_paths(
plan,
published_route_posts,
published_posts,
data.published_list_posts,
data.post_index,
nil
@@ -1111,12 +1071,26 @@ defmodule BDS.Generation do
{language, build_generation_post_index(posts)}
end)
# Main-language lists must show the main-language translation of a
# foreign-language post when one exists, mirroring the canonical variant
# lookup single-page renders do. Resolved here (not in generation_data)
# because subtree resolution above needs the raw posts' ids intact.
main_list_posts =
resolve_posts_for_language(
data.published_list_posts,
plan.language,
translations_by_post_language,
plan.language
)
%{
data: data,
translations_by_post_language: translations_by_post_language,
localized_posts_by_language: localized_posts_by_language,
localized_list_posts_by_language: localized_list_posts_by_language,
localized_post_indexes: localized_post_indexes
localized_post_indexes: localized_post_indexes,
main_list_posts: main_list_posts,
main_post_index: build_generation_post_index(main_list_posts)
}
end
@@ -1125,7 +1099,7 @@ defmodule BDS.Generation do
main_root =
if targeted_plan.request_root_routes do
posts = build_list_posts(plan, data.published_list_posts, nil)
posts = build_list_posts(plan, render_data.main_list_posts, nil)
build_root_outputs(plan, plan.language, posts)
else
[]
@@ -1228,7 +1202,7 @@ defmodule BDS.Generation do
end
defp build_targeted_archive_outputs(plan, render_data, targeted_plan, builder) do
main_outputs = builder.(plan, render_data.data.post_index, targeted_plan, [plan.language])
main_outputs = builder.(plan, render_data.main_post_index, targeted_plan, [plan.language])
language_outputs =
Enum.flat_map(additional_languages(plan), fn language ->

View File

@@ -74,8 +74,8 @@ defmodule BDS.Generation.Data do
|> Enum.uniq_by(& &1.id)
|> Enum.sort_by(&{-(&1.created_at || 0), -(&1.published_at || 0), to_string(&1.slug)})
{published_route_posts, translations_by_post} =
build_generation_route_posts(
translations_by_post =
build_generation_translations(
plan.project_id,
project_data_dir,
published_posts,
@@ -87,7 +87,6 @@ defmodule BDS.Generation.Data do
project_data_dir: project_data_dir,
published_posts: published_posts,
published_list_posts: published_list_posts,
published_route_posts: published_route_posts,
translations_by_post: translations_by_post,
post_index: build_generation_post_index(published_list_posts)
}
@@ -121,18 +120,13 @@ defmodule BDS.Generation.Data do
post_language = String.downcase(to_string(post.language || ""))
effective_language = if post_language == "", do: main, else: post_language
cond do
is_binary(Map.get(post, :translation_source_slug)) ->
post
effective_language == target ->
post
true ->
case Map.get(translations_by_post_language, {post.id, target_language}) do
nil -> post
translation -> build_localized_subtree_variant(post, translation)
end
if effective_language == target do
post
else
case Map.get(translations_by_post_language, {post.id, target_language}) do
nil -> post
translation -> build_localized_subtree_variant(post, translation)
end
end
end)
end
@@ -231,7 +225,7 @@ defmodule BDS.Generation.Data do
read_snapshot(full_path, fallback_post, &build_post_snapshot/2)
end
defp build_generation_route_posts(
defp build_generation_translations(
project_id,
project_data_dir,
published_posts,
@@ -268,17 +262,7 @@ defmodule BDS.Generation.Data do
end)
|> Map.new(fn {post_id, translations} -> {post_id, Enum.reverse(translations)} end)
route_posts =
Enum.flat_map(published_posts, fn post ->
variants =
translations_by_post
|> Map.get(post.id, [])
|> Enum.map(&build_published_translation_variant(post, &1))
[post | variants]
end)
{route_posts, translations_by_post}
translations_by_post
end
defp published_translation_snapshot(project_data_dir, %Translation{} = translation) do
@@ -357,31 +341,6 @@ defmodule BDS.Generation.Data do
}
end
defp build_published_translation_variant(post, translation) do
%{
id: translation.id,
project_id: post.project_id,
title: translation.title,
slug: "#{post.slug}.#{translation.language}",
excerpt: translation.excerpt,
content: nil,
status: :published,
author: post.author,
created_at: post.created_at,
updated_at: translation.updated_at,
published_at: translation.published_at || post.published_at,
file_path: translation.file_path,
tags: post.tags,
categories: post.categories,
template_slug: post.template_slug,
language: translation.language,
do_not_translate: post.do_not_translate,
translation_source_slug: post.slug,
translation_canonical_language: post.language,
translation_file_path: translation.file_path
}
end
defp build_localized_subtree_variant(post, translation) do
%{
post

View File

@@ -22,16 +22,6 @@ defmodule BDS.Generation.Outputs do
def route_post_output_path(post, ""), do: post_output_path(post)
def route_post_output_path(post, route_language), do: post_output_path(post, route_language)
@spec suppress_subtree_translation_variants([map()], [String.t()]) :: [map()]
def suppress_subtree_translation_variants(route_posts, additional_languages) do
subtree_languages = MapSet.new(additional_languages)
Enum.reject(route_posts, fn post ->
is_binary(Map.get(post, :translation_source_slug)) and
MapSet.member?(subtree_languages, to_string(post.language))
end)
end
@spec build_validation_route_paths(map(), [map()], [map()], map(), String.t() | nil) :: [
String.t()
]

View File

@@ -28,15 +28,15 @@ defmodule BDS.Generation.Validation do
@spec build_post_timestamp_checks(String.t(), [map()], map()) :: [map()]
def build_post_timestamp_checks(
project_data_dir,
published_route_posts,
published_posts,
generated_file_updated_at
) do
build_timestamp_checks(
project_data_dir,
published_route_posts,
published_posts,
generated_file_updated_at,
&BDS.Generation.Paths.post_output_path/1,
fn post -> Map.get(post, :translation_file_path) || post.file_path end
& &1.file_path
)
end