Browse Source

Merge branch 'main' into next

next 3.0.0-beta.0
Thomas Kaul 4 days ago
parent
commit
226eeed156
  1. 7
      CHANGELOG.md
  2. 39
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  3. 8
      apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts
  4. 180
      apps/client/src/locales/messages.es.xlf
  5. 4
      libs/common/eslint.config.cjs
  6. 26
      libs/common/src/lib/chart-helper.ts
  7. 2
      libs/common/src/lib/class-transformer.ts
  8. 22
      libs/common/src/lib/helper.ts
  9. 2
      libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts
  10. 2
      libs/common/src/lib/routes/interfaces/internal-route.interface.ts
  11. 4
      libs/common/src/lib/utils/form.util.ts
  12. 2
      libs/common/src/lib/validators/is-currency-code.ts
  13. 3
      libs/common/tsconfig.json
  14. 2
      libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts
  15. 4013
      package-lock.json
  16. 68
      package.json

7
CHANGELOG.md

@ -16,12 +16,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Breaking Change**: The `sslmode=prefer` parameter in `DATABASE_URL` is no longer supported. Please update your environment variables (see `.env`) to use `sslmode=require` if _SSL_ is enabled or remove the `sslmode` parameter entirely if _SSL_ is not used.
## Unreleased
## 2.234.0 - 2026-01-30
### Changed
- Improved the usability of the create asset profile dialog in the market data section of the admin control panel
- Improved the language localization for Chinese (`zh`)
- Improved the language localization for German (`de`)
- Improved the language localization for Spanish (`es`)
- Upgraded `angular` from version `21.0.6` to `21.1.1`
- Upgraded `lodash` from version `4.17.21` to `4.17.23`
- Upgraded `Nx` from version `22.3.3` to `22.4.1`
- Upgraded `prettier` from version `3.8.0` to `3.8.1`
## 2.233.0 - 2026-01-23

39
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts

@ -63,7 +63,7 @@ import {
import { DeviceDetectorService } from 'ngx-device-detector';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs';
import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { AdminMarketDataService } from './admin-market-data.service';
import { GfAssetProfileDialogComponent } from './asset-profile-dialog/asset-profile-dialog.component';
@ -482,32 +482,27 @@ export class GfAdminMarketDataComponent
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ dataSource, symbol } = {}) => {
if (dataSource && symbol) {
this.adminService
.addAssetProfile({ dataSource, symbol })
.pipe(
switchMap(() => {
this.isLoading = true;
this.changeDetectorRef.markForCheck();
.subscribe((result) => {
if (!result) {
this.router.navigate(['.'], { relativeTo: this.route });
return this.adminService.fetchAdminMarketData({
filters: this.activeFilters,
take: this.pageSize
});
}),
takeUntil(this.unsubscribeSubject)
)
.subscribe(({ marketData }) => {
this.dataSource = new MatTableDataSource(marketData);
this.dataSource.sort = this.sort;
this.isLoading = false;
return;
}
this.changeDetectorRef.markForCheck();
const { addAssetProfile, dataSource, symbol } = result;
if (addAssetProfile && dataSource && symbol) {
this.adminService
.addAssetProfile({ dataSource, symbol })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.loadData();
});
} else {
this.loadData();
}
this.router.navigate(['.'], { relativeTo: this.route });
this.onOpenAssetProfileDialog({ dataSource, symbol });
});
});
}

8
apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts

@ -101,6 +101,7 @@ export class GfCreateAssetProfileDialogComponent implements OnDestroy, OnInit {
public onSubmit() {
if (this.mode === 'auto') {
this.dialogRef.close({
addAssetProfile: true,
dataSource:
this.createAssetProfileForm.get('searchSymbol').value.dataSource,
symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol
@ -127,10 +128,15 @@ export class GfCreateAssetProfileDialogComponent implements OnDestroy, OnInit {
takeUntil(this.unsubscribeSubject)
)
.subscribe(() => {
this.dialogRef.close();
this.dialogRef.close({
addAssetProfile: false,
dataSource: this.dataSourceForExchangeRates,
symbol: `${DEFAULT_CURRENCY}${currency}`
});
});
} else if (this.mode === 'manual') {
this.dialogRef.close({
addAssetProfile: true,
dataSource: 'MANUAL',
symbol: `${this.ghostfolioPrefix}${this.createAssetProfileForm.get('addSymbol').value}`
});

180
apps/client/src/locales/messages.es.xlf

@ -40,7 +40,7 @@
</trans-unit>
<trans-unit id="9153520284278555926" datatype="html">
<source>please</source>
<target state="new">please</target>
<target state="translated">por favor</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">333</context>
@ -84,7 +84,7 @@
</trans-unit>
<trans-unit id="1351814922314683865" datatype="html">
<source>with</source>
<target state="new">with</target>
<target state="translated">con</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">87</context>
@ -368,7 +368,7 @@
</trans-unit>
<trans-unit id="5611965261696422586" datatype="html">
<source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<target state="translated">y es impulsado por los esfuerzos de sus <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contribuidores<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">49</context>
@ -652,7 +652,7 @@
</trans-unit>
<trans-unit id="2395205455607568422" datatype="html">
<source>No auto-renewal on membership.</source>
<target state="new">No auto-renewal on membership.</target>
<target state="translated">No se renueva automáticamente la membresía.</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context>
<context context-type="linenumber">74</context>
@ -1096,7 +1096,7 @@
</trans-unit>
<trans-unit id="366169681580494481" datatype="html">
<source>Performance with currency effect</source>
<target state="new">Performance with currency effect</target>
<target state="translated">Rendimiento con el efecto del tipo de cambio de divisa</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">135</context>
@ -1912,7 +1912,7 @@
</trans-unit>
<trans-unit id="6004588582437169024" datatype="html">
<source>Current week</source>
<target state="new">Current week</target>
<target state="translated">Semana actual</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">191</context>
@ -2076,7 +2076,7 @@
</trans-unit>
<trans-unit id="7500665368930738879" datatype="html">
<source>or start a discussion at</source>
<target state="new">or start a discussion at</target>
<target state="translated">o iniciar una discusión en</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">94</context>
@ -2148,7 +2148,7 @@
</trans-unit>
<trans-unit id="2003818202621229370" datatype="html">
<source>Sustainable retirement income</source>
<target state="new">Sustainable retirement income</target>
<target state="translated">Ingreso sostenible de retiro</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">41</context>
@ -2320,7 +2320,7 @@
</trans-unit>
<trans-unit id="1531212547408073567" datatype="html">
<source>contact us</source>
<target state="new">contact us</target>
<target state="translated">contactarnos</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">336</context>
@ -2420,7 +2420,7 @@
</trans-unit>
<trans-unit id="7934616470747135563" datatype="html">
<source>Latest activities</source>
<target state="new">Latest activities</target>
<target state="translated">Últimas actividades</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
<context context-type="linenumber">211</context>
@ -2536,7 +2536,7 @@
</trans-unit>
<trans-unit id="5211792611718918888" datatype="html">
<source>annual interest rate</source>
<target state="new">annual interest rate</target>
<target state="translated">tasa de interés anual</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">185</context>
@ -2656,7 +2656,7 @@
</trans-unit>
<trans-unit id="7341990227686441824" datatype="html">
<source>Could not validate form</source>
<target state="new">Could not validate form</target>
<target state="translated">No se pudo validar el formulario</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">554</context>
@ -2892,7 +2892,7 @@
</trans-unit>
<trans-unit id="8966698274727122602" datatype="html">
<source>Authentication</source>
<target state="new">Authentication</target>
<target state="translated">Autenticación</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">35</context>
@ -3044,7 +3044,7 @@
</trans-unit>
<trans-unit id="3227075298129844075" datatype="html">
<source>If you retire today, you would be able to withdraw</source>
<target state="new">If you retire today, you would be able to withdraw</target>
<target state="translated">Si te retirases hoy, podrías sacar</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">68</context>
@ -3112,7 +3112,7 @@
</trans-unit>
<trans-unit id="7763941937414903315" datatype="html">
<source>Looking for a student discount?</source>
<target state="new">Looking for a student discount?</target>
<target state="translated">¿Buscando un descuento para estudiantes?</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">342</context>
@ -3348,7 +3348,7 @@
</trans-unit>
<trans-unit id="936060984157466006" datatype="html">
<source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source>
<target state="new">Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</target>
<target state="translated">Todo en <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Básico<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, s</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">199</context>
@ -3608,7 +3608,7 @@
</trans-unit>
<trans-unit id="7498591289549626867" datatype="html">
<source>Could not save asset profile</source>
<target state="new">Could not save asset profile</target>
<target state="translated">No se pudo guardar el perfil del activo</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">588</context>
@ -3812,7 +3812,7 @@
</trans-unit>
<trans-unit id="7702646444963497962" datatype="html">
<source>By</source>
<target state="new">By</target>
<target state="translated">Por</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">139</context>
@ -3828,7 +3828,7 @@
</trans-unit>
<trans-unit id="4340477809050781416" datatype="html">
<source>Current year</source>
<target state="new">Current year</target>
<target state="translated">Año actual</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">199</context>
@ -3864,7 +3864,7 @@
</trans-unit>
<trans-unit id="8319378030525016917" datatype="html">
<source>Asset profile has been saved</source>
<target state="new">Asset profile has been saved</target>
<target state="translated">El perfil del activo ha sido guardado</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">578</context>
@ -4056,7 +4056,7 @@
</trans-unit>
<trans-unit id="1468015720862673946" datatype="html">
<source>View Details</source>
<target state="new">View Details</target>
<target state="translated">Ver detalles</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">225</context>
@ -4192,7 +4192,7 @@
</trans-unit>
<trans-unit id="3556628518893194463" datatype="html">
<source>per week</source>
<target state="new">per week</target>
<target state="translated">por semana</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">130</context>
@ -4216,7 +4216,7 @@
</trans-unit>
<trans-unit id="577204259483334667" datatype="html">
<source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source>
<target state="new">and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</target>
<target state="translated">y compartimos agregados <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>métricas clave<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> del rendimiento de la plataforma</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">32</context>
@ -4260,7 +4260,7 @@
</trans-unit>
<trans-unit id="5271053765919315173" datatype="html">
<source>Website of Thomas Kaul</source>
<target state="new">Website of Thomas Kaul</target>
<target state="translated">Sitio web de Thomas Kaul</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">44</context>
@ -4440,7 +4440,7 @@
</trans-unit>
<trans-unit id="2145636458848553570" datatype="html">
<source>Sign in with OpenID Connect</source>
<target state="new">Sign in with OpenID Connect</target>
<target state="translated">Iniciar sesión con OpenID Connect</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context>
<context context-type="linenumber">55</context>
@ -4532,7 +4532,7 @@
</trans-unit>
<trans-unit id="5289957034780335504" datatype="html">
<source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<target state="translated">El código fuente está disponible completamente en <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>software de código abierto<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) bajo la <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>licencia AGPL-3.0<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">16</context>
@ -4604,7 +4604,7 @@
</trans-unit>
<trans-unit id="3004519800638083911" datatype="html">
<source>this is projected to increase to</source>
<target state="new">this is projected to increase to</target>
<target state="translated">esto se proyecta a aumentar a</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">147</context>
@ -4656,7 +4656,7 @@
</trans-unit>
<trans-unit id="3627006945295714424" datatype="html">
<source>Job ID</source>
<target state="new">Job ID</target>
<target state="translated">ID de trabajo</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context>
<context context-type="linenumber">34</context>
@ -4740,7 +4740,7 @@
</trans-unit>
<trans-unit id="8553460997100418147" datatype="html">
<source>for</source>
<target state="new">for</target>
<target state="translated">para</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">128</context>
@ -4764,7 +4764,7 @@
</trans-unit>
<trans-unit id="5134951682994822188" datatype="html">
<source>Could not parse scraper configuration</source>
<target state="new">Could not parse scraper configuration</target>
<target state="translated">No se pudo analizar la configuración del scraper</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">509</context>
@ -4808,7 +4808,7 @@
</trans-unit>
<trans-unit id="9187635907883145155" datatype="html">
<source>Edit access</source>
<target state="new">Edit access</target>
<target state="translated">Editar acceso</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context>
<context context-type="linenumber">11</context>
@ -4880,7 +4880,7 @@
</trans-unit>
<trans-unit id="8984201769958269296" datatype="html">
<source>Get access to 80’000+ tickers from over 50 exchanges</source>
<target state="new">Get access to 80’000+ tickers from over 50 exchanges</target>
<target state="translated">Obtén acceso a más de 80,000 tickers de más de 50 exchanges</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">84</context>
@ -5064,7 +5064,7 @@
</trans-unit>
<trans-unit id="8014012170270529279" datatype="html">
<source>less than</source>
<target state="new">less than</target>
<target state="translated">menos que</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">129</context>
@ -5354,7 +5354,7 @@
</trans-unit>
<trans-unit id="4257439615478050183" datatype="html">
<source>Ghostfolio Status</source>
<target state="new">Ghostfolio Status</target>
<target state="translated">Estado de Ghostfolio</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">62</context>
@ -5362,7 +5362,7 @@
</trans-unit>
<trans-unit id="4275978599610634089" datatype="html">
<source>with your university e-mail address</source>
<target state="new">with your university e-mail address</target>
<target state="translated">con tu dirección de correo electrónico de la universidad</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">348</context>
@ -5382,7 +5382,7 @@
</trans-unit>
<trans-unit id="70768492340592330" datatype="html">
<source>and a safe withdrawal rate (SWR) of</source>
<target state="new">and a safe withdrawal rate (SWR) of</target>
<target state="translated">y una tasa de retiro segura (SWR) de</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">108</context>
@ -5546,7 +5546,7 @@
</trans-unit>
<trans-unit id="5276907121760788823" datatype="html">
<source>Request it</source>
<target state="new">Request it</target>
<target state="translated">Solicitar</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">344</context>
@ -5602,7 +5602,7 @@
</trans-unit>
<trans-unit id="page.fire.projected.1" datatype="html">
<source>,</source>
<target state="new">,</target>
<target state="translated">,</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">145</context>
@ -5618,7 +5618,7 @@
</trans-unit>
<trans-unit id="4905798562247431262" datatype="html">
<source>per month</source>
<target state="new">per month</target>
<target state="translated">por mes</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">94</context>
@ -5866,7 +5866,7 @@
</trans-unit>
<trans-unit id="858192247408211331" datatype="html">
<source>here</source>
<target state="new">here</target>
<target state="translated">aquí</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">347</context>
@ -5874,7 +5874,7 @@
</trans-unit>
<trans-unit id="1600023202562292052" datatype="html">
<source>Close Holding</source>
<target state="new">Close Holding</target>
<target state="translated">Cerrar posición</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">442</context>
@ -6175,7 +6175,7 @@
</trans-unit>
<trans-unit id="7309206099560156141" datatype="html">
<source>{VAR_PLURAL, plural, =1 {activity} other {activities}}</source>
<target state="new">{VAR_PLURAL, plural, =1 {activity} other {activities}}</target>
<target state="translated">{VAR_PLURAL, plural, =1 {actividad} other {actividades}}</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context>
<context context-type="linenumber">14</context>
@ -6255,7 +6255,7 @@
</trans-unit>
<trans-unit id="5707368132268957392" datatype="html">
<source>Include in</source>
<target state="new">Include in</target>
<target state="translated">Incluir en</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">374</context>
@ -6539,7 +6539,7 @@
</trans-unit>
<trans-unit id="3477953895055172777" datatype="html">
<source>View Holding</source>
<target state="new">View Holding</target>
<target state="translated">Ver fondos</target>
<context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">450</context>
@ -6683,7 +6683,7 @@
</trans-unit>
<trans-unit id="4762855117875399861" datatype="html">
<source>Oops! Could not update access.</source>
<target state="new">Oops! Could not update access.</target>
<target state="translated">Oops! No se pudo actualizar el acceso.</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context>
<context context-type="linenumber">178</context>
@ -6691,7 +6691,7 @@
</trans-unit>
<trans-unit id="1355312194390410495" datatype="html">
<source>, based on your total assets of</source>
<target state="new">, based on your total assets of</target>
<target state="translated">, basado en tus activos totales de</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">96</context>
@ -6763,7 +6763,7 @@
</trans-unit>
<trans-unit id="7819314041543176992" datatype="html">
<source>Close</source>
<target state="translated">Cerca</target>
<target state="translated">Cerrar</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">594</context>
@ -6807,7 +6807,7 @@
</trans-unit>
<trans-unit id="4145496584631696119" datatype="html">
<source>Role</source>
<target state="new">Role</target>
<target state="translated">Rol</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">14</context>
@ -6839,7 +6839,7 @@
</trans-unit>
<trans-unit id="8375528527939577247" datatype="html">
<source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source>
<target state="new"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<target state="translated"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Cambiar con efecto de cambio dedivisa <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Cambiar <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">63</context>
@ -6847,7 +6847,7 @@
</trans-unit>
<trans-unit id="6602358241522477056" datatype="html">
<source>If you plan to open an account at</source>
<target state="new">If you plan to open an account at</target>
<target state="translated">Si planeas abrir una cuenta en</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">312</context>
@ -6855,7 +6855,7 @@
</trans-unit>
<trans-unit id="6608617124920241143" datatype="html">
<source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source>
<target state="new"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<target state="translated"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Rendimiento con cambio de divisa <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Rendimiento <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">83</context>
@ -6879,7 +6879,7 @@
</trans-unit>
<trans-unit id="6664504469290651320" datatype="html">
<source>send an e-mail to</source>
<target state="new">send an e-mail to</target>
<target state="translated">enviar un correo electrónico a</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">87</context>
@ -6951,7 +6951,7 @@
</trans-unit>
<trans-unit id="2878377610946588870" datatype="html">
<source>, assuming a</source>
<target state="new">, assuming a</target>
<target state="translated">, asumiendo un</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">174</context>
@ -6959,7 +6959,7 @@
</trans-unit>
<trans-unit id="7522916136412124285" datatype="html">
<source>to use our referral link and get a Ghostfolio Premium membership for one year</source>
<target state="new">to use our referral link and get a Ghostfolio Premium membership for one year</target>
<target state="translated">para usar nuestro enlace de referido y obtener una membresía Ghostfolio Premium por un año</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">340</context>
@ -7039,7 +7039,7 @@
</trans-unit>
<trans-unit id="3528767106831563012" datatype="html">
<source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source>
<target state="new">Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</target>
<target state="translated">Ghostfolio es una aplicación de gestión de patrimonio para aquellos individuos que desean realizar un seguimiento de acciones, ETFs o criptomonedas y tomar decisiones de inversión sólidas y basadas en datos.</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">10</context>
@ -7353,7 +7353,7 @@
</trans-unit>
<trans-unit id="1789421195684815451" datatype="html">
<source>Check the system status at</source>
<target state="new">Check the system status at</target>
<target state="translated">Verificar el estado del sistema en</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">57</context>
@ -7369,7 +7369,7 @@
</trans-unit>
<trans-unit id="7825231215382064101" datatype="html">
<source>Change with currency effect</source>
<target state="new">Change with currency effect</target>
<target state="translated">Cambiar con el efecto del tipo de cambio de divisa</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">116</context>
@ -7509,7 +7509,7 @@
</trans-unit>
<trans-unit id="1325095699053123251" datatype="html">
<source>The project has been initiated by</source>
<target state="new">The project has been initiated by</target>
<target state="translated">El proyecto ha sido iniciado por</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">40</context>
@ -7533,7 +7533,7 @@
</trans-unit>
<trans-unit id="5004550577313573215" datatype="html">
<source>Total amount</source>
<target state="new">Total amount</target>
<target state="translated">Cantidad total</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">95</context>
@ -7625,7 +7625,7 @@
</trans-unit>
<trans-unit id="6752851341939241310" datatype="html">
<source>Find account, holding or page...</source>
<target state="new">Find account, holding or page...</target>
<target state="translated">Buscar cuenta, posición o página...</target>
<context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context>
<context context-type="linenumber">151</context>
@ -8049,7 +8049,7 @@
</trans-unit>
<trans-unit id="7383756232563820625" datatype="html">
<source>Current month</source>
<target state="new">Current month</target>
<target state="translated">Mes actual</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">195</context>
@ -8234,7 +8234,7 @@
</trans-unit>
<trans-unit id="5199695670214400859" datatype="html">
<source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<target state="translated">Si encuentras un error, deseas sugerir una mejora o una nueva <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>característica<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, por favor únete a la comunidad Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, publica en <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">69</context>
@ -8266,7 +8266,7 @@
</trans-unit>
<trans-unit id="187187500641108332" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></source>
<target state="new"><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></target>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{ `Expira ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">39</context>
@ -8334,7 +8334,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRisk.category" datatype="html">
<source>Economic Market Cluster Risks</source>
<target state="new">Economic Market Cluster Risks</target>
<target state="translated">Riesgos del clúster de mercados económicos</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">106</context>
@ -8342,7 +8342,7 @@
</trans-unit>
<trans-unit id="rule.emergencyFund.category" datatype="html">
<source>Emergency Fund</source>
<target state="new">Emergency Fund</target>
<target state="translated">Fondo de emergencia</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">144</context>
@ -8350,7 +8350,7 @@
</trans-unit>
<trans-unit id="rule.fees.category" datatype="html">
<source>Fees</source>
<target state="new">Fees</target>
<target state="translated">Comisiones</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">161</context>
@ -8358,7 +8358,7 @@
</trans-unit>
<trans-unit id="rule.liquidity.category" datatype="html">
<source>Liquidity</source>
<target state="new">Liquidity</target>
<target state="translated">Liquidez</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">70</context>
@ -8366,7 +8366,7 @@
</trans-unit>
<trans-unit id="rule.liquidityBuyingPower" datatype="html">
<source>Buying Power</source>
<target state="new">Buying Power</target>
<target state="translated">Poder de compra</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">71</context>
@ -8374,7 +8374,7 @@
</trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html">
<source>Your buying power is below ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power is below ${thresholdMin} ${baseCurrency}</target>
<target state="translated">Tu poder de compra es inferior a ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">73</context>
@ -8382,7 +8382,7 @@
</trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html">
<source>Your buying power is 0 ${baseCurrency}</source>
<target state="new">Your buying power is 0 ${baseCurrency}</target>
<target state="translated">Tu poder de compra es 0 ${baseCurrency}</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">77</context>
@ -8390,7 +8390,7 @@
</trans-unit>
<trans-unit id="rule.liquidityBuyingPower.true" datatype="html">
<source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power exceeds ${thresholdMin} ${baseCurrency}</target>
<target state="translated">Tu poder de compra excede ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">80</context>
@ -8422,7 +8422,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.max" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</target>
<target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">112</context>
@ -8430,7 +8430,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.min" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</target>
<target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">117</context>
@ -8438,7 +8438,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.true" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">122</context>
@ -8454,7 +8454,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.max" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</target>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">130</context>
@ -8462,7 +8462,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.min" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</target>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">135</context>
@ -8470,7 +8470,7 @@
</trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.true" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">140</context>
@ -8494,7 +8494,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific" datatype="html">
<source>Asia-Pacific</source>
<target state="new">Asia-Pacific</target>
<target state="translated">Asia-Pacífico</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">165</context>
@ -8502,7 +8502,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.max" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target>
<target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">167</context>
@ -8510,7 +8510,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.min" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target>
<target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">171</context>
@ -8518,7 +8518,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.true" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">175</context>
@ -8526,7 +8526,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets" datatype="html">
<source>Emerging Markets</source>
<target state="new">Emerging Markets</target>
<target state="translated">Mercados emergentes</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">180</context>
@ -8534,7 +8534,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.max" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">183</context>
@ -8542,7 +8542,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.min" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">187</context>
@ -8550,7 +8550,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.true" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">191</context>
@ -8558,7 +8558,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html">
<source>Europe</source>
<target state="new">Europe</target>
<target state="translated">Europa</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">195</context>
@ -8566,7 +8566,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.false.max" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target>
<target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">197</context>
@ -8574,7 +8574,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.false.min" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target>
<target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">201</context>
@ -8582,7 +8582,7 @@
</trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.true" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">205</context>
@ -8694,7 +8694,7 @@
</trans-unit>
<trans-unit id="339860602695747533" datatype="html">
<source>Registration Date</source>
<target state="new">Registration Date</target>
<target state="translated">Fecha de registro</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">26</context>

4
libs/common/eslint.config.cjs

@ -18,7 +18,9 @@ module.exports = [
{
files: ['**/*.ts', '**/*.tsx'],
// Override or add rules here
rules: {}
rules: {
'@typescript-eslint/prefer-nullish-coalescing': 'error'
}
},
{
files: ['**/*.js', '**/*.jsx'],

26
libs/common/src/lib/chart-helper.ts

@ -1,4 +1,10 @@
import { Chart, TooltipPosition } from 'chart.js';
import type { ElementRef } from '@angular/core';
import type {
Chart,
ChartTypeRegistry,
Plugin,
TooltipPosition
} from 'chart.js';
import { format } from 'date-fns';
import {
@ -34,12 +40,12 @@ export function getTooltipOptions({
locale = getLocale(),
unit = ''
}: {
colorScheme?: ColorScheme;
colorScheme: ColorScheme;
currency?: string;
groupBy?: GroupBy;
locale?: string;
unit?: string;
} = {}) {
}) {
return {
backgroundColor: getBackgroundColor(colorScheme),
bodyColor: `rgb(${getTextColor(colorScheme)})`,
@ -47,7 +53,7 @@ export function getTooltipOptions({
borderColor: `rgba(${getTextColor(colorScheme)}, 0.1)`,
callbacks: {
label: (context) => {
let label = context.dataset.label || '';
let label = context.dataset.label ?? '';
if (label) {
label += ': ';
}
@ -98,10 +104,10 @@ export function getTooltipPositionerMapTop(
};
}
export function getVerticalHoverLinePlugin(
chartCanvas,
colorScheme?: ColorScheme
) {
export function getVerticalHoverLinePlugin<T extends keyof ChartTypeRegistry>(
chartCanvas: ElementRef,
colorScheme: ColorScheme
): Plugin<T> {
return {
afterDatasetsDraw: (chart, _, options) => {
const active = chart.getActiveElements();
@ -110,8 +116,8 @@ export function getVerticalHoverLinePlugin(
return;
}
const color = options.color || `rgb(${getTextColor(colorScheme)})`;
const width = options.width || 1;
const color = options.color ?? `rgb(${getTextColor(colorScheme)})`;
const width = options.width ?? 1;
const {
chartArea: { bottom, top }

2
libs/common/src/lib/class-transformer.ts

@ -16,7 +16,7 @@ export function transformToMapOfBig({
return mapOfBig;
}
export function transformToBig({ value }: { value: string }): Big {
export function transformToBig({ value }: { value: string }): Big | null {
if (value === null) {
return null;
}

22
libs/common/src/lib/helper.ts

@ -144,7 +144,7 @@ export function extractNumberFromString({
}: {
locale?: string;
value: string;
}): number {
}): number | undefined {
try {
// Remove non-numeric characters (excluding international formatting characters)
const numericValue = value.replace(/[^\d.,'’\s]/g, '');
@ -223,8 +223,8 @@ export function getDateFormatString(aLocale?: string) {
);
return formatObject
.map((object) => {
switch (object.type) {
.map(({ type, value }) => {
switch (type) {
case 'day':
return 'dd';
case 'month':
@ -232,7 +232,7 @@ export function getDateFormatString(aLocale?: string) {
case 'year':
return 'yyyy';
default:
return object.value;
return value;
}
})
.join('');
@ -271,9 +271,9 @@ export function getLowercase(object: object, path: string) {
export function getNumberFormatDecimal(aLocale?: string) {
const formatObject = new Intl.NumberFormat(aLocale).formatToParts(9999.99);
return formatObject.find((object) => {
return object.type === 'decimal';
}).value;
return formatObject.find(({ type }) => {
return type === 'decimal';
})?.value;
}
export function getNumberFormatGroup(aLocale = getLocale()) {
@ -281,9 +281,9 @@ export function getNumberFormatGroup(aLocale = getLocale()) {
useGrouping: true
}).formatToParts(9999.99);
return formatObject.find((object) => {
return object.type === 'group';
}).value;
return formatObject.find(({ type }) => {
return type === 'group';
})?.value;
}
export function getStartOfUtcDate(aDate: Date) {
@ -394,7 +394,7 @@ export function isRootCurrency(aCurrency: string) {
});
}
export function parseDate(date: string): Date {
export function parseDate(date: string): Date | undefined {
if (!date) {
return undefined;
}

2
libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts

@ -39,7 +39,7 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
})[];
markets: {
[key in Market]: Pick<
PortfolioDetails['markets'][key],
NonNullable<PortfolioDetails['markets']>[key],
'id' | 'valueInPercentage'
>;
};

2
libs/common/src/lib/routes/interfaces/internal-route.interface.ts

@ -2,7 +2,7 @@ import { User } from '@ghostfolio/common/interfaces';
export interface InternalRoute {
excludeFromAssistant?: boolean | ((aUser: User) => boolean);
path: string;
path?: string;
routerLink: string[];
subRoutes?: Record<string, InternalRoute>;
title: string;

4
libs/common/src/lib/utils/form.util.ts

@ -29,7 +29,7 @@ export async function validateObjectForForm<T>({
if (formControl) {
formControl.setErrors({
validationError: Object.values(constraints)[0]
validationError: Object.values(constraints ?? {})[0]
});
}
@ -37,7 +37,7 @@ export async function validateObjectForForm<T>({
if (formControlInCustomCurrency) {
formControlInCustomCurrency.setErrors({
validationError: Object.values(constraints)[0]
validationError: Object.values(constraints ?? {})[0]
});
}
}

2
libs/common/src/lib/validators/is-currency-code.ts

@ -9,7 +9,7 @@ import {
import { isISO4217CurrencyCode } from 'class-validator';
export function IsCurrencyCode(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
return function (object: object, propertyName: string) {
registerDecorator({
propertyName,
constraints: [],

3
libs/common/tsconfig.json

@ -12,6 +12,7 @@
],
"compilerOptions": {
"module": "preserve",
"lib": ["dom", "es2022"]
"lib": ["dom", "es2022"],
"strictNullChecks": true
}
}

2
libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts

@ -121,7 +121,7 @@ export class GfSymbolAutocompleteComponent
this.control.valueChanges
.pipe(
filter((query) => {
if (query.length === 0) {
if (query?.length === 0) {
this.showDefaultOptions();
return false;

4013
package-lock.json

File diff suppressed because it is too large

68
package.json

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.233.0",
"version": "2.234.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
@ -54,17 +54,17 @@
"workspace-generator": "nx workspace-generator"
},
"dependencies": {
"@angular/animations": "21.0.6",
"@angular/cdk": "21.0.5",
"@angular/common": "21.0.6",
"@angular/compiler": "21.0.6",
"@angular/core": "21.0.6",
"@angular/forms": "21.0.6",
"@angular/material": "21.0.5",
"@angular/platform-browser": "21.0.6",
"@angular/platform-browser-dynamic": "21.0.6",
"@angular/router": "21.0.6",
"@angular/service-worker": "21.0.6",
"@angular/animations": "21.1.1",
"@angular/cdk": "21.1.1",
"@angular/common": "21.1.1",
"@angular/compiler": "21.1.1",
"@angular/core": "21.1.1",
"@angular/forms": "21.1.1",
"@angular/material": "21.1.1",
"@angular/platform-browser": "21.1.1",
"@angular/platform-browser-dynamic": "21.1.1",
"@angular/router": "21.1.1",
"@angular/service-worker": "21.1.1",
"@codewithdan/observable-store": "2.2.15",
"@date-fns/utc": "2.1.1",
"@internationalized/number": "3.6.5",
@ -113,7 +113,7 @@
"http-status-codes": "2.3.0",
"ionicons": "8.0.13",
"jsonpath": "1.1.1",
"lodash": "4.17.21",
"lodash": "4.17.23",
"marked": "17.0.1",
"ms": "3.0.0-canary.1",
"ng-extract-i18n-merge": "3.2.1",
@ -137,32 +137,32 @@
"zone.js": "0.16.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "21.0.4",
"@angular-devkit/core": "21.0.4",
"@angular-devkit/schematics": "21.0.4",
"@angular-devkit/build-angular": "21.1.1",
"@angular-devkit/core": "21.1.1",
"@angular-devkit/schematics": "21.1.1",
"@angular-eslint/eslint-plugin": "21.1.0",
"@angular-eslint/eslint-plugin-template": "21.1.0",
"@angular-eslint/template-parser": "21.1.0",
"@angular/cli": "21.0.4",
"@angular/compiler-cli": "21.0.6",
"@angular/language-service": "21.0.6",
"@angular/localize": "21.0.6",
"@angular/pwa": "21.0.4",
"@angular/cli": "21.1.1",
"@angular/compiler-cli": "21.1.1",
"@angular/language-service": "21.1.1",
"@angular/localize": "21.1.1",
"@angular/pwa": "21.1.1",
"@eslint/eslintrc": "3.3.1",
"@eslint/js": "9.35.0",
"@nestjs/schematics": "11.0.9",
"@nestjs/testing": "11.1.8",
"@nx/angular": "22.3.3",
"@nx/eslint-plugin": "22.3.3",
"@nx/jest": "22.3.3",
"@nx/js": "22.3.3",
"@nx/module-federation": "22.3.3",
"@nx/nest": "22.3.3",
"@nx/node": "22.3.3",
"@nx/storybook": "22.3.3",
"@nx/web": "22.3.3",
"@nx/workspace": "22.3.3",
"@schematics/angular": "21.0.4",
"@nx/angular": "22.4.1",
"@nx/eslint-plugin": "22.4.1",
"@nx/jest": "22.4.1",
"@nx/js": "22.4.1",
"@nx/module-federation": "22.4.1",
"@nx/nest": "22.4.1",
"@nx/node": "22.4.1",
"@nx/storybook": "22.4.1",
"@nx/web": "22.4.1",
"@nx/workspace": "22.4.1",
"@schematics/angular": "21.1.1",
"@storybook/addon-docs": "10.1.10",
"@storybook/angular": "10.1.10",
"@trivago/prettier-plugin-sort-imports": "5.2.2",
@ -170,7 +170,7 @@
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "30.0.0",
"@types/jsonpath": "0.2.4",
"@types/lodash": "4.17.21",
"@types/lodash": "4.17.23",
"@types/node": "22.15.17",
"@types/papaparse": "5.3.7",
"@types/passport-google-oauth20": "2.0.16",
@ -185,7 +185,7 @@
"jest": "30.2.0",
"jest-environment-jsdom": "30.2.0",
"jest-preset-angular": "16.0.0",
"nx": "22.3.3",
"nx": "22.4.1",
"prettier": "3.8.1",
"prettier-plugin-organize-attributes": "1.0.0",
"prisma": "7.3.0",

Loading…
Cancel
Save