Storybook: ngx-translate integration

Created on 17 Apr 2019  路  18Comments  路  Source: storybookjs/storybook

Hi,

How do I integrate ngx-translate so I can use it with Storybook for Angular?

Kind regards,
androdel

angular compatibility with other tools inactive question / support

Most helpful comment

Hi everyone,

I managed to make it work combining some of your solutions. The key is what @kroeder was suggesting of creating a "fake" I18 module and import it in the moduleMetadata. Moreover, the staticTranslateLoader can be used to handle translations within the component. Here I paste the working config that should be added to you component stories.
carbon

Thanks to everyone and I hope you have a nice day :-)

All 18 comments

What have you tried so far? A colleague implemented it in our mono-repo but as far as I can see we needed to do some extra stuff in our story

import { FakeMissingTranslationHandler, MissingTranslationHandler, TranslateLoader } from '@ngx-translate/core';
import { storiesOf } from '@storybook/angular';
import { of } from 'rxjs';
import { I18nModule } from '../i18n.module';
import { I18nDemoComponent } from './i18n-demo.component';

// symbols used in decorators need to be exported
export const staticTranslateLoader: TranslateLoader = {
    getTranslation(lang: string) {
        return of(require('./i18n/en.json'));
    }
};

storiesOf('Foundation', module)
    .add(
        'I18n',
        () => ({
            component: I18nDemoComponent,
            moduleMetadata: {
                imports: [
                    I18nModule.forRoot({
                        loader: {
                            provide: TranslateLoader,
                            useValue: staticTranslateLoader
                        },
                        missingTranslationHandler: {
                            provide: MissingTranslationHandler,
                            useClass: FakeMissingTranslationHandler
                        }
                    })
                ]
            }
        })
    );

Note: I18nModule is something we added. Replace this code with TranslationModule from ngx-translate. Maybe it works right away 馃檪

If this does not solve your issue, we configured it like that:

  • installed @ngx-tranlsate/core
  • added assets/i18n/en.json
  • In your angular.json (example with two different i18n directories)
              { "glob": "**", "input": "./src/assets/i18n", "output": "./i18n" },
              { "glob": "**", "input": "./projects/ui/src/assets/i18n", "output": "./i18n/ui" }
  • Add a loader
export function createStandardLoader(httpClient: HttpClient) {
    const paths = ['./i18n/ui', './i18n'];
    return new MergingMultiHttpLoader(httpClient, paths.map(prefix => ({
        prefix: prefix + '/', suffix: '.json'
    })));
}

Hello I seem to be facing the same issue as well..
I tried the method above but it didn't work

I did a fresh Angular installation and integrated ngx-translate into the actual angular app + into storybook
https://github.com/kroeder/storybook-ngx-translate

I also added a library project but haven't tried integrating ngx-translate there yet.

Please let me know if this is of any help

A couple of notes:

  • I created a I18nModule but it uses forRoot() for ngx-translate so this should only be imported once per app
  • Import it into your app.module.ts or any other root-module of your libs etc.
  • Every storybook story can declare module metadata, you need to import this module into your story
storiesOf('Button', module).add('with text', () => ({
  template: `
    <button>{{ 'basic.submit' | translate }}</button>
  `,
  moduleMetadata: {
    imports: [I18nModule]
  }
}));

Ofcourse, putting the code in a module would be the answer (stupid me).
Anyway, this raises another issue and that is that he cannot find my en.json file, but it is present.

Schermafbeelding 2019-04-21 om 09 53 20

Schermafbeelding 2019-04-21 om 09 58 02

PS: I'm working in an Angular 6 app

@androdel is it in src/assets or projects/your-project/src/assets ?
If you need a custom assets folder then try using https://storybook.js.org/docs/configurations/serving-static-files/#2-via-a-directory

@kroeder it's in src/assets

I now also started with a clean build and it works! Could the issue be Angular 6 related?

I don't think so. They might have added something to the default angular.json when bootstrapping a new angular app. Can you try to upgrading to 7 using ng update @angular/cli @angular/core in a branch?

https://angular.io/cli/update#ng-update

@kroeder

For some reason, i18nModule doesn't work out of the box. I had to use
translate.setDefaultLang('en');
translate.use('en');
everywhere inside the constructor of the Components for which I was creating the story.
Do you have any idea why that would be the case ?

@deepaksslibra https://github.com/kroeder/storybook-ngx-translate/blob/master/src/app/i18n/i18n.module.ts#L26 I did it in the constructor of my I18nModule

This does not work for you?

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Hi everyone,

I managed to make it work combining some of your solutions. The key is what @kroeder was suggesting of creating a "fake" I18 module and import it in the moduleMetadata. Moreover, the staticTranslateLoader can be used to handle translations within the component. Here I paste the working config that should be added to you component stories.
carbon

Thanks to everyone and I hope you have a nice day :-)

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

