Ngx-translate and Ionic 3?

#1

Has anyone tried ngx-translate with the latest Ionic 3 lazy loading stuff?

1 Like
Ionic view & ngx-translate problem
#2

Yes, it works for me. Just follow their github guide and it should work.

1 Like
#3

Can you share with me your app.module.ts and/or your shared.module.ts? I’ve tried everything in the docs they suggested, including the custom TranslationHandler and isolate: true which I don’t need.

I keep getting this error:

Runtime error
No provider for TranslateStore()

Here’s my shared module:

import { NgModule } from '@angular/core';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { Http } from '@angular/http';

export function TranslateLoaderFactory(http: Http) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

@NgModule({
  imports: [
    TranslateModule.forChild({
      loader: {
        provide: TranslateLoader,
        useFactory: (TranslateLoaderFactory),
        deps: [ Http ]
      }
    })
  ],
  exports: [
    TranslateModule
  ]
})

export class SharedModule {
}

Here’s my ionic info:

Cordova CLI: 6.5.0

Ionic Framework Version: 3.0.1
Ionic CLI Version: 2.2.1
Ionic App Lib Version: 2.1.7
Ionic App Scripts Version: 1.3.1
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Windows 10
Node Version: v6.10.0
Xcode version: Not installed

Any ideas?

1 Like
#4

I updated the the ng2-translate guide in the ionic docs yesterday to work with ngx-translate: https://ionicframework.com/docs/resources/ng2-translate/

3 Likes
#5

I load the module in app.module.js

import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
export function createTranslateLoader(http: Http) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

and in ngModule imports

TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: (createTranslateLoader),
        deps: [Http]
      }
    }),
#6

Thanks for that. I’m following everything in that guide and more specifically the ngx-translate docs for lazy loading in ng2/Ionic2 by calling the TranslateModule.forChild() instead. I’m using a SharedModule, but I’ve tried importing it into my app.module.ts and even calling forRoot() but I still get the following runtime error:

No provider for TranslateStore!

I’d love to use the lazy loading stuff, if for no other reason to reduce friction from the ionic g page default functionality. Having to tell my coworkers to remember to delete the module and remove @IonicPage is troublesome.

Any ideas?

#7

I opened a issue on the ngx-translate GitHub repo because I also don’t know how to implement lazy loading together with ngx-translate.
I hope they will awnser soon so I can update the guide for that.

2 Likes
#8

I see it. Thank you.

#9

In case of lazy loading I think you should…

in app.module.ts, change nothing aka something like:

export function exportTranslateStaticLoader(http: Http) {
    return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

 ...
   imports: [
  ...
 TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: exportTranslateStaticLoader,
                deps: [Http]
            }
        }
    ),

In you child module, add it like following:

@NgModule({
    declarations: [
        MyPage
    ],
    imports: [
        IonicPageModule.forChild(MyPage),
        TranslateModule.forChild() // <= Here what you need to add for ngx
    ]
})
export class MyPageModule {
}
4 Likes
#10

Ah yes, I forget this point! I will test it out and update the docs.

It works fine now! Thank you

1 Like
#11

Have a look here: https://github.com/driftyco/ionic-starter-super/issues/98
It includes a working plunker reference for ngx-translate and refers to multiple
PRs for ionic-starter-super related to this.
e.g. https://github.com/driftyco/ionic-starter-super/pull/95
It is still waiting on some TLC from ionic.

#12

Does this result in the translate code being duplicated in every single lazily loaded page?

#13

@rapropos This is what I have in the settings page:

import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { LanguageService } from "../../providers/language-service";
import { LanguageModel } from "../../models/language-model";

@Component({
  selector: 'page-settings',
  templateUrl: 'settings.html'
})
export class SettingsPage {

  languageSelected: any = 'en';
  languages: Array<LanguageModel>;

I do not know if that means all translations are duplicated in each page.
It is a good question!

#14

@rapropos do you mean the code itself in the bundles.js (= app size) or the translations data them self?

When it goes to the translations data, according the doc, per default, they will be shared across modules

Lazy loaded modules

When you lazy load a module, you should use the forChild static method to import the TranslateModule.

Since lazy loaded modules use a different injector from the rest of your application, you can configure them separately with a different loader/parser/missing translations handler. You can also isolate the service by using isolate: true. In which case the service is a completely isolated instance (for translations, current lang, events, …). Otherwise, by default, it will share its data with other instances of the service (but you can still use a different loader/parser/handler even if you don’t isolate the service).

#15

Yes, this. When I experimented with lazy page loading, every custom component that I imported got duplicated. See app-scripts #867. I fear that this might also befall you and others going down this path with ngx-translate. Perhaps it isn’t so large that it has a measurable effect, but it definitely did for me.

#16

@rapropos First I took the same decision as you did, I experimented lazy loading but didn’t followed that pass yet. About your question, I just had a look at my experimental branch and I’ve to say that it seems that ngx-translate isn’t duplicated respectively only included once in the main bundle. But, don’t know if it’s a side effect of my design because I lazy load every pages except the first one (the login page).

I did the following:

First I generated all source map:

source-map-explorer main.js main.js.map 

to

 source-map-explorer 29.main.js 29.main.js.map 

and then checked the html files like following

grep -rnw *.html -e "ngx"

which only gave me one file as result

1 Like
#17

Thank you for your investigation.

1 Like
#18

ngx translation with ionic 3 app not working for me. below is my code:
app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { HttpModule,Http } from '@angular/http';
import { IonicStorageModule } from '@ionic/storage';

import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

import { MyApp } from './app.component';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';



export function createTranslateLoader(http: Http) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

@NgModule({
  declarations: [
    MyApp
  ],
  imports: [
    BrowserModule,
    HttpModule,
    IonicStorageModule.forRoot(),
    TranslateModule.forRoot({
                    loader: {
                      provide: TranslateLoader,
                      useFactory: (createTranslateLoader),
                      deps: [Http]
                    }
                  }),
    IonicModule.forRoot(MyApp),
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
  ]
})
export class AppModule {}

app.component.ts

import { Component, ViewChild } from '@angular/core';
import { Nav, Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { Storage } from '@ionic/storage';
import { TranslateService } from '@ngx-translate/core';


@Component({
  templateUrl: 'app.html'
})

export class MyApp {
  @ViewChild(Nav) nav: Nav;
  public rootPage: any;

  constructor(public platform: Platform, 
    public statusBar: StatusBar, 
    public splashScreen: SplashScreen,
    public storage: Storage,
    public translate: TranslateService) {

    this.storage.get('AppLangcode')
          .then((AppLangcode) => {
            if(AppLangcode==null){
              translate.setDefaultLang('en');
            }else{
              translate.setDefaultLang(AppLangcode);
            }
          })
  }

  initializeApp() {
    this.platform.ready().then(() => {
      this.statusBar.styleDefault();
      this.splashScreen.hide();
      this.menu.swipeEnable(false);

    });
  }
}

In above file i am checking storage preference in local db and then set it to default language on load application.

My RootPage home.module.ts

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { HomePage } from './home';
import { TranslateModule } from '@ngx-translate/core';

@NgModule({
  declarations: [
    HomePage,
  ],
  imports: [
    IonicPageModule.forChild(HomePage),
    TranslateModule.forChild()
  ],
  exports: [
    HomePage
  ]
})
export class HomePageModule {}

home.ts

import { Component} from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { TranslateService } from '@ngx-translate/core';

@IonicPage()
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, 
    public navParams: NavParams,
    public storage: Storage,
    public translate: TranslateService,) {

  }

  ionViewDidLoad() {
    //console.log('ionViewDidLoad HomePagePage');
  }

}

home.html

<ion-header>
  <ion-navbar color='navbarColor'>
    <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title><img src="assets/icon/logo.png" alt="Ionic logo"></ion-title>
  </ion-navbar>
</ion-header>

<ion-content  class="grid-basic-page">
    <ion-grid>
        <ion-row>
          <ion-col (click)="openPage('QuickBookPage');">
            <div><img src="assets/icon/icon-book-cylinder.png">{{"quick_book_pay" | translate}}</div>
          </ion-col>
          <ion-col (click)="openPage('RefilHistoryPage');">
            <div><img src="assets/icon/icon-quickpay.png">{{"refil_history" | translate}}</div>
          </ion-col>
        </ion-row>

        <ion-row>
          <ion-col (click)="openPage('ServicesPage');">
            <div><img src="assets/icon/icon-mechanic.png">{{"service_request" | translate}}</div>
          </ion-col>
          <ion-col>
          <button [disabled]="!clickhandle" (click)="emergencyCall();"><img src="assets/icon/icon-emergency.png">{{"emergency_helpline" | translate}}</button>
          </ion-col>
        </ion-row>  
  </ion-grid>

</ion-content>

Side Menu Page language.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams,Events } from 'ionic-angular';
import { NgForm,FormBuilder, FormGroup, Validators  } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { Storage } from '@ionic/storage';


@IonicPage()
@Component({
  selector: 'page-language',
  templateUrl: 'language.html',
})
export class LanguagePage {
  public langform:FormGroup;
  public langcod:string;

