mwootendev / ngx-translate-plugins

Utilities plugins for the @ngx-translate translation library.
MIT License
16 stars 10 forks source link

Provide / generate placeholders for translation texts to ease component testing. #80

Open apokralipsa opened 9 months ago

apokralipsa commented 9 months ago

Is your feature request related to a problem? Please describe. We are using a following workflow to provide translations in the app:

  1. The keys are extracted during a build using ngx-translate-extract.
  2. The extracted file is uploaded to an external tool where translators provide texts in all the required languages.
  3. A scheduled job extracts the translations from that external tool and uploads it to a storage available to the running app.

This means that the translated texts are never committed into the repository along with the code and we cannot use them in tests directly.

At the same time, we would not like to tie our component tests to a specific implementation of the translation (TranslatePipe / TranslateService). We would like to still just test that the component renders the translated text, not the key itself.

The current solution we use looks something like the following:

const TRANSLATED: Record<string, string> = {
  'My text to translate': 'Mój przetłumaczony tekst'
};

describe('MyComponent', () => {
  // ...
  beforeEach(() => {
    TestBed.configureTestingModule({
      // ...
      imports: [TranslateTestingModule.withTranslations({ pl: TRANSLATED }).withDefaultLanguage('pl')]
    });
  });

  // ...

  it("should show translated text", () => {
    expect(allText()).toContain(TRANSLATED['My text to translate'])
  });

  // ...
});

While overall this works, there are at least a couple of serious pain points:

Describe the solution you'd like I would like the testing tools to return a clearly identifiable text for whatever translation key it is asked for. For example, for a key like "My translation key" it could return text like "Translation for 'My translation key'".

Ideally, when the key contains params, the values of those params would be included in the translation. For example, if the component template contains 'My key with {{param1}} and {{param2}}' | translate: {param1: "foo", param2: "bar"}, the rendered text could be "Translation for 'My key with {{param1}} and {{param2}}' with parameters: {param1: "foo", param2: "bar"}".

If this is done, the test stub I included above could be simplified to:

describe('MyComponent', () => {
  let translateService: TranslateService;
  // ...
  beforeEach(() => {
    TestBed.configureTestingModule({
      // ...
      imports: [TranslateTestingModule]
    });

    translateService = TestBed.inject(TranslateService)
  });

  // ...

  it('should show translated text', () => {
    expect(allText()).toContain(translateService.instant("My text to translate"));
  });

  // ...
});

Or even:

import { translationPlaceholderFor, TranslateTestingModule } from "ngx-translate-testing";

describe('MyComponent', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      // ...
      imports: [TranslateTestingModule]
    });
  });

  // ...

  it('should show translated text', () => {
    expect(allText()).toContain(translationPlaceholderFor('My text to translate'));
  });

  // ...
});

Describe alternatives you've considered I have considered writing a function that accepts translation keys as an array of strings and returns a TRANSLATED object as in the first example. This would still require us to repeat each translation key in the test file at least twice and would not play nicely with keys that contain parameters.

Additional context I think this could be introduced in a backwards compatible way. If somebody already imports TranslateTestingModule.withTranslations(...)... in their testing module, those translation could still be used instead of the new, placeholder ones. Alternatively, we could provide a separate method like TranslateTestingModule.withPlaceholderTranslations() to make the feature even more explicit and opt-in.

I am happy to provide a PR that would introduce this.