Skip to content

Fix compileTEsc() - defaultValue should be translated when translateFn is available #1742

@donRehan

Description

@donRehan

Current Behavior

When a template has <t t-esc="expr">defaultValue</t>, the defaultValue is hardcoded into the generated code WITHOUT translation.

Example generated code:

return text(withDefault(ctx['userName'], `Unknown User`));  // NOT translated

Expected Behavior

The defaultValue should be translated (if translateFn is provided), similar to how compileTSet() already does it.

Example generated code:

return text(withDefault(ctx['userName'], `Utilisateur Inconnu`));  // Translated!

Bug Location

File: src/compiler/code_generator.ts
Lines: 802-805 (in compileTEsc() method)

if (ast.defaultValue) {
  this.helpers.add("withDefault");
  // FIXME: defaultValue is not translated
  expr = `withDefault(${expr}, ${toStringExpression(ast.defaultValue)})`;
}

Similar Working Pattern (Reference)

File: src/compiler/code_generator.ts
Lines: 1121-1124 (in compileTSet() method)

if (ast.defaultValue) {
  const defaultValue = toStringExpression(
    ctx.translate ? this.translate(ast.defaultValue, ctx.translationCtx) : ast.defaultValue
  );
  // ... rest of code
}

This shows the correct pattern: check ctx.translate, then call this.translate() on the defaultValue.

Test Cases Created

I've created Jest tests to verify the fix. Run with: npm test -- compileTEsc_fix

import { compile } from "../src/compiler";

describe("compileTEsc fix - translation of defaultValue", () => {
  
  it("should translate defaultValue when translateFn is provided", () => {
    const template = `<t t-esc="userName">Unknown User</t>`;
    
    const mockTranslateFn = (text: string, ctx: string) => {
      const translations: { [key: string]: string } = {
        "Unknown User": "Utilisateur Inconnu",
      };
      return translations[text] || text;
    };
    
    const fn = compile(template, {
      translateFn: mockTranslateFn,
      hasGlobalValues: false,
    });
    
    const generatedCode = fn.toString();
    expect(generatedCode).toContain("Utilisateur Inconnu");
  });

  it("should NOT translate defaultValue when no translateFn is provided", () => {
    const template = `<t t-esc="userName">Unknown User</t>`;
    
    const fn = compile(template, {
      hasGlobalValues: false,
    });
    
    const generatedCode = fn.toString();
    expect(generatedCode).toContain("Unknown User");
  });

  it("should fallback to original when translation not found", () => {
    const template = `<t t-esc="userName">Some Rare Text</t>`;
    
    const mockTranslateFn = (text: string, ctx: string) => {
      const translations: { [key: string]: string } = {
        "Unknown User": "Utilisateur Inconnu",
      };
      return translations[text] || text;
    };
    
    const fn = compile(template, {
      translateFn: mockTranslateFn,
      hasGlobalValues: false,
    });
    
    const generatedCode = fn.toString();
    expect(generatedCode).toContain("Some Rare Text");
  });

  it("should handle empty defaultValue", () => {
    const template = `<t t-esc="userName"></t>`;
    
    const mockTranslateFn = (text: string, ctx: string) => {
      return text;
    };
    
    const fn = compile(template, {
      translateFn: mockTranslateFn,
      hasGlobalValues: false,
    });
    
    const generatedCode = fn.toString();
    expect(generatedCode).toBeDefined();
    expect(generatedCode.length).toBeGreaterThan(0);
  });

  it("should preserve spaces in translated defaultValue", () => {
    const template = `<t t-esc="userName">  Welcome Back  </t>`;
    
    const mockTranslateFn = (text: string, ctx: string) => {
      const translations: { [key: string]: string } = {
        "  Welcome Back  ": "  Bienvenue Retour  ",
      };
      return translations[text] || text;
    };
    
    const fn = compile(template, {
      translateFn: mockTranslateFn,
      hasGlobalValues: false,
    });
    
    const generatedCode = fn.toString();
    expect(generatedCode).toContain("Bienvenue Retour");
  });
});

Test Results (Current - Failing)

Tests:  2 failed, 3 passed, 5 total
  • ✓ Test 2 (no translateFn): PASS
  • ✓ Test 3 (no match): PASS
  • ✓ Test 4 (empty defaultValue): PASS
  • ✗ Test 1 (translate enabled): FAIL
  • ✗ Test 5 (preserve spaces): FAIL

Question for Maintainers

Is this the correct approach and test coverage for fixing this bug? If yes, I can proceed with implementing the fix. If there's a different approach or additional considerations, please let me know.

Related Code References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions