From 58f6e6bde7bdda462f4c625da5744614349d33f4 Mon Sep 17 00:00:00 2001 From: Gal Podlipnik Date: Wed, 11 Jun 2025 13:51:26 +0200 Subject: [PATCH] locale --- frontend/libs/shared/locale/README.md | 7 ++ frontend/libs/shared/locale/eslint.config.mjs | 34 ++++++ frontend/libs/shared/locale/jest.config.ts | 21 ++++ frontend/libs/shared/locale/project.json | 20 ++++ frontend/libs/shared/locale/src/index.ts | 4 + .../shared/locale/src/lib/common.namespace.ts | 17 +++ .../libs/shared/locale/src/lib/common.sl.ts | 10 ++ .../libs/shared/locale/src/lib/common.t.ts | 12 +++ .../locale/src/lib/locale-shell.component.ts | 10 ++ .../shared/locale/src/lib/locale.routes.ts | 27 +++++ frontend/libs/shared/locale/src/test-setup.ts | 6 ++ frontend/libs/shared/locale/tsconfig.json | 28 +++++ frontend/libs/shared/locale/tsconfig.lib.json | 17 +++ .../libs/shared/locale/tsconfig.spec.json | 16 +++ .../shared/navbar/src/lib/navbar.component.ts | 8 +- frontend/libs/web/containers/src/index.ts | 1 + .../src/lib/containers.component.ts | 8 +- .../translations/containers-sl.translation.ts | 7 ++ .../lib/translations/containers.namespace.ts | 13 +++ .../src/lib/translations/containers.t.ts | 12 +++ .../lib/translations/translation.directive.ts | 11 ++ .../src/lib/translations/translation.pipe.ts | 9 ++ frontend/package-lock.json | 100 +++++++++++++++++- frontend/package.json | 2 + frontend/public/env.js | 0 frontend/src/app/app.config.ts | 4 + frontend/src/app/app.routes.ts | 54 +++++++--- frontend/tsconfig.base.json | 1 + 28 files changed, 444 insertions(+), 15 deletions(-) create mode 100644 frontend/libs/shared/locale/README.md create mode 100644 frontend/libs/shared/locale/eslint.config.mjs create mode 100644 frontend/libs/shared/locale/jest.config.ts create mode 100644 frontend/libs/shared/locale/project.json create mode 100644 frontend/libs/shared/locale/src/index.ts create mode 100644 frontend/libs/shared/locale/src/lib/common.namespace.ts create mode 100644 frontend/libs/shared/locale/src/lib/common.sl.ts create mode 100644 frontend/libs/shared/locale/src/lib/common.t.ts create mode 100644 frontend/libs/shared/locale/src/lib/locale-shell.component.ts create mode 100644 frontend/libs/shared/locale/src/lib/locale.routes.ts create mode 100644 frontend/libs/shared/locale/src/test-setup.ts create mode 100644 frontend/libs/shared/locale/tsconfig.json create mode 100644 frontend/libs/shared/locale/tsconfig.lib.json create mode 100644 frontend/libs/shared/locale/tsconfig.spec.json create mode 100644 frontend/libs/web/containers/src/lib/translations/containers-sl.translation.ts create mode 100644 frontend/libs/web/containers/src/lib/translations/containers.namespace.ts create mode 100644 frontend/libs/web/containers/src/lib/translations/containers.t.ts create mode 100644 frontend/libs/web/containers/src/lib/translations/translation.directive.ts create mode 100644 frontend/libs/web/containers/src/lib/translations/translation.pipe.ts create mode 100644 frontend/public/env.js diff --git a/frontend/libs/shared/locale/README.md b/frontend/libs/shared/locale/README.md new file mode 100644 index 0000000..556624c --- /dev/null +++ b/frontend/libs/shared/locale/README.md @@ -0,0 +1,7 @@ +# locale + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test locale` to execute the unit tests. diff --git a/frontend/libs/shared/locale/eslint.config.mjs b/frontend/libs/shared/locale/eslint.config.mjs new file mode 100644 index 0000000..e920498 --- /dev/null +++ b/frontend/libs/shared/locale/eslint.config.mjs @@ -0,0 +1,34 @@ +import nx from '@nx/eslint-plugin'; +import baseConfig from '../../../eslint.base.config.mjs'; + +export default [ + ...baseConfig, + ...nx.configs['flat/angular'], + ...nx.configs['flat/angular-template'], + { + files: ['**/*.ts'], + rules: { + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: 'lib', + style: 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: 'lib', + style: 'kebab-case', + }, + ], + }, + }, + { + files: ['**/*.html'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/frontend/libs/shared/locale/jest.config.ts b/frontend/libs/shared/locale/jest.config.ts new file mode 100644 index 0000000..8d9840f --- /dev/null +++ b/frontend/libs/shared/locale/jest.config.ts @@ -0,0 +1,21 @@ +export default { + displayName: 'locale', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/libs/shared/locale', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/frontend/libs/shared/locale/project.json b/frontend/libs/shared/locale/project.json new file mode 100644 index 0000000..ecfe09e --- /dev/null +++ b/frontend/libs/shared/locale/project.json @@ -0,0 +1,20 @@ +{ + "name": "locale", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/locale/src", + "prefix": "lib", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/locale/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + } +} diff --git a/frontend/libs/shared/locale/src/index.ts b/frontend/libs/shared/locale/src/index.ts new file mode 100644 index 0000000..e72098f --- /dev/null +++ b/frontend/libs/shared/locale/src/index.ts @@ -0,0 +1,4 @@ +export * from './lib/common.namespace'; +export * from './lib/common.t'; +export * from './lib/locale-shell.component'; +export * from './lib/locale.routes'; diff --git a/frontend/libs/shared/locale/src/lib/common.namespace.ts b/frontend/libs/shared/locale/src/lib/common.namespace.ts new file mode 100644 index 0000000..116c5db --- /dev/null +++ b/frontend/libs/shared/locale/src/lib/common.namespace.ts @@ -0,0 +1,17 @@ +import { createNamespace as createCoreNamespace } from '@mmstack/translate'; + +const ns = createCoreNamespace('common', { + yes: 'Yes', + no: 'No', + actions: { + start: 'Start', + stop: 'Stop', + }, +}); + +export const createNamespace = ns.createMergedNamespace; +export const createTranslation = ns.createTranslation; +export default ns.translation; + +export type CommonLocale = typeof ns.translation; + diff --git a/frontend/libs/shared/locale/src/lib/common.sl.ts b/frontend/libs/shared/locale/src/lib/common.sl.ts new file mode 100644 index 0000000..5c34d29 --- /dev/null +++ b/frontend/libs/shared/locale/src/lib/common.sl.ts @@ -0,0 +1,10 @@ +import { createTranslation } from './common.namespace'; + +export default createTranslation('sl', { + yes: 'Da', + no: 'Ne', + actions: { + start: 'Začni', + stop: 'Ustavi', + }, +}); diff --git a/frontend/libs/shared/locale/src/lib/common.t.ts b/frontend/libs/shared/locale/src/lib/common.t.ts new file mode 100644 index 0000000..788673c --- /dev/null +++ b/frontend/libs/shared/locale/src/lib/common.t.ts @@ -0,0 +1,12 @@ +import { registerNamespace } from '@mmstack/translate'; + +const r = registerNamespace( + () => import('./common.namespace').then((m) => m.default), + { + sl: () => import('./common.sl').then((m) => m.default), + } +); + +export const injectCommonT = r.injectNamespaceT; +export const resolveCommonTranslations = r.resolveNamespaceTranslation; + diff --git a/frontend/libs/shared/locale/src/lib/locale-shell.component.ts b/frontend/libs/shared/locale/src/lib/locale-shell.component.ts new file mode 100644 index 0000000..e3ea9c6 --- /dev/null +++ b/frontend/libs/shared/locale/src/lib/locale-shell.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'frontend-locale-shell', + imports: [RouterOutlet], + template: ``, +}) +export class LocaleShellComponent {} + diff --git a/frontend/libs/shared/locale/src/lib/locale.routes.ts b/frontend/libs/shared/locale/src/lib/locale.routes.ts new file mode 100644 index 0000000..146b900 --- /dev/null +++ b/frontend/libs/shared/locale/src/lib/locale.routes.ts @@ -0,0 +1,27 @@ +import { Route } from '@angular/router'; +import { resolveContainersTranslations } from '@frontend/web/containers'; + +export const LOCALE_ROUTE: Route[] = [ + { + path: 'containers', + loadComponent: () => + import('@frontend/web/containers').then( + (m) => m.ContainersComponentShell + ), + resolve: { + resolveContainersTranslations, + }, + }, + { + path: 'container', + loadComponent: () => + import('@frontend/web/container').then( + (m) => m.ContainerDetailsComponent + ), + }, + { + path: '**', + redirectTo: 'containers', + }, +]; + diff --git a/frontend/libs/shared/locale/src/test-setup.ts b/frontend/libs/shared/locale/src/test-setup.ts new file mode 100644 index 0000000..bc3333b --- /dev/null +++ b/frontend/libs/shared/locale/src/test-setup.ts @@ -0,0 +1,6 @@ +import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; + +setupZoneTestEnv({ + errorOnUnknownElements: true, + errorOnUnknownProperties: true, +}); diff --git a/frontend/libs/shared/locale/tsconfig.json b/frontend/libs/shared/locale/tsconfig.json new file mode 100644 index 0000000..f460bb2 --- /dev/null +++ b/frontend/libs/shared/locale/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "es2022", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/frontend/libs/shared/locale/tsconfig.lib.json b/frontend/libs/shared/locale/tsconfig.lib.json new file mode 100644 index 0000000..a28f762 --- /dev/null +++ b/frontend/libs/shared/locale/tsconfig.lib.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": [ + "src/**/*.spec.ts", + "src/test-setup.ts", + "jest.config.ts", + "src/**/*.test.ts" + ], + "include": ["src/**/*.ts"] +} diff --git a/frontend/libs/shared/locale/tsconfig.spec.json b/frontend/libs/shared/locale/tsconfig.spec.json new file mode 100644 index 0000000..fb72535 --- /dev/null +++ b/frontend/libs/shared/locale/tsconfig.spec.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "target": "es2016", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/frontend/libs/shared/navbar/src/lib/navbar.component.ts b/frontend/libs/shared/navbar/src/lib/navbar.component.ts index de91642..728af98 100644 --- a/frontend/libs/shared/navbar/src/lib/navbar.component.ts +++ b/frontend/libs/shared/navbar/src/lib/navbar.component.ts @@ -10,7 +10,7 @@ import { ThemeToggleService } from '@frontend/shared/theme-toggle'; imports: [MatToolbarModule, MatIcon, MatButtonModule], template: ` -
+
view_carousel Docker Containers
@@ -26,6 +26,12 @@ import { ThemeToggleService } from '@frontend/shared/theme-toggle'; flex: 1 1 auto; } + .title { + display: flex; + align-items: center; + cursor: pointer; + } + .navbar { background-color: var(--mat-accent-500); color: var(--mat-accent-contrast-500); diff --git a/frontend/libs/web/containers/src/index.ts b/frontend/libs/web/containers/src/index.ts index 3c15c90..59d474f 100644 --- a/frontend/libs/web/containers/src/index.ts +++ b/frontend/libs/web/containers/src/index.ts @@ -1 +1,2 @@ export * from './lib/containers-shell.component'; +export * from './lib/translations/containers.t'; diff --git a/frontend/libs/web/containers/src/lib/containers.component.ts b/frontend/libs/web/containers/src/lib/containers.component.ts index 585bdfe..a2932ac 100644 --- a/frontend/libs/web/containers/src/lib/containers.component.ts +++ b/frontend/libs/web/containers/src/lib/containers.component.ts @@ -2,12 +2,18 @@ import { Component, inject, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { WsService } from '@frontend/shared/ws'; import { ContainerCardComponent } from './container-card.component'; +import { ContainersTranslatePipe } from './translations/translation.pipe'; @Component({ selector: 'frontend-containers', standalone: true, - imports: [ContainerCardComponent], + imports: [ContainerCardComponent, ContainersTranslatePipe], template: ` +

+ {{ + 'containers.containersFound' | translate: { count: containers().length } + }} +

@for (container of containers(); track container.id) { import('./containers.namespace').then((m) => m.default), + { + sl: () => import('./containers-sl.translation').then((m) => m.default), + } +); + +export const injectContainersT = r.injectNamespaceT; +export const resolveContainersTranslations = r.resolveNamespaceTranslation; + diff --git a/frontend/libs/web/containers/src/lib/translations/translation.directive.ts b/frontend/libs/web/containers/src/lib/translations/translation.directive.ts new file mode 100644 index 0000000..d284f5e --- /dev/null +++ b/frontend/libs/web/containers/src/lib/translations/translation.directive.ts @@ -0,0 +1,11 @@ +import { Directive } from '@angular/core'; +import { BaseTranslateDirective } from '@mmstack/translate'; +import { type ContainersLocale } from './containers.namespace'; + +@Directive({ + selector: '[translate]', +}) +export class ContainersTranslateDirective< + TInput extends string, +> extends BaseTranslateDirective {} + diff --git a/frontend/libs/web/containers/src/lib/translations/translation.pipe.ts b/frontend/libs/web/containers/src/lib/translations/translation.pipe.ts new file mode 100644 index 0000000..3e8c78f --- /dev/null +++ b/frontend/libs/web/containers/src/lib/translations/translation.pipe.ts @@ -0,0 +1,9 @@ +import { Pipe } from '@angular/core'; +import { BaseTranslatePipe } from '@mmstack/translate'; +import { type ContainersLocale } from './containers.namespace'; + +@Pipe({ + name: 'translate', +}) +export class ContainersTranslatePipe extends BaseTranslatePipe {} + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 10a6d66..0517484 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,10 +18,12 @@ "@angular/platform-browser": "20.0.2", "@angular/platform-browser-dynamic": "20.0.2", "@angular/router": "20.0.2", + "@formatjs/intl": "^3.1.6", "@mmstack/form-material": "19.2.2", "@mmstack/primitives": "19.2.3", "@mmstack/resource": "19.2.0", "@mmstack/router-core": "^19.3.0", + "@mmstack/translate": "^19.2.8", "rxjs": "7.8.2", "zone.js": "0.15.1" }, @@ -5612,6 +5614,78 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-3.1.6.tgz", + "integrity": "sha512-tDkXnA4qpIFcDWac8CyVJq6oW8DR7W44QDUBsfXWIIJD/FYYen0QoH46W7XsVMFfPOVKkvbufjboZrrWbEfmww==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "intl-messageformat": "10.7.16", + "tslib": "^2.8.0" + }, + "peerDependencies": { + "typescript": "^5.6.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -7043,6 +7117,19 @@ "rxjs": "~7.8.2" } }, + "node_modules/@mmstack/translate": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@mmstack/translate/-/translate-19.2.8.tgz", + "integrity": "sha512-YsKjX6kLqSXyjKywdZKZDfTtcpvYWYTJzyfUJM66tprAcGoEI3Nsc97bh1k7lXKkbCRqgwuiEVa2gBjWjcF7/Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": "~19.2.3", + "@formatjs/intl": "~3.1.6" + } + }, "node_modules/@modern-js/node-bundle-require": { "version": "2.67.6", "resolved": "https://registry.npmjs.org/@modern-js/node-bundle-require/-/node-bundle-require-2.67.6.tgz", @@ -14215,7 +14302,6 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "dev": true, "license": "MIT" }, "node_modules/dedent": { @@ -17117,6 +17203,18 @@ "dev": true, "license": "ISC" }, + "node_modules/intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 847e32f..6058b5f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,10 +18,12 @@ "@angular/platform-browser": "20.0.2", "@angular/platform-browser-dynamic": "20.0.2", "@angular/router": "20.0.2", + "@formatjs/intl": "^3.1.6", "@mmstack/form-material": "19.2.2", "@mmstack/primitives": "19.2.3", "@mmstack/resource": "19.2.0", "@mmstack/router-core": "^19.3.0", + "@mmstack/translate": "^19.2.8", "rxjs": "7.8.2", "zone.js": "0.15.1" }, diff --git a/frontend/public/env.js b/frontend/public/env.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 8fee80a..b41e3ad 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -3,6 +3,7 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter, withPreloading } from '@angular/router'; import { ENVIRONMENT } from '@frontend/shared/environment'; import { PreloadStrategy } from '@mmstack/router-core'; +import { provideIntlConfig } from '@mmstack/translate'; import { environment } from '../environments/environment'; import { appRoutes } from './app.routes'; @@ -11,6 +12,9 @@ export const appConfig: ApplicationConfig = { provideZoneChangeDetection({ eventCoalescing: true }), provideHttpClient(), provideRouter(appRoutes, withPreloading(PreloadStrategy)), + provideIntlConfig({ + defaultLocale: 'en', + }), { provide: ENVIRONMENT, useValue: environment }, ], }; diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 17b4f87..04b0346 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -1,22 +1,52 @@ -import { Route } from '@angular/router'; +import { ActivatedRouteSnapshot, Route } from '@angular/router'; + +import { inject, Injectable, LOCALE_ID } from '@angular/core'; +import { + LocaleShellComponent, + resolveCommonTranslations, +} from '@frontend/shared/locale'; + +@Injectable({ + providedIn: 'root', +}) +export class LocaleStore { + locale = 'en'; +} export const appRoutes: Route[] = [ { - path: 'containers', - loadComponent: () => - import('@frontend/web/containers').then( - (m) => m.ContainersComponentShell - ), + path: ':locale', + component: LocaleShellComponent, + resolve: { + localeId: (route: ActivatedRouteSnapshot) => { + const store = inject(LocaleStore); + const locale = route.params['locale'] || 'en'; + + store.locale = locale; + + return locale; + }, + common: resolveCommonTranslations, + }, + providers: [ + { + provide: LOCALE_ID, + useFactory: (store: LocaleStore) => { + return store.locale; + }, + deps: [LocaleStore], + }, + ], + loadChildren: () => + import('@frontend/shared/locale').then((m) => m.LOCALE_ROUTE), }, { - path: 'container', - loadComponent: () => - import('@frontend/web/container').then( - (m) => m.ContainerDetailsComponent - ), + path: '', + redirectTo: 'en', + pathMatch: 'full', }, { path: '**', - redirectTo: 'containers', + redirectTo: 'en', }, ]; diff --git a/frontend/tsconfig.base.json b/frontend/tsconfig.base.json index 530e19d..84acd37 100644 --- a/frontend/tsconfig.base.json +++ b/frontend/tsconfig.base.json @@ -16,6 +16,7 @@ "baseUrl": ".", "paths": { "@frontend/shared/environment": ["libs/shared/environment/src/index.ts"], + "@frontend/shared/locale": ["libs/shared/locale/src/index.ts"], "@frontend/shared/navbar": ["libs/shared/navbar/src/index.ts"], "@frontend/shared/theme-toggle": [ "libs/shared/theme-toggle/src/index.ts"