Issue
I was using this library ngx-translate-multi-http-loader to being able to load multiple language file for my ionic 5 App.
I didn't had any errors, but the translation wasn't working.
Then I added inside the TranslateModule.forRoot({})
the defaultLanguage: 'en'
, and it then resulted in the following error message
Something went wrong for the following translation file: ./assets/i18n/en.json
Database not created. Must call create() first
I needed a lot of time to figure it out how to fix it and couldn't find no help on internet.
Solution
2023 Fix
This error shouldn't happen anymore with >= 9.2.0. Since the library is now using httpBackend
The error
This topic pointed me in the right direction. Basically, the required loader that we use to import the translations files
export function HttpLoaderFactory(httpClient: HttpClient) {
return new MultiTranslateHttpLoader(httpClient, [
{ prefix: './assets/i18n/', suffix: '.json' },
{ prefix: `./assets/i18n/${APP_CONFIG.appType}/`, suffix: '.json' },
])
}
Is directly imported into the app.module.ts @ngModule
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient],
},
}),
If you find this post, it surely mean you're using an HttpInterceptor
in your app, that does request the data from the ionic storage (or any other services) to apply a specific logic to the request -> Let say, you want to add a token to the request.
Let's have the following example
@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
constructor(private _storage: Storage) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
// Clone the request to add the new header.
let authReq: any
return from(this.getToken()).pipe(
switchMap((token) => {
if (!req.headers.get('Authorization'))
authReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`),
})
else authReq = req.clone()
//send the newly created request
return next.handle(authReq).pipe(
retry(1),
catchError((err, caught) => {
const error = (err && err.error && err.error.message) || err.statusText
return throwError(error)
}) as any,
)
}),
)
}
getToken(): Observable<any> {
const token$ = new Observable((observer) => {
this._storage.get('token').then(async (token) => {
if (token) {
observer.next(token)
} else {
observer.next(ORGANISATION_TOKEN)
}
})
})
return token$
}
}
Then, because the ngx-translate-multi-http-loader
is requesting the translations file with the angular default http
class, you'll be passing through this http interceptor. But the _storage
isn't instantiated yet. which result in an Database not created. Must call create() first
which make our request fail into the Something went wrong for the following translation file: ./assets/i18n/en.json
The fix
We have to ignore this interceptor for that specific request.
For that purpose :
you could do it with a
httpBackend
-> here an explanation. But it wont work, because you wont have instantiate your services before the http call.You have to add a specific header, that tell your
HttpInterceptor
to ignore the request and let it going through without further investigation. Thx JohnrSharpe
export const InterceptorSkipHeader = 'X-Skip-Interceptor'
@Injectable()
export class UsHttpInterceptor implements HttpInterceptor {
constructor(private _storage: Storage, private _statusService: MegaphoneStatusService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (req.headers.has(InterceptorSkipHeader)) {
const headers = req.headers.delete(InterceptorSkipHeader)
return next.handle(req.clone({ headers }))
}
//... Intercept
For being able to pass that header into the ngx-translate-multi-http-loader
, well.. you can't. because the library do not accept it, but it isn't complicated to copy it.
// core.ngx-loader.ts
import { HttpClient, HttpHeaders } from '@angular/common/http'
import { TranslateLoader } from '@ngx-translate/core'
import merge from 'deepmerge'
import { forkJoin, Observable, of } from 'rxjs'
import { catchError, map } from 'rxjs/operators'
import { InterceptorSkipHeader } from '../http.interceptor'
export class MultiTranslateHttpLoader implements TranslateLoader {
constructor(
private http: HttpClient,
private resources: {
prefix: string
suffix: string
}[],
) {}
public getTranslation(lang: string): Observable<any> {
const headers = new HttpHeaders().set(InterceptorSkipHeader, '') // <-- Our Skip interceptor
const requests = this.resources.map((resource) => {
const path = resource.prefix + lang + resource.suffix
return this.http.get(path, { headers }).pipe( // <-- We add the header into the request
catchError((res) => {
console.error('Something went wrong for the following translation file:', path)
console.error(res.message)
return of({})
}),
)
})
return forkJoin(requests).pipe(map((response) => merge.all(response)))
}
}
And there you are.
Answered By - Raphaël Balet
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.