From 026f0472cb6be656672c61ea80f9a3b5d76999a0 Mon Sep 17 00:00:00 2001 From: boyan-tonchev <108174872+boyan-tonchev@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:04:19 +0300 Subject: [PATCH] GDB-10443 Implement i18n in the new workbench (#1485) * Move the services to a separate directory. * Implements Service Provider What Implemented a service provider. Why The new service provider is responsible for managing shared service instances across microfrontends. Frameworks like Angular and React provide dependency injection, allowing a single instance to be injected into all components. In a single-spa application, such functionality does not exist by default. The service provider addresses this need. How The service provider includes lazy loading functionality that returns an instance of an already instantiated service. If the service is not present, a new one is created and stored in a static map. The static modifier ensures that only one instance of each service class is used across the microfrontends. * GDB-10443: Implement i18n in the New Workbench ## What - Implemented core functionality for emitting events across all microfrontends. - Implemented global-context.service.ts. - Implemented language.service.ts. ## Why - Microfrontends are separate modules and should not be aware of each other. The API is a shared module across all of them. The event emitting functionality enables communication between these modules. - The global context will maintain shared state between all microfrontends. - The service will manage language functionality. ## How - Utilized the browser's built-in custom event propagation. Events are emitted from the document body, ensuring that all microfrontends can listen to them. - The global state is designed to be extendable with additional data as needed. Currently, it includes a language field that holds the user's selected language. This can be accessed through the onLanguageChanged function, which returns a Subject and emits a value every time the language changes. - The language service manages language-related functionality. All microfrontends can use it to fetch and update the language. * GDB-10443: Implement i18n in the New Workbench ## What - Implemented core functionality for emitting events across all microfrontends; - Implemented global-context.service.ts; - Implemented language.service.ts; - Implemented translated label component in shared-components. ## Why - Microfrontends are separate modules and should not be aware of each other. The API is a shared module across all of them. The event emitting functionality enables communication between these modules; - The global context will maintain shared state between all microfrontends; - The service will manage language functionality; - To translate labels when the language is changed. ## How - Utilized the browser's built-in custom event propagation. Events are emitted from the document body, ensuring that all microfrontends can listen to them; - The global state is designed to be extendable with additional data as needed. Currently, it includes a language field that holds the user's selected language. This can be accessed through the onLanguageChanged function, which returns a Subject and emits a value every time the language changes; - The language service manages language-related functionality. All microfrontends can use it to fetch and update the language. * GDB-10443: Implement i18n in the Legacy Workbench What Integrated the translation functionality of the legacy workbench with the new language change implementation. Why The language selection functionality has been moved to the shared-component microfrontend, centralizing language changes across the application. How Added a listener for the language change event. When the event is triggered, the new language value is set in the translation service, which triggers the re-translating the entire workbench. * Removes the Workbench prefix. * Remove usage of global context * Add some missing documentation and changed the event emitter to use the document.body as a source for the emitted events instead of just `div` --------- Co-authored-by: svilen.velikov --- packages/api/package-lock.json | 17 +++ packages/api/package.json | 13 ++- packages/api/src/authentication-service.ts | 5 - packages/api/src/models/events/event.ts | 24 ++++ packages/api/src/ontotext-workbench-api.ts | 7 +- packages/api/src/repository-service.ts | 5 - packages/api/src/service.provider.ts | 23 ++++ .../src/services/authentication.service.ts | 7 ++ packages/api/src/services/event.service.ts | 44 ++++++++ packages/api/src/services/language.service.ts | 44 ++++++++ .../api/src/services/repository.service.ts | 7 ++ packages/api/src/services/service.ts | 4 + packages/api/tsconfig.json | 3 +- packages/legacy-workbench/src/app.js | 18 +++ .../src/js/angular/controllers.js | 9 -- packages/shared-components/.gitignore | 1 + packages/shared-components/package-lock.json | 17 +++ packages/shared-components/package.json | 1 + .../assets/graphdb-logo-sq.svg | 0 .../{components => }/assets/graphdb-logo.svg | 0 .../shared-components/src/assets/i18n/en.json | 42 +++++++ .../shared-components/src/assets/i18n/fr.json | 45 ++++++++ .../shared-components/src/components.d.ts | 59 ++++++++++ .../components/onto-header/onto-header.tsx | 23 +++- .../components/onto-navbar/onto-navbar.tsx | 6 +- .../src/components/onto-navbar/readme.md | 13 +++ .../src/components/translate-label/readme.md | 42 +++++++ .../test/translate-label.e2e.ts | 11 ++ .../test/translate-label.spec.tsx | 18 +++ .../translate-label/translate-label.scss | 3 + .../translate-label/translate-label.tsx | 44 ++++++++ .../translation/translation-observer.ts | 14 +++ .../translation/translation-parameter.ts | 4 + .../src/services/translation.service.ts | 105 ++++++++++++++++++ packages/shared-components/tsconfig.json | 4 + .../src/app/graphql/graphql.component.ts | 6 +- 36 files changed, 652 insertions(+), 36 deletions(-) delete mode 100644 packages/api/src/authentication-service.ts create mode 100644 packages/api/src/models/events/event.ts delete mode 100644 packages/api/src/repository-service.ts create mode 100644 packages/api/src/service.provider.ts create mode 100644 packages/api/src/services/authentication.service.ts create mode 100644 packages/api/src/services/event.service.ts create mode 100644 packages/api/src/services/language.service.ts create mode 100644 packages/api/src/services/repository.service.ts create mode 100644 packages/api/src/services/service.ts rename packages/shared-components/src/{components => }/assets/graphdb-logo-sq.svg (100%) rename packages/shared-components/src/{components => }/assets/graphdb-logo.svg (100%) create mode 100644 packages/shared-components/src/assets/i18n/en.json create mode 100644 packages/shared-components/src/assets/i18n/fr.json create mode 100644 packages/shared-components/src/components/translate-label/readme.md create mode 100644 packages/shared-components/src/components/translate-label/test/translate-label.e2e.ts create mode 100644 packages/shared-components/src/components/translate-label/test/translate-label.spec.tsx create mode 100644 packages/shared-components/src/components/translate-label/translate-label.scss create mode 100644 packages/shared-components/src/components/translate-label/translate-label.tsx create mode 100644 packages/shared-components/src/models/translation/translation-observer.ts create mode 100644 packages/shared-components/src/models/translation/translation-parameter.ts create mode 100644 packages/shared-components/src/services/translation.service.ts diff --git a/packages/api/package-lock.json b/packages/api/package-lock.json index b6b758162..252f55b89 100644 --- a/packages/api/package-lock.json +++ b/packages/api/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "@ontotext/workbench-api", "dependencies": { + "@reactivex/rxjs": "^6.6.7", "@types/jest": "^27.0.1", "@types/systemjs": "^6.1.1", "@types/webpack-env": "^1.16.2", @@ -2676,6 +2677,22 @@ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, + "node_modules/@reactivex/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/@reactivex/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-xZIV2JgHhWoVPm3uVcFbZDRVJfx2hgqmuTX7J4MuKaZ+j5jN29agniCPBwrlCmpA15/zLKcPi7/bogt0ZwOFyA==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@reactivex/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", diff --git a/packages/api/package.json b/packages/api/package.json index 29404fcdf..f867ad662 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -19,29 +19,30 @@ "@babel/eslint-parser": "^7.23.3", "@babel/plugin-transform-runtime": "^7.23.3", "@babel/preset-env": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", "@babel/runtime": "^7.23.3", "babel-jest": "^27.5.1", "concurrently": "^6.2.1", "cross-env": "^7.0.3", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", + "eslint-config-ts-important-stuff": "^1.1.0", "eslint-plugin-prettier": "^3.4.1", "identity-obj-proxy": "^3.0.0", "jest": "^27.5.1", "jest-cli": "^27.5.1", "prettier": "^2.3.2", "pretty-quick": "^3.1.1", + "ts-config-single-spa": "^3.0.0", + "typescript": "^4.3.5", "webpack": "^5.89.0", - "webpack-merge": "^5.8.0", "webpack-cli": "^4.10.0", - "webpack-dev-server": "^4.0.0", - "@babel/preset-typescript": "^7.23.3", - "eslint-config-ts-important-stuff": "^1.1.0", - "typescript": "^4.3.5", "webpack-config-single-spa-ts": "^4.0.0", - "ts-config-single-spa": "^3.0.0" + "webpack-dev-server": "^4.0.0", + "webpack-merge": "^5.8.0" }, "dependencies": { + "@reactivex/rxjs": "^6.6.7", "@types/jest": "^27.0.1", "@types/systemjs": "^6.1.1", "@types/webpack-env": "^1.16.2", diff --git a/packages/api/src/authentication-service.ts b/packages/api/src/authentication-service.ts deleted file mode 100644 index 00827ee56..000000000 --- a/packages/api/src/authentication-service.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class AuthenticationService { - static login(): string { - return "Athentication.login from the API"; - } -} diff --git a/packages/api/src/models/events/event.ts b/packages/api/src/models/events/event.ts new file mode 100644 index 000000000..6dc5cfc61 --- /dev/null +++ b/packages/api/src/models/events/event.ts @@ -0,0 +1,24 @@ +/** + * A generic shape of our internal event data payloads. + */ +export class Event { + /** + * The name of the event. + */ + readonly NAME; + /** + * The payload of the event. + */ + readonly payload; + /** + * Creates a new instance of the event. + * + * @param name - the name of the event. + * @param payload - the payload of the event. This is optional and if omitted, the event will have no payload, + * just a name. + */ + constructor(name: string, payload?: any) { + this.NAME = name; + this.payload = payload; + } +} diff --git a/packages/api/src/ontotext-workbench-api.ts b/packages/api/src/ontotext-workbench-api.ts index b3e680b18..65b07e4d7 100644 --- a/packages/api/src/ontotext-workbench-api.ts +++ b/packages/api/src/ontotext-workbench-api.ts @@ -1,3 +1,6 @@ // Anything exported from this file is importable by other in-browser modules. -export {AuthenticationService} from './authentication-service'; -export {RepositoryService} from './repository-service'; +export {AuthenticationService} from './services/authentication.service'; +export {RepositoryService} from './services/repository.service'; +export {ServiceProvider} from './service.provider' +export {EventService} from './services/event.service'; +export {LanguageService} from './services/language.service'; diff --git a/packages/api/src/repository-service.ts b/packages/api/src/repository-service.ts deleted file mode 100644 index f5f9c66be..000000000 --- a/packages/api/src/repository-service.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class RepositoryService { - static getRepositories(): Promise { - return fetch("http://localhost:9000/rest/repositories/all"); - } -} diff --git a/packages/api/src/service.provider.ts b/packages/api/src/service.provider.ts new file mode 100644 index 000000000..dddaa0d1f --- /dev/null +++ b/packages/api/src/service.provider.ts @@ -0,0 +1,23 @@ +import {Service} from './services/service'; + +/** + * Service provider for all {@link Service} instances. + * This provider caches all workbench services created on demand, ensuring that all micro frontends share a single instance of each service. + */ +export class ServiceProvider { + + /** + * The static modifier ensures the map is the same for all ServiceProviders. Each micro-frontend will have its + * own instance of {@see ServiceFactoryService}, but the map with instances will be shared. + * + * @private + */ + private static readonly SERVICE_INSTANCES = new Map + + public static get(type: { new(service: Service): T; }): T { + if (!ServiceProvider.SERVICE_INSTANCES.has(type.name)) { + ServiceProvider.SERVICE_INSTANCES.set(type.name, new type(this)); + } + return this.SERVICE_INSTANCES.get(type.name) as T; + } +} diff --git a/packages/api/src/services/authentication.service.ts b/packages/api/src/services/authentication.service.ts new file mode 100644 index 000000000..89b2fafc1 --- /dev/null +++ b/packages/api/src/services/authentication.service.ts @@ -0,0 +1,7 @@ +import {Service} from './service'; + +export class AuthenticationService implements Service { + login(): string { + return "Athentication.login from the API"; + } +} diff --git a/packages/api/src/services/event.service.ts b/packages/api/src/services/event.service.ts new file mode 100644 index 000000000..e5f6cbba9 --- /dev/null +++ b/packages/api/src/services/event.service.ts @@ -0,0 +1,44 @@ +import {Event} from '../models/events/event'; +import {Service} from './service'; + +/** + * Service used for global communication within all modules. It allows emitting and subscribing to CustomEvents across + * the application where the events are emitted via the document.body element. This allows for the events + * to be caught by any component in any module. + */ +export class EventService implements Service { + /** + * Emits a {@link CustomEvent} of type passed event.NAME and detail event.payload. + * + * @param event - the event to be emitted. + * @return the emitted event. + */ + emit(event: Event): CustomEvent { + const customEvent = new CustomEvent(event.NAME, {detail: event.payload}); + this.getHostElement().dispatchEvent(customEvent); + return customEvent; + } + + /** + * Subscribes for event of type eventName. + * + * @param eventName - type of subscription event. + * @param callback - callback function that will be called when the event occurred. + * + * @return unsubscribe function which can be used for manual unsubscription. + */ + subscribe(eventName: string, callback: (payload: any) => void): () => void { + const listener = (event) => { + if (event instanceof CustomEvent) { + callback(event.detail); + } + }; + this.getHostElement().addEventListener(eventName, listener); + + return () => this.getHostElement().removeEventListener(eventName, listener); + } + + private getHostElement(): HTMLElement { + return document.body; + } +} diff --git a/packages/api/src/services/language.service.ts b/packages/api/src/services/language.service.ts new file mode 100644 index 000000000..ec3d1cf2c --- /dev/null +++ b/packages/api/src/services/language.service.ts @@ -0,0 +1,44 @@ +import {Service} from './service'; +import {ReplaySubject, Subject} from '@reactivex/rxjs/dist/package'; + +/** + * The LanguageService class manages the application's language settings. + */ +export class LanguageService implements Service { + + static readonly DEFAULT_LANGUAGE = 'en'; + private readonly selectedLanguage = new ReplaySubject(1); + + /** + * Constructs a new LanguageService instance. + * Reads the initial language setting from local storage (not yet implemented) + * and sets it as the first value of the ReplaySubject. + */ + constructor() { + // TODO read it from local store and pass it as first value + } + + /** + * Changes the current language of the application. + * This method updates the language setting and notifies all subscribers + * about the language change. The new language is also intended to be saved + * to local storage (not yet implemented). + * + * @param {string} locale - The new language code to set (e.g., 'en', 'fr', 'de'). + */ + changeLanguage(locale: string): void { + // TODO save it to local store. + this.selectedLanguage.next(locale); + } + + /** + * Returns an observable that emits the current language whenever it changes. + * Subscribers to this observable will receive updates whenever the language + * is changed using the `changeLanguage` method. + * + * @returns {Subject} An observable stream of language changes. + */ + onLanguageChanged(): Subject { + return this.selectedLanguage; + } +} diff --git a/packages/api/src/services/repository.service.ts b/packages/api/src/services/repository.service.ts new file mode 100644 index 000000000..22632f246 --- /dev/null +++ b/packages/api/src/services/repository.service.ts @@ -0,0 +1,7 @@ +import {Service} from './service'; + +export class RepositoryService implements Service { + getRepositories(): Promise { + return fetch("http://localhost:9000/rest/repositories/all"); + } +} diff --git a/packages/api/src/services/service.ts b/packages/api/src/services/service.ts new file mode 100644 index 000000000..4f06d06d3 --- /dev/null +++ b/packages/api/src/services/service.ts @@ -0,0 +1,4 @@ +/** + * Interface for identifying a service as part of the workbench system. + */ +export interface Service {} diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index 09157ecc3..1ecff6536 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "ts-config-single-spa", - "files": [ - "src/authentication-service.ts", "src/ontotext-workbench-api.ts"], + "files": ["src/ontotext-workbench-api.ts"], "compilerOptions": { "declarationDir": "dist", "outDir": "dist", diff --git a/packages/legacy-workbench/src/app.js b/packages/legacy-workbench/src/app.js index 3aa927bd5..fa4c8b2ca 100644 --- a/packages/legacy-workbench/src/app.js +++ b/packages/legacy-workbench/src/app.js @@ -15,6 +15,7 @@ import 'angular/core/directives/operations-statuses-monitor/operations-statuses- import 'angular/core/directives/autocomplete/autocomplete.directive'; import 'angular/core/directives/prop-indeterminate/prop-indeterminate.directive'; import {defineCustomElements} from 'ontotext-yasgui-web-component/loader'; +import {ServiceProvider, LanguageService} from "@ontotext/workbench-api"; // $translate.instant converts from strings to <b> // and $sce.trustAsHtml could not recognise that this is valid html @@ -157,6 +158,7 @@ const moduleDefinition = function (productInfo) { // to construct version/edition-specific links. $menuItemsProvider.setProductInfo(productInfo); + // TODO this remove this when clean code. The main menu is processed in shared-components let mainMenu = PluginRegistry.get('main.menu'); mainMenu.forEach(function (menu) { menu.items.forEach(function (item) { @@ -218,6 +220,22 @@ const moduleDefinition = function (productInfo) { ThemeService.applyDarkThemeMode(); GuidesService.init(); + + // ========================= + // Functions and configurations for integration with the shared-components module. + // ========================= + const languageService = ServiceProvider.get(LanguageService); + + const languageChangeSubscriptions = languageService.onLanguageChanged() + .subscribe((language) => { + $translate.use(language); + }); + + $rootScope.$on('destroy', () => { + if (languageChangeSubscriptions) { + languageChangeSubscriptions.unsubscribe(); + } + }) }]); workbench.filter('titlecase', function() { diff --git a/packages/legacy-workbench/src/js/angular/controllers.js b/packages/legacy-workbench/src/js/angular/controllers.js index abb4ece93..956e53861 100644 --- a/packages/legacy-workbench/src/js/angular/controllers.js +++ b/packages/legacy-workbench/src/js/angular/controllers.js @@ -23,7 +23,6 @@ import './guides/directives'; import {GUIDE_PAUSE} from './guides/tour-lib-services/shepherd.service'; import 'angular-pageslide-directive/dist/angular-pageslide-directive'; import 'angularjs-slider/dist/rzslider.min'; -// import {AuthenticationService, RepositoryService} from "@ontotext/workbench-api"; angular .module('graphdb.workbench.se.controllers', [ @@ -61,14 +60,6 @@ homeCtrl.$inject = ['$scope', '$rootScope', '$http', '$repositories', '$jwtAuth' function homeCtrl($scope, $rootScope, $http, $repositories, $jwtAuth, $licenseService, AutocompleteRestService, LicenseRestService, RepositoriesRestService, RDF4JRepositoriesRestService, toastr) { - // console.log(`LOGIN TS API in new WB`, AuthenticationService.login()); - // RepositoryService.getRepositories().then((response) => { - // console.log(`response`, response); - // return response.json(); - // }).then((data) => { - // console.log(`REPOSITORIES TS API in new WB`, data); - // }); - $scope.doClear = false; $scope.getActiveRepositorySize = function () { diff --git a/packages/shared-components/.gitignore b/packages/shared-components/.gitignore index c3ea58a61..5b05faa92 100644 --- a/packages/shared-components/.gitignore +++ b/packages/shared-components/.gitignore @@ -24,3 +24,4 @@ $RECYCLE.BIN/ Thumbs.db UserInterfaceState.xcuserstate .env +api/ diff --git a/packages/shared-components/package-lock.json b/packages/shared-components/package-lock.json index e561737e2..65bd22acf 100644 --- a/packages/shared-components/package-lock.json +++ b/packages/shared-components/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@reactivex/rxjs": "^6.6.7", "font-awesome": "^4.7.0", "single-spa": "^6.0.1" }, @@ -1039,6 +1040,22 @@ } } }, + "node_modules/@reactivex/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/@reactivex/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-xZIV2JgHhWoVPm3uVcFbZDRVJfx2hgqmuTX7J4MuKaZ+j5jN29agniCPBwrlCmpA15/zLKcPi7/bogt0ZwOFyA==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@reactivex/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 95cae22d5..d74f96376 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -54,6 +54,7 @@ "puppeteer": "^21.9.0" }, "dependencies": { + "@reactivex/rxjs": "^6.6.7", "font-awesome": "^4.7.0", "single-spa": "^6.0.1" } diff --git a/packages/shared-components/src/components/assets/graphdb-logo-sq.svg b/packages/shared-components/src/assets/graphdb-logo-sq.svg similarity index 100% rename from packages/shared-components/src/components/assets/graphdb-logo-sq.svg rename to packages/shared-components/src/assets/graphdb-logo-sq.svg diff --git a/packages/shared-components/src/components/assets/graphdb-logo.svg b/packages/shared-components/src/assets/graphdb-logo.svg similarity index 100% rename from packages/shared-components/src/components/assets/graphdb-logo.svg rename to packages/shared-components/src/assets/graphdb-logo.svg diff --git a/packages/shared-components/src/assets/i18n/en.json b/packages/shared-components/src/assets/i18n/en.json new file mode 100644 index 000000000..7e3dee567 --- /dev/null +++ b/packages/shared-components/src/assets/i18n/en.json @@ -0,0 +1,42 @@ +{ + "common.import": "Import", + "menu.rdf.label": "RDF", + "menu.explore.label": "Explore", + "menu.graphs.overview.label": "Graphs overview", + "menu.class.relationships.label": "Class relationships", + "menu.class.hierarchy.label": "Class hierarchy", + "menu.similarity.label": "Similarity", + "menu.sparql.label": "SPARQL", + "menu.monitor.label": "Monitor", + "menu.queries.and.updates.label": "Queries and Updates", + "menu.backup_and_restore.label": "Backup and Restore", + "menu.enableFtsIndex.label": "Enable full-text search (FTS) index", + "menu.ftsIndexes.label": "FTS indexes to build (comma delimited)", + "menu.ftsStringLiteralsIndex.label": "FTS index for xsd:string literals", + "menu.ftsIrisIndex.label": "FTS index for full-text indexing of IRIs", + "menu.ftsDefaultAnalyzer.label": "Analyzer for the default index", + "menu.resources.label": "System", + "menu.setup.label": "Setup", + "menu.lab.label": "Lab", + "menu.ttyg.label": "Talk to Your Graph", + "menu.repositories.label": "Repositories", + "menu.aclmanagement.label": "ACL Management", + "menu.users.and.access.label": "Users and Access", + "menu.my.settings.label": "My Settings", + "menu.cluster.label": "Cluster", + "menu.connectors.label": "Connectors", + "menu.namespaces.label": "Namespaces", + "menu.autocomplete.label": "Autocomplete", + "menu.jdbc.label": "JDBC", + "menu.sparql.template.label": "SPARQL Templates", + "menu.license.label": "License", + "menu.help.label": "Help", + "menu.system.information.label": "System information", + "menu.rest.api.label": "REST API", + "menu.documentation.label": "Documentation", + "menu.tutorials.label": "Tutorials", + "menu.support.label": "Support", + "menu.guides.label": "Interactive guides", + "menu.plugins.label": "Plugins", + "visual.graph.label": "Visual graph" +} diff --git a/packages/shared-components/src/assets/i18n/fr.json b/packages/shared-components/src/assets/i18n/fr.json new file mode 100644 index 000000000..15b90adfb --- /dev/null +++ b/packages/shared-components/src/assets/i18n/fr.json @@ -0,0 +1,45 @@ +{ + "common.import": "Importer", + "menu.rdf.label": "RDF", + "menu.explore.label": "Explorez", + "menu.graphs.overview.label": "Aperçu des graphes", + "menu.class.relationships.label": "Relations entre les classes", + "menu.class.hierarchy.label": "Hiérarchie des classes", + "menu.visual.graph.label": "Graphique visuel", + "menu.similarity.label": "Similarité", + "menu.sparql.label": "SPARQL", + "menu.monitor.label": "Surveiller", + "menu.queries.and.updates.label": "Requêtes et mises à jour", + "menu.backup_and_restore.label": "Sauvegarde et Restauration", + "menu.enableFtsIndex.label": "Activer l'index de recherche plein texte", + "menu.ftsIndexes.label": "Index de recherche en texte intégral à construire (délimités par des virgules)", + "menu.ftsStringLiteralsIndex.label": "Index de recherche en texte intégral pour les littéraux xsd:string", + "menu.ftsIrisIndex.label": "Index de recherche en texte intégral pour l'indexation en texte intégral des IRI", + "menu.ftsDefaultAnalyzer.label": "Analyseur pour l'index par défaut", + "menu.ftsLanguages.label": "Entrez la langue pour la recherche plein texte", + "menu.resources.label": "Système", + "menu.setup.label": "Configurer", + "menu.lab.label": "Laboratoire", + "menu.ttyg.label": "Parlez à votre graphe", + "menu.repositories.label": "Dépôts", + "menu.aclmanagement.label": "Gestion ACL", + "menu.users.and.access.label": "Utilisateurs et accès", + "menu.my.settings.label": "Mes paramètres", + "menu.cluster.label": "Cluster", + "menu.connectors.label": "Connecteurs", + "menu.namespaces.label": "Espaces de noms", + "menu.autocomplete.label": "Autocomplétion", + "menu.rdf.rank.label": "Rang RDF", + "menu.jdbc.label": "JDBC", + "menu.sparql.template.label": "Modèles SPARQL", + "menu.license.label": "Licence", + "menu.help.label": "Aide", + "menu.system.information.label": "Informations sur le système", + "menu.rest.api.label": "API REST", + "menu.documentation.label": "Documentation", + "menu.tutorials.label": "Tutoriels", + "menu.support.label": "Support", + "menu.guides.label": "Guides interactifs", + "menu.plugins.label": "Plugins", + "visual.graph.label": "Graphique visuel" +} diff --git a/packages/shared-components/src/components.d.ts b/packages/shared-components/src/components.d.ts index 9185b2053..1cfda0b8e 100644 --- a/packages/shared-components/src/components.d.ts +++ b/packages/shared-components/src/components.d.ts @@ -6,7 +6,9 @@ */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { ExternalMenuModel } from "./components/onto-navbar/menu-model"; +import { TranslationParameter } from "./models/translation/translation-parameter"; export { ExternalMenuModel } from "./components/onto-navbar/menu-model"; +export { TranslationParameter } from "./models/translation/translation-parameter"; export namespace Components { interface OntoFooter { } @@ -15,6 +17,20 @@ export namespace Components { interface OntoNavbar { "menuItems": ExternalMenuModel; } + /** + * The purpose of this component is to display translated literals in the DOM. A Stencil component re-renders when a prop or state changes, + * but it may not re-render when the language changes. In such cases, this component should be used. It handles language change events + * and re-translates the passed language and translation parameters. + * Example of usage: + * + * + * + * + * + * + * ; "onto-header": LocalJSX.OntoHeader & JSXBase.HTMLAttributes; "onto-navbar": LocalJSX.OntoNavbar & JSXBase.HTMLAttributes; + /** + * The purpose of this component is to display translated literals in the DOM. A Stencil component re-renders when a prop or state changes, + * but it may not re-render when the language changes. In such cases, this component should be used. It handles language change events + * and re-translates the passed language and translation parameters. + * Example of usage: + * + * + * ; } } } diff --git a/packages/shared-components/src/components/onto-header/onto-header.tsx b/packages/shared-components/src/components/onto-header/onto-header.tsx index 94f72db3d..0816d6e90 100644 --- a/packages/shared-components/src/components/onto-header/onto-header.tsx +++ b/packages/shared-components/src/components/onto-header/onto-header.tsx @@ -1,4 +1,5 @@ import { Component, Host, h } from '@stencil/core'; +import {ServiceProvider, LanguageService} from "@ontotext/workbench-api"; @Component({ tag: 'onto-header', @@ -6,13 +7,33 @@ import { Component, Host, h } from '@stencil/core'; shadow: false, }) export class OntoHeader { + + private languageService: LanguageService; + + // TODO remove this when implement language selector + private locale = 'en' + + constructor() { + this.languageService = ServiceProvider.get(LanguageService); + } + + // TODO remove this when implement language selector + private onLanguageChanged(): void { + if (this.locale === 'en') { + this.locale = 'fr'; + } else { + this.locale = 'en'; + } + this.languageService.changeLanguage(this.locale); + } + render() { return (
🔍
TestRepo ⌄
-
EN ⌄
+
this.onLanguageChanged()}>EN ⌄
); diff --git a/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx b/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx index d25fc9479..db53885e8 100644 --- a/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx +++ b/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx @@ -101,14 +101,14 @@ export class OntoNavbar {