Thank you so much for the solution! Though, diving deeper I stumbled upon a problem. I started to use this feature with the 'StoriesOf' function, but eventually, I had to pass to an arrow function approach to build the story. The reason is, basically, for combine Knobs with the translate pipe and it works fine! Unfortunately, with the template on the arrow functions inputs works properly whilst actions don't! I leave an example below, thank you!
export const primary = () => ({ title: "UiEmptyScreen", moduleMetadata: { imports: [I18nModule], declarations: [UiEmptyScreenComponent], providers: [TranslateService] }, component: UiEmptyScreenComponent, template: [title]="title | translate"
[subtitle]="subtitle | translate"
[icon]="icon | translate"
[buttonTitle]="buttonTitle | translate"
(clickedButton)="clickedButton"

, props: { title: text("title", "dashboard.claim.empty_screen.TITLE"), subtitle: text("subtitle", "dashboard.claim.empty_screen.SUBTITLE"), icon: text("icon", "dashboard.claim.empty_screen.ICON"), buttonTitle: text( "buttonTitle", "dashboard.claim.empty_screen.BUTTON_TITLE" ), clickedButton: action("clickedButton event") } });

Component.ts:
`import { Component, Input, Output, EventEmitter } from "@angular/core";

@Component({
selector: "presenter-empty-screen",
templateUrl: "./ui-empty-screen.component.html",
styleUrls: ["./ui-empty-screen.component.scss"]
})
export class UiEmptyScreenComponent {
@Input() title: string;
@Input() subtitle: string;
@Input() icon: string;
@Input() buttonTitle: string;
@Output() clickedButton = new EventEmitter();

constructor() {}

onClick(): void {
this.clickedButton.emit();
}
}
`

I've solved it! I attach the code below. Anyway, I would like to be capable of switch language translate with a knob, is it possible?
Thanks!
export const englishComponent = () => ({ moduleMetadata: { imports: [I18nModule], declarations: [UiEmptyScreenComponent], providers: [TranslateService] }, component: UiEmptyScreenComponent, template: [title]="title | translate"
[subtitle]="subtitle | translate"
[icon]="icon | translate"
[buttonTitle]="buttonTitle | translate"
(clickedButton)="clickedButton()"

, props: { title: text("title", "dashboard.claim.empty_screen.TITLE"), subtitle: text("subtitle", "dashboard.claim.empty_screen.SUBTITLE"), icon: text("icon", "dashboard.claim.empty_screen.ICON"), buttonTitle: text( "buttonTitle", "dashboard.claim.empty_screen.BUTTON_TITLE" ), clickedButton: () => TranslateService.use("es") } });

I've written a small article about this subject, in case anyone is looking for help: https://medium.com/@dSebastien/using-ngx-translate-in-storybook-stories-3f4228f80e02

@dsebastien i have a nx monorepo angular + storybook setup, i tried what you described in your blog but i still get error because it cannot resolve that pipe translate on the templates. any clues?

```
// THE STORYBOOK NGX TRANSLATE CONFIG MODULE
import { TranslateModule, TranslateService, TranslateLoader } from "@ngx-translate/core"
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { NgModule } from '@angular/core';
import { HttpClient, HttpClientModule } from '@angular/common/http';

export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, './assets/i18n/', '.json');
}

@NgModule({
imports: [
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient],
},
})
],
providers: [TranslateService]
})
export class SBTranslateModule {
constructor(translateService: TranslateService) {
console.log("Configuring the translation service: ", translateService);
console.log("Translations: ", translateService.translations);
translateService.setDefaultLang("en-US");
translateService.use("en-US");
}
}

// THE STORYBOOK STORY
...
export default {
title: 'HeaderComponent',
decorators:[
moduleMetadata({
declarations:[HeaderComponent],
imports: [SBTranslateModule],
providers: [{ provide: APP_BASE_HREF, useValue: '/' }],
}),
]
};

const label = 'title';
const defaultValue = {
title: 'back',
routerLink: '/',
};
const groupId = 'GROUP-ID1';

export const Default = () => ({
props: {
title: text('text', 'Header!'),
back: object(label, defaultValue, groupId),
},
component: HeaderComponent,
});

I think I know why. You still need to import the TranslateModule, not only for .forRoot. You can do it like this for example:

@NgModule({
  imports: [
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient],
      },
    }),
    TranslateModule,
  ],
  providers: [TranslateService]
})
export class SBTranslateModule {
  constructor(translateService: TranslateService) {
    console.log("Configuring the translation service: ", translateService);
    console.log("Translations: ", translateService.translations);
    translateService.setDefaultLang("en-US");
    translateService.use("en-US");
  }
}

In my case I didn't need it because I have a CoreModule which takes care of it. I'll adapt the blog post to mention it! :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rpersaud picture rpersaud  路  3Comments

alexanbj picture alexanbj  路  3Comments

dnlsandiego picture dnlsandiego  路  3Comments

sakulstra picture sakulstra  路  3Comments

Jonovono picture Jonovono  路  3Comments