Open santhanam87 opened 8 years ago
The method setTranslation
has a 3rd parameter "shouldMerge: boolean" that you can use if you want to append translations instead of replacing them. You will have to use your own loader for that probably, but it's possible :)
Are there any plans of providing built-in support for partial loading?
I have implemented one for loading multiple files.
` /**/ import {TranslateLoader} from 'ng2-translate/ng2-translate'; import {Http, Response} from "@angular/http"; import {Observable} from "rxjs/Observable";
import '../../rxjs-extensions'
export class TranslateParitalLoader implements TranslateLoader {
constructor(private http: Http, private prefix: Array
/**
* Gets the translations from the server
* @param lang
* @returns {any}
*/
getObservableForHttp(value, combinedObject, lang: string) {
return Observable.create(observer => {
this.http.get(`${value}/${lang}${this.suffix}`)
.subscribe((res) => {
let responseObj = res.json();
Object.keys(responseObj).forEach(key=>{
combinedObject[key] = responseObj[key];
});
console.log(combinedObject);
observer.next(combinedObject);
//call complete if you want to close this stream (like a promise)
observer.complete();
});
});
}
public getTranslation(lang: string): Observable<any> {
var combinedObject = new Object();
var oldObsevers;
var newObserver;
this.prefix.forEach((value) =>{
newObserver = this.getObservableForHttp(value, combinedObject, lang);
if (oldObsevers == null) {
oldObsevers = newObserver;
}
else {
oldObsevers = oldObsevers.merge(newObserver);
}
});
return oldObsevers;
}
}`
I too run in to this problem. Highly appreciated if the solution is in-built.
Ganesh
@ocombe we are using angular-i18n for https://jhipster.github.io and now we are migrating to provide ng2 support. we are trying to use ng2-translate here but we are having trouble as we have a lot of partial files for each language and we used the partialLoader from angular-i18n. JHipster should bring you a lot users and downloads and it would be highly appreciated if the feature can be provided out of the box The PR for this is https://github.com/jhipster/generator-jhipster/pull/4304
+1 for partial loading support
Btw we have implemented a custom loader in JHipster for this and works quite nice :)
Thanks & Regards, Deepu
On Fri, Dec 9, 2016 at 1:28 AM, Phu Pham notifications@github.com wrote:
+1 for partial loading support
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ocombe/ng2-translate/issues/199#issuecomment-265897773, or mute the thread https://github.com/notifications/unsubscribe-auth/ABDlFxl3vgPSV7QzxDoKjrMGTp_OkXjVks5rGKCtgaJpZM4JlqmZ .
Is the loader somewhere public, so that I can add a link to it ?
But it would be even better if its supported out of the box as it would be the most practical use case in real systems
I'll add this with the new modular system for the next major version, the lib will use scoped modules and you will have multiple loaders to choose from in order to compose your perfect translate library :)
Hi @ocombe, so this next major version will allow you to split up your translations by modules? So lazy loaded modules can also have their translations files lazily loaded? Is that correct?
Any rough ETA's on that?
Yes that's correct, the beta 1 of the new version (6.0.0-beta.1) is already available
Loading separate language files for modules didn't work for me yet. As a workaround I'm using the following Gulp:
var gulp = require('gulp');
var merge = require('gulp-merge-json');
var fs = require('fs');
var path = require('path');
var languages = 'en,nl,de,es,pt'.split(',');
var i18n_source = 'resources/i18n';
var i18n_dest = 'src/assets/i18n';
// Currently unused but could be handy
function getDirs(dir) {
return fs.readdirSync(dir).filter(function(file) {
return fs.statSync(path.join(dir, file)).isDirectory();
});
}
// Merge multiple i18n json files together
gulp.task('i18n', function() {
return languages.map(function(lang) {
return gulp.src(`${ i18n_source }/*/${ lang }.json`)
.pipe(merge({
fileName: `${ lang }.json`
}))
.pipe(gulp.dest(i18n_dest));
});
});
It loads files from e.g. resources/*/en.json
, merges them and spits them out to src/assets/i18n/en.json
Hi
I'm a bit lost. Does the 6.0.0 version support multiple file loading ?
Regards
Julien
Hey @ocombe, I know you're probably super busy these days with your new work on the Angular team, but I was wondering if you could briefly explain how to use the partial loader for translations that only need to be loaded as the modules they belong to are lazily loaded.
When you said you will have multiple loaders to choose from in order to compose your perfect translate library
earlier in this thread - are those loaders already available in the master branch? I was digging around the source code and didn't seem to find any. If they exist, could you point them out so we can at least guesstimate how they should be used? Thanks :)
Hey, yes super busy with my new work in the core team (I expected to have more free time once I was freelance, but I'm actually working more).
To use a partial loader, someone will have to write one, it should be easy to do but I don't have much time to work on this :-/ If someone wants to start working on it, I could help
Is there any doc regarding how to use multiple loaders ?
Someone started working on partial loader?
@ocombe What will they come out with next?
+1 for partial loading support
Thank you @nabeelbukhari for the provided code snippet, loading translations from several folders works for me now.
However, when I use the custom loader, the translate-pipe does not work anymore - it always just returns the key that should be translated. Translations are loaded successfully, and using e.g. the directive translates values correctly. Am I missing something?
@Selupsis the reason the pipe is not working in @nabeelbukhari 's example is because it is calling observer.next() / observer.complete() too soon. It should call it after ALL paths have been loaded. I fixed this by implementing a counter and only calling .next/.complete after all paths are loaded.
I'm sure it can be written a lot cleaner or smarter, so happy to see any improvements! https://gist.github.com/vincentfierant/babfff11a152d4ca8d432b5c938ae2e0
Hi,
Using Observable.reduce worked for me for merging two translation files (for the sake of experiment I just concatenated '2' to the file name, but obviously you can refactor it to a loop over multiple prefixes). It works with the pipe - translations from both files are working fine.
class CustomLoader implements TranslateLoader {
constructor(private http: HttpClient, private prefix: string = "/assets/i18n/", private suffix: string = ".json") {}
public getTranslation(lang: string): any {
const $firstFile = this.http.get(`${this.prefix}${lang}${this.suffix}`);
const $secondFile = this.http.get(`${this.prefix}${lang}2${this.suffix}`);
const reducer = (translations, val) => { return Object.assign(translations, val) };
return Observable.merge($firstFile, $secondFile)
.reduce(reducer, {});
}
}
I wrote a article about how to have 1 json file per lazy loaded module without having to write a new Custom Loader etc... it's quiet simple, only the documentation is not clear in fact: https://medium.com/@TuiZ/how-to-split-your-i18n-file-per-lazy-loaded-module-with-ngx-translate-3caef57a738f
import {HttpClient} from '@angular/common/http';
import {TranslateLoader} from '@ngx-translate/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';
export function translateLoader(http: HttpClient) {
return new MultiTranslateHttpLoader(http, [
{prefix: './assets/i18n/', suffix: '.json'},
{prefix: './assets/i18n/countries-', suffix: '.json'}
]);
}
export class MultiTranslateHttpLoader implements TranslateLoader {
constructor(private http: HttpClient,
public resources: { prefix: string, suffix: string }[] = [{
prefix: '/assets/i18n/',
suffix: '.json'
}]) {}
/**
* Gets the translations from the server
* @param lang
* @returns {any}
*/
public getTranslation(lang: string): any {
return Observable.forkJoin(this.resources.map(config => {
return this.http.get(`${config.prefix}${lang}${config.suffix}`);
})).map(response => {
return response.reduce((a, b) => {
return Object.assign(a, b);
});
});
}
}
https://gist.github.com/BorisWechselberger/08e2424e1267ed27f9b4a046cc3357c8
@BorisWechselberger I did tried the code you provided above, it works for me!!!
Here is a problem I have faced, I guessed this problem is caused by the different package version:
To solve this problem, I changed the getTranslation() like this :
public getTranslation(lang: string): any {
return Observable.forkJoin(this.resources.map(config => {
return this.http.get(`${config.prefix}${lang}${config.suffix}`);
})).map(response => {
return response.reduce((a:any , b:any) => {
a._body = JSON.parse(a._body); //parse JSON String to Javascript Object
b._body = JSON.parse(b._body); //parse JSON String to Javascript Object
let obj:any = Object.assign(a._body, b._body);
return obj;
});
});
}
Here to share!! Feel free to feedback.
@Willis0826 , how do you implement it ? in app.module , app.component ?
Nice work, I'll probably need this momentarily.
On Tue, Dec 12, 2017 at 3:03 PM, Alberto Fuentes notifications@github.com wrote:
@Willis0826 https://github.com/willis0826 , how do you implement it ? in app.module , app.component ?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ngx-translate/core/issues/199#issuecomment-351209778, or mute the thread https://github.com/notifications/unsubscribe-auth/AX5XbtINz_iFx5M8uFAzWtQiO_-WDVxLks5s_vgdgaJpZM4JlqmZ .
-- "I studied engineering"
CONFIDENTIALITY NOTICE: The contents of this email message and any attachments are intended solely for the addressee(s) and may contain confidential and/or privileged information and may be legally protected from disclosure. It is then shared with tech companies, bots, hackers, government agencies, and marketers. The security of this message is none, and it may be shared on Instagram at anytime. If you are OK with this, please respond. There isn't really any security or privacy anywhere. If you disagree you may want to go camping and talk to people face-to-face like in old times.
@yaotzin68 Sorry for my late reply ! Yes, I implement the MultiTranslateHttpLoader
class in app.module.
I have created a github repository for a ngx translate http loader that can load multiple translation files: https://github.com/denniske/ngx-translate-multi-http-loader
Simple example: https://stackblitz.com/edit/ngx-translate-multi-http-loader-sample
I edited @Richie765 's comment so it can autodetect languages and I added a watch so it can detect changes in language files. code:
var gulp = require('gulp');
var merge = require('gulp-merge-json');
var i18n_source = 'src/resources/i18n';
var i18n_dest = 'src/assets/i18n';
var glob = require('glob');
// Merge multiple i18n json files together
function fixi18n(done) {
const langs = [
...new Set(
glob
.sync(`${i18n_source}/**/*.json`)
.map(x => x.match(/[ \w-]+?(?=\.)/gm)[0])
)
];
console.log('Fixing languages: ', langs);
return langs.map(function(lang) {
return gulp
.src(`${i18n_source}/**/${lang}.json`)
.pipe(
merge({
fileName: `${lang}.json`
})
)
.pipe(gulp.dest(i18n_dest, { overwrite: true }))
.on('end', () => done());
});
}
gulp.task('i18n', fixi18n);
function watchAppJs(done) {
return gulp
.watch(`${i18n_source}/**/*.*`, gulp.series(fixi18n))
.on('end', () => done());
}
gulp.task('default', gulp.series(fixi18n, watchAppJs));
Instead of ng serve i run with concurrently eg of package.json
"scripts": {
...
"prebuild": "gulp i18n",
"build": "ng build",
"serve": "concurrently --raw --kill-others --kill-others-on-fail \\"ng serve\\" \\"gulp\\" "
..
}
Hi,
@BorisWechselberger 's solution works, but so far as I can see, all the files will still be loaded on initial request? I have tried to use forChild()
in a lazy loaded method and then provide the custom loader, but that does not seem to work, I have to provide the loader at forRoot()
So it is required to do this in app.module.ts
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateLoader, //<-- Boris's loader
deps: [HttpClient]
},
isolate: false
}),
I cannot use the default loader in app.module.ts
, and once the user visits a lazy-loaded module that does need those translations, include those extra files, or can I?
lazy.module.ts
(whatever sub-module in a web application that is loaded via routing)
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: translateLoader, //<-- Boris's loader
deps: [HttpClient]
},
isolate: false
}),
I have also tried to follow the default documentation, using useClass
but that didn't help either:
I also modified the MultiTranslateHttpLoader
constructor to include the extra file but getTranslation
is never called anyway
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useClass: MultiTranslateHttpLoader,
deps: [HttpClient]
},
isolate: false
}),
@ocombe is this a bug? It does hit the constructor of MultiTranslateHttpLoader
but getTranslation
is not called. Looking forward to what ngx-translate v6 will bring.
How can I achieve this? Because as it seems now it would still need to load each file on initial request, I am not sure what the benefit of that is?
And for newcomers, you have to modify getTranslation
with newer version of rxjs (from 6.x I believe)
import { forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';
public getTranslation(lang: string): any {
return forkJoin(this.resources.map(config => {
return this.http.get(`${config.prefix}${lang}${config.suffix}`);
})).pipe(map(response => {
return response.reduce((a, b) => {
return Object.assign(a, b);
});
}));
}
Is there a way to have a file for each module in the Angular application and have the JSON structured so each translated text piece has all the language translations at the same point rather than in separate files? The risk of missing a piece would be greatly reduced. For example, something like this:
{ 'xm.sidebar.links.1': { title: 'Sidebar Link 1', desc: 'Top level navigation link 1', en: 'Home', fr: 'Maison', de: 'Zuhause' } }
As Angular apps are built (and split) around modularity, being able to structure and access translations in this manner would seem to make it easier to maintain. Additionally, it'd allow for it to be easier for a translation file to be a somewhat flat JSON so it would be simple to use the same text in multiple locations to keep changes uniform where required (if a reused term 'frequency' needed to be replaced with 'freq/dBm' it could be managed with a change in just one place).
My company, Hyland Software, has created a angular cli builder that will aggregate language files from libraries and app to create one master translation file per each language. Is this something the community would be interested in if we open sourced it?
@cgatian I was planning on making something similar for my future library Locl
, I'd like to take a look at it at least, if you don't decide to distribute it
@ocombe perhaps we can leave it generic enough to be used by multiple libraries? If you wouldnt mind hit me up on Twitter @cgatian
@cgatian sorry your DMs are closed on Twitter, you'll have to send me a message @OCombe 🙂
Anyone have an idea why I get this error? I am trying BorisWechselberger implementation
My application is big and I can't keep all the keys related to one language in a file, that would be so huge to load, Is there is any way to handle this case ?
Thanks, Santhanam E