How to setup translation in Angular 16 with WebSDK?

We have an old Angular Cumulocity App that we migrate the whole way from Angular 12 up to Angular 16. For the migration step to Angular 16 we create a new app as described in the upgrade guide.
After many refactoring the application runs mostly like before. However, I was unable to solve one problem: The translation inside of the app.

For this I check out the language customization guide.
I copy the existing de.po, en.po and locales.pot into the folder <appname>/locales. I also copied the i18n.ts inside of the <appname>/src. Then I run

c8ycli locale-compile <appname>/locales/de.po
c8ycli locale-compile <appname>/locales/en.po

I copy the resulting json files into tenant-configuration/ui-assets.

I edit the options.json file:

"languages": {
    "de": {
      "name": "Germany",
      "nativeName": "Deutsch",
      "url": "/apps/public/ui-assets/de.json"
    "en": {
      "name": "English",
      "nativeName": "English",
      "url": "/apps/public/ui-assets/en.json"

When I now run my app yarn start the app starts normally. I get no error messages. When I go to http://localhost:4200/apps/ my app shows up. But no texts get translated. I only see the translation tokens. This is happen when a token gets translated via translate-pipe or inside of a component via Translation Service.

When I try to add the po files to the i18n.ts which is includes in the main.ts:

import 'locales/de.po';
import 'locales/en.po';

I the TranslationService is able to translate the tokens. Means everything I translate inside a component with this.translate(<token>) will be translated. But everything I translate via translation-Pipe still show only the token.

Im confuessed why the documentation say I should use the json-files but it doesn’t work at the end. Only the po-files work a little bit but not completely. How to setup translation correct? Are there any examples where I can see how it is done correctly?

Hi @David.Richter,

Can you provide the Web SDK version you are using?
The first approach, adding the translations via the public-options, is mainly meant for adding new languages while avoiding to rebuild the applications as this approach allows to add the additional translations to all applications.
The reason that the approach with the json file did not work for you, could be because of a missing dynamicOptionsUrl entry in your applications cumulocity.config.ts. This should usually be set to /apps/public/public-options/options.json.

As you are building a separate application and probably you only need the translated strings within that application, I would suggest to stick with the second approach of importing the *.po files.
Are you missing the translations of your custom application or are you missing the translations of the default texts?
Your i18n.ts file should look like this if you only want to support English and German:

import '@c8y/ng1-modules/core/locales/en.po';
import '@c8y/ng1-modules/core/locales/de.po';
import './locales/de.po';
import './locales/en.po';

When you build your application, can you verify that the generated de.json and en.json files (in the dist/<appname> folder) include your translations?

There is a widget-plugin sample plugin which also includes a sample custom translation.


1 Like

Hello @Tristan_Bastian ,

we currently on version 1019.21.1 of the Web SDK.

The complete i18n.ts looks like this:

 * Internationalizing files in po format (
 * You can always add additional strings by adding your own po file. All po files are
 * combined to one JSON file per language and are loaded if the specific language is needed.
import '@c8y/ngx-components/locales/de.po';
import '@c8y/ngx-components/locales/en.po';
import '@c8y/ngx-components/locales/es.po';
import '@c8y/ngx-components/locales/fr.po';
import '@c8y/ngx-components/locales/ja_JP.po';
import '@c8y/ngx-components/locales/ko.po';
import '@c8y/ngx-components/locales/nl.po';
import '@c8y/ngx-components/locales/pl.po';
import '@c8y/ngx-components/locales/pt_BR.po';
import '@c8y/ngx-components/locales/ru.po';
import '@c8y/ngx-components/locales/zh_CN.po';
import '@c8y/ngx-components/locales/zh_TW.po';
import 'locales/de.po';
import 'locales/en.po';

Seams a little bit different to what you posted. The default po files comes from ngx-components in my case while in your case the po files should come from ng1-modules.

I use now option 2 with the po files instead of json files. I build the project and check out the build directory. In dist/<appname> and found a de.json (and all the other languages too). In this I see all my translation tokens with the right translation from my po-files. For checking I edit my po file, rebuild the project and I see my edits in the resulting json file. Seams to work.

Where do I get the widget-plugin sample? Do you mean this GitHub - SoftwareAG/cumulocity-ui-plugin-examples (which is 8 years old)?

These imports from @c8y/ngx-components/locales/<lang>.po are actually the correct ones.
Which application did you select when running ng add @c8y/websdk?

You can select the widget-plugin when running: ng add @c8y/websdk.

I select hybrid app because we had a hybrid app before.

I checkout now the widget-plugin. I create a new project and add @c8y/websdk. I selected widget-plugin and check out the code. The only translation that I found was inside of the @NgModule deklaration with a hookComponent using getText from @c8y/ngx-components.

I add a new language token to the de.po file and add this to the template via translate-pipe. My IDE ask from where I want to add the TranslationModule

I was not sure what I have to choose so I try first @ngx-translate/core. TranslationModule from package @ngx-translate/core get add to the widget-plugin.module.ts. I try also all other Modules but the translation via translation-pipe doesn’t work. I still see only my translation token so the same problem I have in my project.

I’ve attached a sample application based on the widget-plugin.
I’ve added sample translation for the string to the de.po file:

msgid "My test string to be translated"
msgstr "Mein test string der übersetzt werden soll"

and added this HTML in the component:

      <h4>Not translated:</h4>
      <p>My test string to be translated</p>
      <h4>Translated via pipe:</h4>
      <p>{{ 'My test string to be translated' | translate }}</p>
      <h4>Translated via directive:</h4>
      <p translate>My test string to be translated</p>

I’ve imported the C8yTranslateModule, but the CoreModule, I18nModule and CommonModule should also work just fine.

These texts are translated as expected:

Please let me know in case you are receiving different result using the attached project. (175.2 KB)

Ok, you are right its working also for me. I was checking the wrong thing. I create in our project a new component and make it standalone and translation was also working in this component. I guess something is wrong in our modules. I will clean it up and maybe make more components standalone and I hope this will fix the problem in generell.

I can update the results here again, but it seems to be a project problem rather than a c8y.

So here my update after many trail and error. Testing with new components and so on. The problem was pretty simple. In our code base we more then one time import the CoreModule from @c8y/ngx-components with forRoot(). I could simple fix it by removing forRoot() in all cases where only components from CoreModule are needed.

Thank you for you help @Tristan_Bastian :slight_smile:

1 Like