-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a93702b
Showing
34 changed files
with
19,882 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
dist/ | ||
.idea | ||
coverage | ||
.DS_Store | ||
|
||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
lerna-debug.log* | ||
|
||
# Dependency directories | ||
node_modules/ | ||
jspm_packages/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# Translation UI Plugin | ||
|
||
## General | ||
|
||
Manage your translations in Cumulocity IoT for all supported languages easily using the Translation UI Plugin. Translations can be added and updated using the Translation table. Translations are globally available in all Cumulocity applications running on the same Cumulocity tenant via the `public-options` application and its `options.json` file. | ||
|
||
The Translation UI Plugin automatically register a new navigation entry in the left side navigation menu. The Translation Plugin can be accessed by navigating to `Configuration` -> `Localization`. | ||
|
||
> **Important:** If the Plugin is used in an application it needs to check if the `public-options` application exists. The `public-options` application is used to store and share translations to the different applications. In case the application doesn't exist, it will automatically be created in the Cumulocity tenant via the Translation UI Plugin. Creating this application requires `Admin` permission on `Application management`. Therefore, make sure the plugin is initally called with a user having the necessary permission, otherwise an error will be thrown. | ||
## Add new term with translations | ||
|
||
To add a new term and corresponding translations follow these steps: | ||
|
||
1. Click on the button `Add term` at the top-right in the action bar. | ||
|
||
2. In the upcoming modal dialog provide the term (key), which should be translated, and the related translations. | ||
|
||
3. Click on `Save` to store the changes locally. | ||
|
||
4. In order for the translations to be applied to Cumulocity and its application, click on the `Apply` button in the action bar. | ||
|
||
5. A success notification appears if the changes could be applied successfully | ||
|
||
6. The translations are directly applied to your application. | ||
|
||
![alt translation plugin demo](/assets/translation_plugin.gif) | ||
|
||
## Update existing translations | ||
|
||
Translations can be updated directly in the table or via the modal dialog. | ||
|
||
**Table** | ||
|
||
1. Hover over the cell in the table for the translation, which you want to update. | ||
|
||
2. Click on the `Pencil` icon to edit the translation | ||
|
||
3. Update the translation and click on the `Checkmark` icon to save the translation locally | ||
|
||
4. Click on `Apply` in the action bar to apply the update to Cumulocity and its applications. | ||
|
||
![alt translation plugin update translation directly in table](/assets/translation-update-table_plugin.gif) | ||
|
||
**Modal dialog** | ||
|
||
1. For the term you want to update the translations for, click on the `Edit` button at the right of the corresponding row. | ||
|
||
2. Update the translations for the relevant languages | ||
|
||
3. Click on `Save` to store the changes locally. | ||
|
||
4. In order for the translations to be applied to Cumulocity and its application, click on the `Apply` button in the action bar. | ||
|
||
5. A success notification appears if the changes could be applied successfully | ||
|
||
![alt translation plugin update translation via modal dialog](/assets/translation-update-modal_plugin.gif) | ||
|
||
## Delete a term | ||
|
||
1. Hover over the row of the term you want to delete. | ||
|
||
2. Click on the `Delete` button, which is displayed on hovering over the row. | ||
|
||
3. In the upcoming confirmation dialog click on the `Confirm` button. | ||
|
||
4. In order for the changes to take affect click on the `Apply` button in the action bar. | ||
|
||
5. A success notification appears if the changes could be applied successfully. | ||
|
||
![alt translation plugin delete a translation](/assets/translation-delete_plugin.gif) | ||
|
||
## Local development | ||
|
||
### Recommended version | ||
|
||
* node v 14.x | ||
* npm v 6.x | ||
|
||
### Plugin versions | ||
|
||
* Angular v 14.x | ||
* WebSDK v 1016.0.x | ||
|
||
**How to start** | ||
Change the target tenant and application you want to run this plugin on in the `package.json`. | ||
|
||
``` | ||
c8ycli server -u https://{{your-tenant}}.cumulocity.com/ --shell {{cockpit}} | ||
``` | ||
Keep in mind that this plugin needs to have an app (e.g. cockpit) running with at least the same version as this plugin. if your tenant contains an older version, use the c8ycli to create a cockpit clone running with at least v 1016.0.59! Upload this clone to the target tenant (e.g. cockpit-1016) and reference this name in the --shell command. | ||
|
||
The widget plugin can be locally tested via the start script: | ||
|
||
``` | ||
npm start | ||
``` | ||
|
||
In the Module Federation terminology, `widget` plugin is called `remote` and the `cokpit` is called `shell`. Modules provided by this `widget` will be loaded by the `cockpit` application at the runtime. This plugin provides a basic custom widget that can be accessed through the `Add widget` menu. | ||
|
||
> Note that the `--shell` flag creates a proxy to the cockpit application and provides` AdvancedMapWidgetModule` as an `remote` via URL options. | ||
Also deploying needs no special handling and can be simply done via `npm run deploy`. As soon as the application has exports it will be uploaded as a plugin. | ||
|
||
------------------------------ | ||
These tools are provided as-is and without warranty or support. They do not constitute part of the Software AG product suite. Users are free to use, fork and modify them, subject to the license agreement. While Software AG welcomes contributions, we cannot guarantee to include every contribution in the master project. | ||
_____________________ | ||
For more information you can Ask a Question in the [TECHcommunity Forums](http://tech.forums.softwareag.com/techjforum/forums/list.page?product=cumulocity). | ||
You can find additional information in the [Software AG TECHcommunity](http://techcommunity.softwareag.com/home/-/product/name/cumulocity). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
describe('Example test', () => { | ||
/*let testComponent; | ||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ExampleModule] | ||
}); | ||
testComponent = TestBed.createComponent(TestComponent); | ||
});*/ | ||
|
||
test('Always true', () => { | ||
expect(true).toBe(true); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { NgModule } from '@angular/core'; | ||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||
import { RouterModule as ngRouterModule } from '@angular/router'; | ||
import { BootstrapComponent, CoreModule, RouterModule } from '@c8y/ngx-components'; | ||
import { BsModalRef } from 'ngx-bootstrap/modal'; | ||
import { TranslationDirectoryModule } from './modules/translation-manager/translation-directory.module'; | ||
|
||
// Translations | ||
import './locales/de.po'; | ||
|
||
@NgModule({ | ||
imports: [ | ||
BrowserAnimationsModule, | ||
ngRouterModule.forRoot([], { enableTracing: false, useHash: true }), | ||
RouterModule.forRoot(), | ||
CoreModule.forRoot(), | ||
TranslationDirectoryModule, | ||
], | ||
providers: [BsModalRef], | ||
bootstrap: [BootstrapComponent] | ||
}) | ||
export class AppModule {} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/** | ||
* Internationalizing files in po format (https://en.wikipedia.org/wiki/Gettext#Translating) | ||
* 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 './locales/de.po'; // <- adding additional strings to the german translation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import './polyfills'; | ||
import './i18n'; | ||
|
||
import { enableProdMode } from '@angular/core'; | ||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; | ||
import { AppModule } from './app.module'; | ||
|
||
declare const __MODE__: string; | ||
if (__MODE__ === 'production') { | ||
enableProdMode(); | ||
} | ||
|
||
export function bootstrap() { | ||
platformBrowserDynamic() | ||
.bootstrapModule(AppModule) | ||
.catch(err => console.log(err)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// jest.config.js | ||
module.exports = { | ||
preset: 'jest-preset-angular', | ||
setupFilesAfterEnv: ['<rootDir>/setup-jest.js'], | ||
transformIgnorePatterns: ['/!node_modules\\/lodash-es/'] | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: c8y.plugin\n" | ||
"Language: de\n" | ||
"Content-Type: text/plain; charset=UTF-8\n" | ||
"Content-Transfer-Encoding: 8bit\n" | ||
"Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||
|
||
msgid "Terms" | ||
msgstr "Begriffe" | ||
|
||
msgid "Add term" | ||
msgstr "Begriff hinzufügen" | ||
|
||
msgid "Add translation" | ||
msgstr "Übersetzung hinzufügen" | ||
|
||
msgid "Edit translation" | ||
msgstr "Übersetzung bearbeiten" | ||
|
||
msgid "Localization" | ||
msgstr "Lokalisierung" | ||
|
||
msgid "Add term and translations" | ||
msgstr "Begriff und Übersetzungen hinzufügen" | ||
|
||
msgid "Please provide atleast one translation to save the key." | ||
msgstr "Bitte mindestens eine Übersetzung angeben, damit der Pfad gespeichert werden kann" | ||
|
||
msgid "e.g. Group" | ||
msgstr "z.B. Gruppe" |
45 changes: 45 additions & 0 deletions
45
...-manager/manage-translation-cell-renderer/manage-translation-cell-renderer.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<div | ||
(click)="editCell()" | ||
*ngIf="!isCellEditable" | ||
class="text-truncate pointer d-flex" | ||
title="{{ context.value }}" | ||
> | ||
<span *ngIf="!isCellEditable && context.value !== ''" class="text-truncate"> | ||
{{ context.value }} | ||
</span> | ||
<span | ||
*ngIf="!isCellEditable && context.value === ''" | ||
class="text-truncate" | ||
title="{{ 'Add translation' | translate }}" | ||
> | ||
<em class="text-muted">{{ 'Add translation' | translate }}</em> | ||
</span> | ||
<i | ||
c8yIcon="pencil" | ||
title="{{ 'Edit translation' | translate }}" | ||
class="showOnHover text-primary m-l-4" | ||
></i> | ||
</div> | ||
|
||
<div class="input-group input-group-sm" *ngIf="isCellEditable"> | ||
<input | ||
type="text" | ||
placeholder="{{ 'Add translation' | translate }}" | ||
class="form-control" | ||
#cellInput | ||
[(ngModel)]="cellValue" | ||
/> | ||
<div class="input-group-btn"> | ||
<button | ||
class="btn btn-clean" | ||
type="button" | ||
title="{{ 'Cancel' | translate }}" | ||
(click)="cancel()" | ||
> | ||
<i c8yIcon="times" class="text-danger"></i> | ||
</button> | ||
<button class="btn btn-clean" type="button" title="{{ 'Save' | translate }}" (click)="save()"> | ||
<i c8yIcon="check" class="text-primary"></i> | ||
</button> | ||
</div> | ||
</div> |
120 changes: 120 additions & 0 deletions
120
...nager/manage-translation-cell-renderer/manage-translation-cell-renderer.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { CellRendererContext } from '@c8y/ngx-components'; | ||
import { ManageTranslationCellRendererComponent } from './manage-translation-cell-renderer.component'; | ||
|
||
describe('ManageTranslationCellRendererComponent', () => { | ||
let component: ManageTranslationCellRendererComponent; | ||
let translationDirectoryServiceMock: any; | ||
let contextMock: CellRendererContext; | ||
let c8yModalServiceMock: any; | ||
let translateServiceMock: any; | ||
|
||
beforeEach(() => { | ||
translationDirectoryServiceMock = { | ||
saveTranslationLocally: jest.fn(), | ||
validateTranslationIsProvided: jest.fn(), | ||
deleteTranslationItem: { emit: jest.fn() }, | ||
saveTranslationItem: { emit: jest.fn() }, | ||
normalizeTranslationValues: jest.fn(), | ||
isAssetTypeOrCustomProperty: jest.fn() | ||
}; | ||
|
||
c8yModalServiceMock = { | ||
confirm: jest.fn() | ||
}; | ||
|
||
translateServiceMock = { | ||
instant: jest.fn() | ||
}; | ||
|
||
contextMock = new CellRendererContext(); | ||
contextMock.item = { translationKey: 'asset' }; | ||
contextMock.property = { name: 'de', path: 'de' }; | ||
|
||
component = new ManageTranslationCellRendererComponent( | ||
contextMock, | ||
translationDirectoryServiceMock, | ||
c8yModalServiceMock, | ||
translateServiceMock | ||
); | ||
|
||
jest.useFakeTimers(); | ||
}); | ||
|
||
describe('ManageTranslationCellRendererComponent test suite', () => { | ||
it('User clicking on Edit icon to update the translation values', () => { | ||
// given | ||
const cellVal = 'Deutsch translation'; | ||
component.context.value = cellVal; | ||
component.cellInput = { nativeElement: {} }; | ||
component.cellInput.nativeElement.focus = jest.fn(); | ||
|
||
// when | ||
component.editCell(); | ||
jest.runAllTimers(); | ||
|
||
// expect | ||
expect(component.cellValue).toBe(cellVal); | ||
expect(component.isCellEditable).toBeTruthy(); | ||
}); | ||
|
||
it('User clicking on Save icon without providing any input values', () => { | ||
// given | ||
component.cellValue = ''; | ||
|
||
// when | ||
component.save(); | ||
|
||
// expect | ||
expect(component.isCellEditable).not.toBeTruthy(); | ||
expect(component.context.value).toBe(''); | ||
}); | ||
|
||
it('should display popup if no translation is provided for the key, on click of cancel should remove the popup', async () => { | ||
// given | ||
component.cellValue = ''; | ||
const modalSpy = spyOn(c8yModalServiceMock, 'confirm').and.returnValue(Promise.reject()); | ||
|
||
// when | ||
await component.save(); | ||
|
||
// expect | ||
expect(modalSpy).toHaveBeenCalled(); | ||
expect(component.isCellEditable).not.toBeTruthy(); | ||
}); | ||
|
||
it('User clicking on Save icon by providing input values', () => { | ||
// given | ||
component.cellValue = 'sample'; | ||
|
||
// when | ||
component.save(); | ||
|
||
// expect | ||
expect(component.context.value).toBe('sample'); | ||
}); | ||
|
||
it('If translation value is not provided while saving the cell', () => { | ||
// given | ||
component.context.value = ''; | ||
const spyDirectoryService = jest | ||
.spyOn(translationDirectoryServiceMock, 'validateTranslationIsProvided') | ||
.mockReturnValue(true); | ||
|
||
// when | ||
component.save(); | ||
|
||
// expect | ||
expect(translationDirectoryServiceMock.validateTranslationIsProvided('sample')).toBe(true); | ||
expect(spyDirectoryService).toHaveBeenCalled(); | ||
expect(component.context.item).toStrictEqual({ de: '', translationKey: 'asset' }); | ||
}); | ||
|
||
it('User clicking on Cancel icon should cancel the changes', () => { | ||
// when | ||
component.cancel(); | ||
|
||
// expect | ||
expect(component.isCellEditable).not.toBeTruthy(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.