I try to use Angular official internationalization tools with angular universal. So far I'm able to translate the client side rendering using the following procedure (thanks to this answer https://stackoverflow.com/a/40930110/1110635) :
I add "i18n" attributes as stated in the documentation in my templates :
./src/+app/about/about.component.html :
<h1 i18n="H1 of the about component">About</h1>
...
Then I run :
./node_modules/.bin/ng-xi18n
to generate the base messages.xlf file.
Then I copy this file for each locale I want to support as "messages.[locale].xlf" in a "locale" folder. When ready, I create a "messages.[locale].ts" for each xlf file containing an exported string of its content :
./locale/messages.fr.ts :
// TRANSLATION_FR is only for "messages.fr.ts" of course.
// I would create a TRANSLATION_ES const inside "messages.es.ts" for spanish for example.
export const TRANSLATION_FR: string = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="004b222ff9ef9dd4771b777950ca1d0e4cd4348a" datatype="html">
<source>About</source>
<target>A propos</target>
<note priority="1" from="description">H1 of the about component</note>
</trans-unit>
</body>
</file>
</xliff>
`;
Finally, my client.ts file looks like the following :
./src/client.ts :
[...]
// i18n
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID } from '@angular/core';
import { TRANSLATION_FR } from '../locale/messages.fr';
import { MainModule } from './browser.module';
export const platformRef = platformUniversalDynamic();
// on document ready bootstrap Angular 2
export function main() {
return platformRef.bootstrapModule(MainModule, {
providers: [
{provide: TRANSLATIONS, useValue: TRANSLATION_FR},
{provide: TRANSLATIONS_FORMAT, useValue: "xlf"},
{provide: LOCALE_ID, useValue: 'fr'}
]
});
}
bootloader(main);
This works and make the "client side" application work as expected. "About" is replaced by "A propos". BUT, because angular universal pre-render the page on the server side using express the text is not translated until the client side bootstraping is done.
So when you first go on the page you see "About" for about 1 second before the client side kicks in an replace it with "A propos".
The solution seem obvious, simply run the translation service on the server side ! But I have no idea how to do that.
My server.ts looks like this :
./src/server.ts
[...]
// i18n
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID } from '@angular/core';
import { TRANSLATION_FR } from '../locale/messages.fr';
const app = express();
const ROOT = path.join(path.resolve(__dirname, '..', 'dist'));
// Express View
app.engine('.html', createEngine({
ngModule: MainModule,
providers: [
/**
* HERE IS THE IMPORTANT PART.
* I tried to declare providers but it has no effect.
*/
{provide: TRANSLATIONS, useValue: TRANSLATION_FR},
{provide: TRANSLATIONS_FORMAT, useValue: "xlf"},
{provide: LOCALE_ID, useValue: 'fr'}
]
}));
app.set('port', process.env.PORT || 3000);
app.set('views', ROOT);
app.set('view engine', 'html');
[...]
function ngApp(req, res) {
res.render('index', {
req,
res,
preboot: false,
baseUrl: '/',
requestUrl: req.originalUrl,
originUrl: `http://localhost:${ app.get('port') }`
});
}
app.get('*', ngApp);
// Server
let server = app.listen(app.get('port'), () => {
console.log(`Listening on: http://localhost:${server.address().port}`);
});
I have no direct access to the bootstrapModule method like on the client side. The providers key on the "createEngine" parameter object was already there in the original server.ts code.
What am I missing?