  constructor(public navCtrl: NavController, 
    public navParams: NavParams,
    public formBuilder: FormBuilder,
    public translate: TranslateService,
    public storage: Storage) {

    this.storage.get('AppLangcode')
          .then((AppLangcode) => {
            
            if(AppLangcode==null){
              this.langcod = 'en';
              this.langform.get('langcode').setValue(this.langcod);
            }else{
              this.langcod = AppLangcode;
              this.langform.get('langcode').setValue(this.langcod);
            }
          })

    this.langform = formBuilder.group({
      langcode: [this.langcod, Validators.required]
    });
  }

  langselect(form: NgForm){
    let langselcode = this.langform.value.langcode;
    this.storage.set('AppLangcode', langselcode);
    this.translate.setDefaultLang(langselcode);
    this.translate.use(langselcode);
  }

}

language.module.ts

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { LanguagePage } from './language';
import { TranslateModule } from '@ngx-translate/core';

@NgModule({
  declarations: [
    LanguagePage,
  ],
  imports: [
    IonicPageModule.forChild(LanguagePage),
    TranslateModule.forChild()
  ],
  exports: [
    LanguagePage
  ]
})
export class LanguagePageModule {}

language.html

<ion-header>
 <ion-navbar color='navbarColor'>
    <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title><img src="assets/icon/logo.png" alt="Ionic logo"></ion-title>
  </ion-navbar>
</ion-header>


<ion-content padding>
<form [formGroup]="langform" (submit)="langselect($event)">

    <ion-list radio-group  formControlName="langcode">
      <ion-row responsive-sm>
      <ion-col col-6>
            <ion-item>
              <ion-label>{{"english" | translate}}</ion-label>
              <ion-radio value="en" checked></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{"hindi" | translate}}</ion-label>
              <ion-radio value="hi"></ion-radio>
            </ion-item>
      </ion-col>
    </ion-row>

  </ion-list>

    <ion-row responsive-sm>
      <ion-col>
        <button ion-button block type="submit" [disabled]="!langform.valid">
            Submit
        </button>
      </ion-col>
    </ion-row>
  </form>

</ion-content>

en.json

{
    "english"   : "English",
    "hindi"     : "हिंदी",

    "quick_book_pay":"Quick Book & Pay",
    "refil_history":"Refill History",
    "service_request":"Service Request",
    "emergency_helpline":"Emergency Helpline"
}

hi.json

{
    "english"   : "English",
    "hindi"     : "हिंदी",
    
    "quick_book_pay":"त्वरित बुक और भुगतान करें",
    "refil_history":"रीफिल इतिहास",
    "service_request":"सेवा अनुरोध",
    "emergency_helpline":"आपातकालीन हेल्पलाइन"
}

On change language it show keys instead translation. Please let me know what wrong i am doing ? I am looking for the issue for a long.

similar issue posted here https://stackoverflow.com/questions/44371233/ionic-3-with-ngx-translate-lazy-loaded

Ngx-translate and lazy loading - some languages not found
#19
  1. Your code looks good. May only guess is maybe trying to move your initialization part from app.component constructor to app.component ngAfterViewInit for example ? A wild guess is that maybe this.storage isn’t yet initialized when you try to access it in your app.component contructor and therefore the default language couldn’t be set?

  2. If I doesn’t miss it I’ve to feeling you are not telling ngx-translate which language should be use. You set a default language but doesn’t set the one to use. I would add this.translateService.use(…);

  3. Also it’s possible to guess the language without the storage, so I would do something like

     ngAfterViewInit() {
        let userLang = navigator.language.split('-')[0];
       userLang = /(hi)/gi.test(userLang) ? userLang : 'en';
    
     this.translateService.setDefaultLang('en');
     this.translateService.use(userLang);
    
     this.storage.get('AppLangcode')
       .then((AppLangcode) => {
         if(AppLangcode==null){
            this.translateService.use(userLang);
         }else{
            this.translateService.use(AppLangcode);
         }
       });
     }
    

Not sure it will work but maybe i will give you some ideas?

#20
  1. Thanks for your response. storage preference working good and it also set default language when app initlize. like if hi language it set in storage then my home page data also shows in hi langauge.
    this code working fine (you can see app.component.ts ):
this.storage.get('AppLangcode')
  .then((AppLangcode) => {
    if(AppLangcode==null){
      translate.setDefaultLang('en');
    }else{
      translate.setDefaultLang(AppLangcode);
    }
  })

2.On change language i am doing 3 tasks, adding preference to storage for future app uses. setting langauge to default and using also for current session.

see Side Menu Page language.ts

langselect(form: NgForm){
    let langselcode = this.langform.value.langcode;
    this.storage.set('AppLangcode', langselcode);
    this.translate.setDefaultLang(langselcode);
    this.translate.use(langselcode);
  }

3.i didn’t get purpose of below code, do app remember my default preference without save it for future session ?

 let userLang = navigator.language.split('-')[0];
  userLang = /(hi)/gi.test(userLang) ? userLang : 'en';
1 Like