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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions