Docs i18n: harden doc-mode pipeline

This commit is contained in:
Josh Palmer
2026-02-01 22:47:33 +01:00
parent da9f28d270
commit e70984745b
7 changed files with 760 additions and 37 deletions

View File

@@ -11,47 +11,37 @@ import (
"gopkg.in/yaml.v3"
)
func processFile(ctx context.Context, translator *PiTranslator, tm *TranslationMemory, docsRoot, filePath, srcLang, tgtLang string) error {
absPath, err := filepath.Abs(filePath)
func processFile(ctx context.Context, translator *PiTranslator, tm *TranslationMemory, docsRoot, filePath, srcLang, tgtLang string) (bool, error) {
absPath, relPath, err := resolveDocsPath(docsRoot, filePath)
if err != nil {
return err
}
relPath, err := filepath.Rel(docsRoot, absPath)
if err != nil {
return err
}
if relPath == "." || relPath == "" {
return fmt.Errorf("file %s resolves to docs root %s", absPath, docsRoot)
}
if filepath.IsAbs(relPath) || relPath == ".." || strings.HasPrefix(relPath, ".."+string(filepath.Separator)) {
return fmt.Errorf("file %s not under docs root %s", absPath, docsRoot)
return false, err
}
content, err := os.ReadFile(absPath)
if err != nil {
return err
return false, err
}
frontMatter, body := splitFrontMatter(string(content))
frontData := map[string]any{}
if frontMatter != "" {
if err := yaml.Unmarshal([]byte(frontMatter), &frontData); err != nil {
return fmt.Errorf("frontmatter parse failed for %s: %w", relPath, err)
return false, fmt.Errorf("frontmatter parse failed for %s: %w", relPath, err)
}
}
if err := translateFrontMatter(ctx, translator, tm, frontData, relPath, srcLang, tgtLang); err != nil {
return err
return false, err
}
body, err = translateHTMLBlocks(ctx, translator, body, srcLang, tgtLang)
if err != nil {
return err
return false, err
}
segments, err := extractSegments(body, relPath)
if err != nil {
return err
return false, err
}
namespace := cacheNamespace()
@@ -64,7 +54,7 @@ func processFile(ctx context.Context, translator *PiTranslator, tm *TranslationM
}
translated, err := translator.Translate(ctx, seg.Text, srcLang, tgtLang)
if err != nil {
return fmt.Errorf("translate failed (%s): %w", relPath, err)
return false, fmt.Errorf("translate failed (%s): %w", relPath, err)
}
seg.Translated = translated
entry := TMEntry{
@@ -86,16 +76,16 @@ func processFile(ctx context.Context, translator *PiTranslator, tm *TranslationM
translatedBody := applyTranslations(body, segments)
updatedFront, err := encodeFrontMatter(frontData, relPath, content)
if err != nil {
return err
return false, err
}
outputPath := filepath.Join(docsRoot, tgtLang, relPath)
if err := os.MkdirAll(filepath.Dir(outputPath), 0o755); err != nil {
return err
return false, err
}
output := updatedFront + translatedBody
return os.WriteFile(outputPath, []byte(output), 0o644)
return false, os.WriteFile(outputPath, []byte(output), 0o644)
}
func splitFrontMatter(content string) (string, string) {
@@ -125,8 +115,8 @@ func splitFrontMatter(content string) (string, string) {
}
func encodeFrontMatter(frontData map[string]any, relPath string, source []byte) (string, error) {
if len(frontData) == 0 {
return "", nil
if frontData == nil {
frontData = map[string]any{}
}
frontData["x-i18n"] = map[string]any{
"source_path": relPath,
@@ -154,6 +144,13 @@ func translateFrontMatter(ctx context.Context, translator *PiTranslator, tm *Tra
}
data["summary"] = translated
}
if title, ok := data["title"].(string); ok {
translated, err := translateSnippet(ctx, translator, tm, relPath+":frontmatter:title", title, srcLang, tgtLang)
if err != nil {
return err
}
data["title"] = translated
}
if readWhen, ok := data["read_when"].([]any); ok {
translated := make([]any, 0, len(readWhen))
for idx, item := range readWhen {