From 5b21429f361b2ad237c2e94c9e7908850f59db2a Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 6 May 2025 15:54:06 +0300 Subject: [PATCH 01/10] chore(i18n): ngx-translate lib installed and configured --- package.json | 2 ++ src/app/app.config.ts | 9 ++++++++- src/app/app.module.ts | 20 -------------------- src/app/core/helpers/i18n.helper.ts | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 21 deletions(-) delete mode 100644 src/app/app.module.ts create mode 100644 src/app/core/helpers/i18n.helper.ts diff --git a/package.json b/package.json index f753a31bd..33e2e94ca 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "@angular/platform-browser-dynamic": "^19.2.0", "@angular/router": "^19.2.0", "@angular/service-worker": "^19.2.0", + "@ngx-translate/core": "^16.0.4", + "@ngx-translate/http-loader": "^16.0.1", "@ngxs/devtools-plugin": "^19.0.0", "@ngxs/logger-plugin": "^19.0.0", "@ngxs/store": "^19.0.0", diff --git a/src/app/app.config.ts b/src/app/app.config.ts index db007ae47..138c2eae3 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,4 +1,8 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { + ApplicationConfig, + importProvidersFrom, + provideZoneChangeDetection, +} from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; import { provideStore } from '@ngxs/store'; @@ -10,6 +14,8 @@ import { provideHttpClient } from '@angular/common/http'; import { ConfirmationService } from 'primeng/api'; import { STATES } from '@core/constants/ngxs-states.constant'; import { provideServiceWorker } from '@angular/service-worker'; +import { provideTranslation } from '@core/helpers/i18n.helper'; +import { TranslateModule } from '@ngx-translate/core'; export const appConfig: ApplicationConfig = { providers: [ @@ -35,5 +41,6 @@ export const appConfig: ApplicationConfig = { registrationStrategy: 'registerWhenStable:30000', }), ConfirmationService, + importProvidersFrom(TranslateModule.forRoot(provideTranslation())), ], }; diff --git a/src/app/app.module.ts b/src/app/app.module.ts deleted file mode 100644 index 51cadfd08..000000000 --- a/src/app/app.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core'; -import { NgxsModule } from '@ngxs/store'; -import { AuthState } from '@core/store/auth'; -import { HomeState } from 'src/app/features/home/store'; -import { TokensState } from '@core/store/settings'; -import { AddonsState } from '@core/store/settings/addons'; -import { SearchState } from '@osf/features/search/store'; - -@NgModule({ - imports: [ - NgxsModule.forRoot([ - AuthState, - TokensState, - AddonsState, - HomeState, - SearchState, - ]), - ], -}) -export class AppModule {} diff --git a/src/app/core/helpers/i18n.helper.ts b/src/app/core/helpers/i18n.helper.ts new file mode 100644 index 000000000..0d25bede6 --- /dev/null +++ b/src/app/core/helpers/i18n.helper.ts @@ -0,0 +1,16 @@ +import { TranslateLoader, TranslateModuleConfig } from '@ngx-translate/core'; +import { HttpClient } from '@angular/common/http'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; + +function httpLoaderFactory(http: HttpClient): TranslateHttpLoader { + return new TranslateHttpLoader(http, './assets/i18n/', '.json'); +} + +export const provideTranslation = (): TranslateModuleConfig => ({ + defaultLanguage: 'en', + loader: { + provide: TranslateLoader, + useFactory: httpLoaderFactory, + deps: [HttpClient], + }, +}); From 125228bda287db87615bcfc746e8ceada5b2e293 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 6 May 2025 17:17:36 +0300 Subject: [PATCH 02/10] refactor(i18n): moving all strings to a translation file --- .../forgot-password.component.html | 12 +- .../forgot-password.component.ts | 7 +- .../reset-password.component.html | 24 +- .../reset-password.component.ts | 2 + .../auth/sign-up/sign-up.component.html | 54 ++-- .../auth/sign-up/sign-up.component.ts | 2 + src/app/features/home/home.component.html | 45 +-- src/app/features/home/home.component.ts | 9 +- src/app/features/home/logged-out/data.ts | 29 +- .../logged-out/home-logged-out.component.html | 153 ++++++---- .../logged-out/home-logged-out.component.scss | 8 +- .../logged-out/home-logged-out.component.ts | 10 +- .../my-projects/my-projects.component.html | 25 +- .../my-projects/my-projects.component.ts | 10 +- .../developer-app-details.component.html | 60 ++-- .../developer-app-details.component.ts | 8 + .../developer-apps-container.component.ts | 8 +- .../developer-apps-list.component.html | 3 +- .../developer-apps-list.component.ts | 3 +- .../token-add-edit-form.component.html | 16 +- .../token-add-edit-form.component.ts | 11 +- .../token-created-dialog.component.html | 19 +- .../token-created-dialog.component.ts | 11 +- .../token-details.component.html | 7 +- .../token-details/token-details.component.ts | 10 +- .../tokens-list/tokens-list.component.html | 5 +- .../tokens-list/tokens-list.component.ts | 4 +- .../settings/tokens/tokens.component.html | 4 +- .../settings/tokens/tokens.component.ts | 4 +- .../add-project-form.component.html | 36 ++- .../add-project-form.component.ts | 2 + .../my-projects-table.component.html | 12 +- .../my-projects-table.component.ts | 15 +- .../password-input-hint.component.html | 3 +- .../password-input-hint.component.ts | 3 +- src/assets/i18n/en.json | 270 ++++++++++++++++++ src/assets/styles/styles.scss | 4 + 37 files changed, 685 insertions(+), 223 deletions(-) create mode 100644 src/assets/i18n/en.json diff --git a/src/app/features/auth/forgot-password/forgot-password.component.html b/src/app/features/auth/forgot-password/forgot-password.component.html index 83874af3f..352859cc8 100644 --- a/src/app/features/auth/forgot-password/forgot-password.component.html +++ b/src/app/features/auth/forgot-password/forgot-password.component.html @@ -1,23 +1,23 @@
-

Forgot Your Password?

-

Enter your email address and we'll send a link to reset your password

+

{{ "auth.forgot-password.title" | translate }}

+

{{ "auth.forgot-password.description" | translate }}

- +
@@ -27,7 +27,7 @@

Forgot Your Password?

} diff --git a/src/app/features/auth/forgot-password/forgot-password.component.ts b/src/app/features/auth/forgot-password/forgot-password.component.ts index 897144f09..32eb8ad59 100644 --- a/src/app/features/auth/forgot-password/forgot-password.component.ts +++ b/src/app/features/auth/forgot-password/forgot-password.component.ts @@ -7,11 +7,12 @@ import { Message } from 'primeng/message'; import { ForgotPasswordFormGroupType } from '@osf/features/auth/forgot-password/forgot-password-form-group.type'; import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; import { toSignal } from '@angular/core/rxjs-interop'; +import { TranslatePipe } from '@ngx-translate/core'; @Component({ selector: 'osf-forgot-password', standalone: true, - imports: [InputText, ReactiveFormsModule, Button, Message], + imports: [InputText, ReactiveFormsModule, Button, Message, TranslatePipe], templateUrl: './forgot-password.component.html', styleUrl: './forgot-password.component.scss', }) @@ -29,12 +30,12 @@ export class ForgotPasswordComponent { if (this.forgotPasswordForm.valid) { this.message.set({ severity: 'success', - content: 'Thanks. Check your email to reset your password.', + content: 'auth.forgot-password.messages.success', }); // this.message.set({ // severity: 'error', - // content: 'Email not found.', + // content: 'auth.forgot-password.messages.error' // }); } } diff --git a/src/app/features/auth/reset-password/reset-password.component.html b/src/app/features/auth/reset-password/reset-password.component.html index dab201213..14060bf7a 100644 --- a/src/app/features/auth/reset-password/reset-password.component.html +++ b/src/app/features/auth/reset-password/reset-password.component.html @@ -1,10 +1,12 @@ @if (!isFormSubmitted()) {
-

Reset Password

+

{{ "auth.reset-password.title" | translate }}

- + Reset Password
- + Reset Password "passwordMismatch" ] && resetPasswordForm.get("confirmNewPassword")?.touched ) { - Passwords must match + {{ + "auth.common.password.mismatch" | translate + }} }
} @else {
-

Thank You!

-

You have successfully reset your password

+

{{ "auth.reset-password.success.title" | translate }}

+

+ {{ "auth.reset-password.success.message" | translate }} +

diff --git a/src/app/features/auth/reset-password/reset-password.component.ts b/src/app/features/auth/reset-password/reset-password.component.ts index c62c53e2f..6be1d077b 100644 --- a/src/app/features/auth/reset-password/reset-password.component.ts +++ b/src/app/features/auth/reset-password/reset-password.component.ts @@ -11,6 +11,7 @@ import { PasswordInputHintComponent } from '@shared/components/password-input-hi import { ResetPasswordFormGroupType } from './reset-password-form-group.type'; import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; import { toSignal } from '@angular/core/rxjs-interop'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'osf-reset-password', @@ -21,6 +22,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; ReactiveFormsModule, RouterLink, PasswordInputHintComponent, + TranslateModule, ], templateUrl: './reset-password.component.html', styleUrl: './reset-password.component.scss', diff --git a/src/app/features/auth/sign-up/sign-up.component.html b/src/app/features/auth/sign-up/sign-up.component.html index 9c0883e56..d3e0b0f10 100644 --- a/src/app/features/auth/sign-up/sign-up.component.html +++ b/src/app/features/auth/sign-up/sign-up.component.html @@ -1,10 +1,13 @@ @if (!isFormSubmitted()) { } @else {
-

Registration successful

-

Check your email to confirm your account

+

{{ "auth.sign-up.success.title" | translate }}

+

{{ "auth.sign-up.success.message" | translate }}

} diff --git a/src/app/features/auth/sign-up/sign-up.component.ts b/src/app/features/auth/sign-up/sign-up.component.ts index f7cdc2cf0..bee910f84 100644 --- a/src/app/features/auth/sign-up/sign-up.component.ts +++ b/src/app/features/auth/sign-up/sign-up.component.ts @@ -16,6 +16,7 @@ import { Router, RouterLink } from '@angular/router'; import { PasswordInputHintComponent } from '@shared/components/password-input-hint/password-input-hint.component'; import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; import { toSignal } from '@angular/core/rxjs-interop'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'osf-sign-up', @@ -31,6 +32,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; NgOptimizedImage, RouterLink, PasswordInputHintComponent, + TranslateModule, ], templateUrl: './sign-up.component.html', styleUrl: './sign-up.component.scss', diff --git a/src/app/features/home/home.component.html b/src/app/features/home/home.component.html index bb496f3d0..21f9f28c8 100644 --- a/src/app/features/home/home.component.html +++ b/src/app/features/home/home.component.html @@ -5,16 +5,23 @@ >

- Go to My Projects to organize your work or - search OSF + {{ "home.logged-in.dashboard.quick-search.go-to" | translate }} + + {{ "home.logged-in.dashboard.quick-search.my-projects" | translate }} + + {{ "home.logged-in.dashboard.quick-search.to-organize" | translate }} + + {{ "home.logged-in.dashboard.quick-search.search" | translate }} + + {{ "home.logged-in.dashboard.quick-search.osf" | translate }}

-

Discover Public Projects

+

{{ "home.logged-in.public-projects.title" | translate }}

-

Browse the latest research

-

- Check out the latest preprints hosted on OSF covering a variety of - research areas -

+

{{ "home.logged-in.latest-research.title" | translate }}

+

{{ "home.logged-in.latest-research.subtitle" | translate }}

- +
-

Hosting a conference or meeting?

-

- Use the OSF for Meetings service to provide a central location for - conference submissions -

+

{{ "home.logged-in.hosting.title" | translate }}

+

{{ "home.logged-in.hosting.subtitle" | translate }}

- +
diff --git a/src/app/features/home/home.component.ts b/src/app/features/home/home.component.ts index fd210e573..75138fa60 100644 --- a/src/app/features/home/home.component.ts +++ b/src/app/features/home/home.component.ts @@ -30,11 +30,18 @@ import { MyProjectsItem } from '@osf/features/my-projects/entities/my-projects.e import { GetUserInstitutions } from '@osf/core/store/institutions'; import { DialogService } from 'primeng/dynamicdialog'; import { AddProjectFormComponent } from '@shared/components/add-project-form/add-project-form.component'; +import { TranslatePipe } from '@ngx-translate/core'; @Component({ selector: 'osf-home', standalone: true, - imports: [RouterLink, Button, SubHeaderComponent, MyProjectsTableComponent], + imports: [ + RouterLink, + Button, + SubHeaderComponent, + MyProjectsTableComponent, + TranslatePipe, + ], templateUrl: './home.component.html', styleUrl: './home.component.scss', providers: [DialogService], diff --git a/src/app/features/home/logged-out/data.ts b/src/app/features/home/logged-out/data.ts index b7a92ed6c..4ec78a683 100644 --- a/src/app/features/home/logged-out/data.ts +++ b/src/app/features/home/logged-out/data.ts @@ -1,33 +1,30 @@ export const slides = [ { img: 'assets/images/carousel1.png', - name: '2-st slide', - title: - 'OSF is a game changer for those wanting to effectively share their research process in the spirit of collaboration.', + name: 'Patricia Ayala', + title: 'home.logged-out.testimonials.slides.patricia.quote', author: 'Patricia Ayala', - facility: 'Research Services Librarian | University of Toronto', + facility: 'home.logged-out.testimonials.slides.patricia.facility', }, { img: 'assets/images/carousel2.png', - name: '1-st slide', - title: - 'OSF is indispensable in helping me create reproducible research pipelines from preregistration through data collection and analysis. Its versatility makes it my one-stop shop for projects. The Dropbox integration effortlessly transforms my existing local workflow to public repository.', + name: 'Maya Mathur', + title: 'home.logged-out.testimonials.slides.maya.quote', author: 'Maya Mathur', - facility: 'Department of Epidemiology Harvard University', + facility: 'home.logged-out.testimonials.slides.maya.facility', }, { img: 'assets/images/carousel3.png', - name: '1-st slide', - title: - 'Because SocArXiv is a not-for-profit organization, researchers can be assured that they are sharing their research in an environment where access, inclusivity, and preservation, rather than profit, will remain at the heart of the mission. A great benefit of partnering with OSF is that this application is a free public good.', + name: 'Philip Cohen', + title: 'home.logged-out.testimonials.slides.philip.quote', author: 'Philip Cohen', - facility: 'SocArXiv papers', + facility: 'home.logged-out.testimonials.slides.philip.facility', }, ]; export const integrationIcons = [ { - title: 'Authentication', + title: 'home.logged-out.integrations.categories.authentication', first: { path: 'assets/icons/socials/integrations/incommon.png', height: '36', @@ -40,7 +37,7 @@ export const integrationIcons = [ }, }, { - title: 'Discovery', + title: 'home.logged-out.integrations.categories.discovery', first: { path: 'assets/icons/socials/integrations/google-scholar.png', height: '36', @@ -53,7 +50,7 @@ export const integrationIcons = [ }, }, { - title: 'References', + title: 'home.logged-out.integrations.categories.references', first: { path: 'assets/icons/socials/integrations/mendeley.png', height: '42', @@ -66,7 +63,7 @@ export const integrationIcons = [ }, }, { - title: 'Storage', + title: 'home.logged-out.integrations.categories.storage', first: { path: 'assets/icons/socials/integrations/dropbox.png', height: '34', diff --git a/src/app/features/home/logged-out/home-logged-out.component.html b/src/app/features/home/logged-out/home-logged-out.component.html index d2ca38df9..89fe4f17d 100644 --- a/src/app/features/home/logged-out/home-logged-out.component.html +++ b/src/app/features/home/logged-out/home-logged-out.component.html @@ -6,14 +6,11 @@ >
-

There’s a better way to manage your research

-

- OSF is a free, open platform to support your research and enable - collaboration -

+

+

-

Discover Public Research

-

- Discover projects, data, materials, and collaborators on OSF that might - be helpful to your own research -

+

+

{{ "home.logged-out.discover.subtitle" | translate }}

-

How OSF Supports your research

+

search icon search icon
-

Search And Discover

+

+ {{ "home.logged-out.support.sections.search.title" | translate }} +

- Find papers, data, and materials to inspire your next research - project. Search public projects to build on the work of others and - find new collaborators + {{ + "home.logged-out.support.sections.search.description" + | translate + }}

@@ -82,24 +81,27 @@

search icon search icon
-

Design Your Study

+

+ {{ "home.logged-out.support.sections.design.title" | translate }} +

- Start a project and add collaborators, giving them access to - protocols and other research materials. Built-in version control - tracks the evolution of your study + {{ + "home.logged-out.support.sections.design.description" + | translate + }}

@@ -107,24 +109,27 @@

search icon search icon
-

Collect and Analyze Data

+

+ {{ "home.logged-out.support.sections.collect.title" | translate }} +

- Store data, code, and other materials in OSF Storage, or connect - your Dropbox or other third-party account. Every file gets a - unique, persistent URL for citing and sharing + {{ + "home.logged-out.support.sections.collect.description" + | translate + }}

@@ -132,18 +137,21 @@

search icon
-

Publish Your Reports

+

+ {{ "home.logged-out.support.sections.publish.title" | translate }} +

- Share papers in OSF Preprints or a community-based preprint - provider, so others can find and cite your work. Track impact with - metrics like downloads and view counts + {{ + "home.logged-out.support.sections.publish.description" + | translate + }}

@@ -160,12 +168,15 @@

- +
-

What others are saying

+