Merge branch 'master' into simon/angular-universal

* master: (42 commits)
  i18n: Added missing Bisq translations. Minor missing space and character fixes.
  add missing 'sat/vb' string for i18n
  i18n: Removed CR from "In X minutes" translation
  i18n: Updated strings for "x confirmations", "x transactions", "x blocks" and "block ETA". Peg-out, and bisq headers.
  Update translation strings from Transifex
  Enable 'ka' locale for Georgian
  Update translation strings from Transifex
  Disable Vietnamese locale 'vi' until translations are completed
  Update translation strings from Transifex
  add 'sat' string for i18n
  Update translation strings from Transifex
  Update translation strings from Transifex
  i18n update
  Remove extra garbage characters from OP_RETURN tooltip. fixes #254
  Fix for changing locale on other networks than mainnet. fixes #253
  i18n: Asset search box
  Update translation strings from Transifex
  i18n: Added X of X transaction. Flipped collapse/expand
  i18n: Added "miner identification" and updated "navigate to sponsor"
  Update translations from Transifex
  ...

# Conflicts:
#	frontend/src/app/components/app/app.component.ts
#	frontend/src/app/components/mempool-block/mempool-block.component.ts
This commit is contained in:
softsimon 2020-12-08 00:11:38 +07:00
commit f9e8dfb079
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
82 changed files with 31603 additions and 8412 deletions

View File

@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
[mempool.frontend-src-locale-messages-xlf--wiz-i18n]
[mempool.frontend-src-locale-messages-xlf--master]
file_filter = frontend/src/locale/messages.<lang>.xlf
source_lang = en-US
type = XLIFF

View File

@ -19,6 +19,10 @@
"baseHref":"/"
},
"locales": {
"ar": {
"translation": "src/locale/messages.ar.xlf",
"baseHref": "/ar/"
},
"cs": {
"translation": "src/locale/messages.cs.xlf",
"baseHref": "/cs/"
@ -43,13 +47,17 @@
"translation": "src/locale/messages.ja.xlf",
"baseHref": "/ja/"
},
"ka": {
"translation": "src/locale/messages.ka.xlf",
"baseHref": "/ka/"
},
"nl": {
"translation": "src/locale/messages.nl.xlf",
"baseHref": "/nl/"
},
"nn": {
"translation": "src/locale/messages.nn.xlf",
"baseHref": "/nn/"
"nb": {
"translation": "src/locale/messages.nb.xlf",
"baseHref": "/nb/"
},
"pt": {
"translation": "src/locale/messages.pt.xlf",
@ -71,6 +79,10 @@
"translation": "src/locale/messages.uk.xlf",
"baseHref": "/uk/"
},
"vi": {
"translation": "src/locale/messages.vi.xlf",
"baseHref": "/vi/"
},
"zh": {
"translation": "src/locale/messages.zh.xlf",
"baseHref": "/zh/"

View File

@ -23,7 +23,7 @@
"ng": "./node_modules/@angular/cli/bin/ng",
"tsc": "./node_modules/typescript/bin/tsc",
"i18n-extract-from-source": "./node_modules/@angular/cli/bin/ng xi18n --ivy --out-file ./src/locale/messages.xlf",
"i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1",
"i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1 --force",
"serve": "ng serve --proxy-config proxy.conf.json",
"start": "npm run generate-config && npm run sync-assets-dev && ng serve --proxy-config proxy.conf.json",
"build": "npm run generate-config && ng build --prod --localize && npm run sync-assets",

View File

@ -65,7 +65,7 @@ export interface Language {
}
export const languages: Language[] = [
// { code: 'ar', name: 'العربية' }, // Arabic
{ code: 'ar', name: 'العربية' }, // Arabic
// { code: 'bg', name: 'Български' }, // Bulgarian
// { code: 'bs', name: 'Bosanski' }, // Bosnian
// { code: 'ca', name: 'Català' }, // Catalan
@ -81,6 +81,7 @@ export const languages: Language[] = [
{ code: 'fa', name: 'فارسی' }, // Persian
{ code: 'fr', name: 'Français' }, // French
// { code: 'gl', name: 'Galego' }, // Galician
{ code: 'ka', name: 'ქართული' }, // Georgian
// { code: 'ko', name: '한국어' }, // Korean
// { code: 'hr', name: 'Hrvatski' }, // Croatian
// { code: 'id', name: 'Bahasa Indonesia' },// Indonesian
@ -94,8 +95,8 @@ export const languages: Language[] = [
// { code: 'ms', name: 'Bahasa Melayu' }, // Malay
{ code: 'nl', name: 'Nederlands' }, // Dutch
{ code: 'ja', name: '日本語' }, // Japanese
// { code: 'nb', name: 'Norsk bokmål' }, // Norwegian Bokmål
{ code: 'nn', name: 'Norsk' }, // Norwegian Nynorsk
{ code: 'nb', name: 'Norsk' }, // Norwegian Bokmål
// { code: 'nn', name: 'Norsk Nynorsk' }, // Norwegian Nynorsk
// { code: 'pl', name: 'Polski' }, // Polish
{ code: 'pt', name: 'Português' }, // Portuguese
// { code: 'pt-BR', name: 'Português (Brazil)' }, // Portuguese (Brazil)

View File

@ -43,11 +43,10 @@ import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faAngleDoubleDown, faAngleDoubleUp, faAngleDown, faAngleUp, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
import { faAngleDown, faAngleUp, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
faLink, faList, faSearch, faTachometerAlt, faThList, faTint, faTv } from '@fortawesome/free-solid-svg-icons';
import { ApiDocsComponent } from './components/api-docs/api-docs.component';
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
import { TranslationStringsComponent } from './components/translation-strings/translation-strings.component';
import { StorageService } from './services/storage.service';
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
@ -84,7 +83,6 @@ import { HttpCacheInterceptor } from './services/http-cache.interceptor';
DashboardComponent,
ApiDocsComponent,
TermsOfServiceComponent,
TranslationStringsComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),

View File

@ -1,14 +1,14 @@
<div class="container-xl">
<h1 style="float: left;">Registered assets</h1>
<h1 style="float: left;" i18n="Registered assets page header">Registered assets</h1>
<br>
<div class="clearfix"></div>
<form [formGroup]="searchForm" class="form-inline">
<div class="input-group m-2">
<input style="width: 250px;" formControlName="searchText" type="text" class="form-control" placeholder="Search asset">
<input style="width: 250px;" formControlName="searchText" type="text" class="form-control" i18n-placeholder="Search Assets Placeholder Text" placeholder="Search asset">
<div class="input-group-append">
<button [disabled]="!searchForm.get('searchText')?.value.length" class="btn btn-secondary" type="button" (click)="searchForm.get('searchText')?.setValue('');" autocomplete="off">Clear</button>
<button [disabled]="!searchForm.get('searchText')?.value.length" class="btn btn-secondary" type="button" (click)="searchForm.get('searchText')?.setValue('');" autocomplete="off" i18n="Search Clear Button">Clear</button>
</div>
</div>
</form>
@ -16,11 +16,11 @@
<ng-container *ngIf="(assets$ | async) as filteredAssets; else isLoading">
<table class="table table-borderless table-striped">
<thead>
<th class="td-name">Name</th>
<th>Ticker</th>
<th class="d-none d-md-block">Issuer domain</th>
<th>Asset ID</th>
<th class="d-none d-lg-block">Issuance TX</th>
<th class="td-name" i18n="Asset name header">Name</th>
<th i18n="Asset ticker header">Ticker</th>
<th class="d-none d-md-block" i18n="Asset Issuer Domain header">Issuer domain</th>
<th i18n="Asset ID header">Asset ID</th>
<th class="d-none d-lg-block" i18n="Asset issuance transaction header">Issuance TX</th>
</thead>
<tbody>
<tr *ngFor="let asset of filteredAssets; trackBy: trackByAsset">
@ -43,11 +43,11 @@
<table class="table table-borderless table-striped">
<thead>
<th>Name</th>
<th>Ticker</th>
<th>Issuer domain</th>
<th>Asset ID</th>
<th>Issuance TX</th>
<th i18n="Asset name header">Name</th>
<th i18n="Asset ticker header">Ticker</th>
<th i18n="Asset Issuer Domain header">Issuer domain</th>
<th i18n="Asset ID header">Asset ID</th>
<th i18n="Asset issuance transaction header">Issuance TX</th>
</thead>
<tbody>
<tr *ngFor="let dummy of [0,0,0]">
@ -64,7 +64,7 @@
<ng-template [ngIf]="error">
<div class="text-center">
Error loading assets data.
<ng-container i18n="Asset data load error">Error loading assets data.</ng-container>
<br>
<i>{{ error.error }}</i>
</div>

View File

@ -6,6 +6,7 @@ import { distinctUntilChanged, map, filter, mergeMap, tap, take } from 'rxjs/ope
import { ActivatedRoute, Router } from '@angular/router';
import { merge, combineLatest, Observable } from 'rxjs';
import { AssetExtended } from '../interfaces/electrs.interface';
import { SeoService } from '../services/seo.service';
@Component({
selector: 'app-assets',
@ -32,9 +33,11 @@ export class AssetsComponent implements OnInit {
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private seoService: SeoService,
) { }
ngOnInit() {
this.seoService.setTitle($localize`:@@ee8f8008bae6ce3a49840c4e1d39b4af23d4c263:Assets`);
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
this.searchForm = this.formBuilder.group({

View File

@ -1,5 +1,5 @@
<div class="container-xl">
<h1 style="float: left;">Address</h1>
<h1 style="float: left;" i18n="shared.address">Address</h1>
<a [routerLink]="['/address/' | relativeUrl, addressString]" style="line-height: 56px; margin-left: 10px;">
<span class="d-inline d-lg-none">{{ addressString | shortenString : 24 }}</span>
<span class="d-none d-lg-inline">{{ addressString }}</span>
@ -17,15 +17,15 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Total received</td>
<td i18n="address.total-received">Total received</td>
<td>{{ totalReceived / 100 | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Total sent</td>
<td i18n="address.total-sent">Total sent</td>
<td>{{ totalSent / 100 | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Final balance</td>
<td i18n="address.balance">Balance</td>
<td>{{ (totalReceived - totalSent) / 100 | number: '1.2-2' }} BSQ (<app-bsq-amount [bsq]="totalReceived - totalSent" [forceFiat]="true" [green]="true"></app-bsq-amount>)</td>
</tr>
</tbody>
@ -43,7 +43,11 @@
<br>
<h2>{{ transactions.length | number }} transactions</h2>
<h2>
<ng-container *ngTemplateOutlet="transactions.length === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: transactions.length}"></ng-container>
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
</h2>
<ng-template ngFor let-tx [ngForOf]="transactions">

View File

@ -36,7 +36,7 @@ export class BisqAddressComponent implements OnInit, OnDestroy {
this.transactions = null;
document.body.scrollTo(0, 0);
this.addressString = params.get('id') || '';
this.seoService.setTitle('Address: ' + this.addressString);
this.seoService.setTitle($localize`:@@bisq-address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
return this.bisqApiService.getAddress$(this.addressString)
.pipe(

View File

@ -1,7 +1,7 @@
<div class="container-xl">
<div class="title-block">
<h1>Block <ng-template [ngIf]="blockHeight"><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
<h1><ng-template [ngIf]="blockHeight" i18n="block.block">Block <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
</div>
<div class="clearfix"></div>
@ -14,15 +14,15 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Hash</td>
<td class="td-width" i18n="block.hash">Hash</td>
<td><a [routerLink]="['/block/' | relativeUrl, block.hash]" title="{{ block.hash }}">{{ block.hash | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="block.hash"></app-clipboard></td>
</tr>
<tr>
<td>Timestamp</td>
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
<td>
{{ block.time | date:'yyyy-MM-dd HH:mm' }}
<div class="lg-inline">
<i>(<app-time-since [time]="block.time / 1000" [fastRender]="true"></app-time-since> ago)</i>
<i>(<app-time-since [time]="block.time / 1000" [fastRender]="true"></app-time-since>)</i>
</div>
</td>
</tr>
@ -32,7 +32,7 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Previous hash</td>
<td class="td-width" i18n="block.previous_hash|Transaction Previous Hash">Previous hash</td>
<td><a [routerLink]="['/block/' | relativeUrl, block.previousBlockHash]" title="{{ block.hash }}">{{ block.previousBlockHash | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="block.previousBlockHash"></app-clipboard></td>
</tr>
</table>
@ -44,7 +44,11 @@
<br>
<h2>{{ block.txs.length | number }} transactions</h2>
<h2>
<ng-container *ngTemplateOutlet="block.txs.length === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.txs.length| number}"></ng-container>
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
</h2>
<ng-template ngFor let-tx [ngForOf]="block.txs">
@ -73,11 +77,11 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Hash</td>
<td class="td-width" i18n="block.hash">Hash</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Timestamp</td>
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
<td><span class="skeleton-loader"></span></td>
</tr>
</table>
@ -86,7 +90,7 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Previous hash</td>
<td class="td-width" i18n="block.previous_hash|Transaction Previous Hash">Previous hash</td>
<td><span class="skeleton-loader"></span></td>
</tr>
</table>

View File

@ -82,7 +82,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy {
}
this.isLoading = false;
this.blockHeight = block.height;
this.seoService.setTitle('Block: #' + block.height + ': ' + block.hash);
this.seoService.setTitle($localize`:@@bisq-block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.hash}:BLOCK_HASH:`);
this.block = block;
});
}

View File

@ -1,5 +1,5 @@
<div class="container-xl">
<h1 style="float: left;">Blocks</h1>
<h1 style="float: left;" i18n="Bisq blocks header">Blocks</h1>
<br>
<div class="clearfix"></div>
@ -9,15 +9,15 @@
<div class="table-responsive-sm">
<table class="table table-borderless table-striped">
<thead>
<th style="width: 25%;">Height</th>
<th style="width: 25%;">Confirmed</th>
<th style="width: 25%;">Total Sent</th>
<th class="d-none d-md-block" style="width: 25%;">Transactions</th>
<th style="width: 25%;" i18n="Bisq block height header">Height</th>
<th style="width: 25%;" i18n="Bisq block confirmed time header">Confirmed</th>
<th style="width: 25%;" i18n="Bisq block total BSQ tokens sent header">Total sent</th>
<th class="d-none d-md-block" style="width: 25%;" i18n="Bisq block transactions title">Transactions</th>
</thead>
<tbody *ngIf="blocks.value; else loadingTmpl">
<tr *ngFor="let block of blocks.value[0]; trackBy: trackByFn">
<td><a [routerLink]="['/block/' | relativeUrl, block.hash]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
<td><app-time-since [time]="block.time / 1000" [fastRender]="true"></app-time-since> ago</td>
<td><app-time-since [time]="block.time / 1000" [fastRender]="true"></app-time-since></td>
<td>{{ calculateTotalOutput(block) / 100 | number: '1.2-2' }}<span class="d-none d-md-inline"> BSQ</span></td>
<td class="d-none d-md-block">{{ block.txs.length }}</td>
</tr>

View File

@ -32,7 +32,7 @@ export class BisqBlocksComponent implements OnInit {
) { }
ngOnInit(): void {
this.seoService.setTitle('Blocks');
this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`);
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
this.loadingItems = Array(this.itemsPerPage);
if (document.body.clientWidth < 768) {

View File

@ -1,5 +1,5 @@
<div class="container-xl">
<h1 style="float: left;">BSQ Statistics</h1>
<h1 style="float: left;" i18n="BSQ statistics header">BSQ statistics</h1>
<br>
<div class="clearfix"></div>
@ -7,41 +7,37 @@
<div class="row">
<div class="col-sm">
<table class="table table-borderless table-striped">
<thead>
<th>Property</th>
<th>Value</th>
</thead>
<tbody *ngIf="!isLoading; else loadingTemplate">
<tr>
<td class="td-width">Existing amount</td>
<td class="td-width" i18n="BSQ existing amount">Existing amount</td>
<td>{{ (stats.minted - stats.burnt) / 100 | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Minted amount</td>
<td i18n="BSQ minted amount">Minted amount</td>
<td>{{ stats.minted | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Burnt amount</td>
<td i18n="BSQ burnt amount">Burnt amount</td>
<td>{{ stats.burnt | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Addresses</td>
<td i18n="BSQ addresses">Addresses</td>
<td>{{ stats.addresses | number }}</td>
</tr>
<tr>
<td>Unspent TXOs</td>
<td i18n="BSQ unspent transaction outputs">Unspent TXOs</td>
<td>{{ stats.unspent_txos | number }}</td>
</tr>
<tr>
<td>Spent TXOs</td>
<td i18n="BSQ spent transaction outputs">Spent TXOs</td>
<td>{{ stats.spent_txos | number }}</td>
</tr>
<tr>
<td>Price</td>
<td i18n="BSQ token price">Price</td>
<td><app-fiat [value]="price"></app-fiat></td>
</tr>
<tr>
<td>Market cap</td>
<td i18n="BSQ token market cap">Market cap</td>
<td><app-fiat [value]="price * (stats.minted - stats.burnt) / 100"></app-fiat></td>
</tr>
</tbody>
@ -55,23 +51,23 @@
<ng-template #loadingTemplate>
<tbody>
<tr>
<td class="td-width">Existing amount</td>
<td class="td-width" i18n>Existing amount</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Minted amount</td>
<td i18n>Minted amount</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Burnt amount</td>
<td i18n>Burnt amount</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Addresses</td>
<td i18n>Addresses</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Unspent TXOs</td>
<td i18n>Unspent TXOs</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
@ -79,11 +75,11 @@
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Price</td>
<td i18n>Price</td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td>Market cap</td>
<td i18n>Market cap</td>
<td><span class="skeleton-loader"></span></td>
</tr>
</tbody>

View File

@ -21,8 +21,7 @@ export class BisqStatsComponent implements OnInit {
) { }
ngOnInit() {
this.seoService.setTitle('BSQ Statistics');
this.seoService.setTitle($localize`:@@2a30a4cdb123a03facc5ab8c5b3e6d8b8dbbc3d4:BSQ statistics`);
this.stateService.bsqPrice$
.subscribe((bsqPrice) => {
this.price = bsqPrice;

View File

@ -4,15 +4,15 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Inputs</td>
<td class="td-width" i18n="transaction.inputs">Inputs</td>
<td>{{ totalInput / 100 | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Outputs</td>
<td i18n="transaction.outputs">Outputs</td>
<td>{{ totalOutput / 100 | number: '1.2-2' }} BSQ</td>
</tr>
<tr>
<td>Issuance</td>
<td i18n="asset.issued-amount|Liquid Asset issued amount">Issued amount</td>
<td>{{ totalIssued / 100 | number: '1.2-2' }} BSQ</td>
</tr>
</tbody>
@ -22,11 +22,11 @@
<table class="table table-borderless table-striped">
<tbody class="mobile-even">
<tr>
<td class="td-width">Type</td>
<td class="td-width" i18n>Type</td>
<td><app-bisq-icon class="mr-1" [txType]="tx.txType"></app-bisq-icon> {{ tx.txTypeDisplayString }}</td>
</tr>
<tr>
<td>Version</td>
<td i18n="transaction.version">Version</td>
<td>{{ tx.txVersion }}</td>
</tr>
</tbody>

View File

@ -1,11 +1,14 @@
<div class="container-xl">
<h1 class="float-left mr-3 mb-md-3">Transaction</h1>
<h1 class="float-left mr-3 mb-md-3" i18n="shared.transaction">Transaction</h1>
<ng-template [ngIf]="!isLoading && !error">
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">{{ latestBlock.height - bisqTx.blockHeight + 1 }} confirmation<ng-container *ngIf="latestBlock.height - bisqTx.blockHeight + 1 > 1">s</ng-container></button>
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">
<ng-container *ngTemplateOutlet="latestBlock.height - bisqTx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - bisqTx.blockHeight + 1}"></ng-container>
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
</button>
<div>
<a [routerLink]="['/bisq-tx' | relativeUrl, bisqTx.id]" style="line-height: 56px;">
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
@ -21,22 +24,22 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td>Timestamp</td>
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
<td>
{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }}
<div class="lg-inline">
<i>(<app-time-since [time]="bisqTx.time / 1000" [fastRender]="true"></app-time-since> ago)</i>
<i>(<app-time-since [time]="bisqTx.time / 1000" [fastRender]="true"></app-time-since>)</i>
</div>
</td>
</tr>
<tr>
<td class="td-width">Included in block</td>
<td class="td-width" i18n="transaction.included-in-block|Transaction included in block">Included in block</td>
<td>
<a [routerLink]="['/block/' | relativeUrl, bisqTx.blockHash]" [state]="{ data: { blockHeight: bisqTx.blockHeight } }">{{ bisqTx.blockHeight }}</a>
</td>
</tr>
<tr>
<td class="td-width">Features</td>
<td class="td-width" i18n="transaction.features|Transaction features">Features</td>
<td>
<app-tx-features *ngIf="tx; else loadingTx" [tx]="tx"></app-tx-features>
<ng-template #loadingTx>
@ -51,12 +54,12 @@
<table class="table table-borderless table-striped">
<tbody>
<tr>
<td class="td-width">Burnt</td>
<td class="td-width" i18n="BSQ burnt amount">Burnt amount</td>
<td>
{{ bisqTx.burntFee / 100 | number: '1.2-2' }} BSQ (<app-bsq-amount [bsq]="bisqTx.burntFee" [forceFiat]="true" [green]="true"></app-bsq-amount>)
</tr>
<tr>
<td>Fee per vByte</td>
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
<td *ngIf="!isLoadingTx; else loadingTxFee">
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB
&nbsp;
@ -75,14 +78,14 @@
<br>
<h2>Details</h2>
<h2 i18n="transaction.details">Details</h2>
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
<br>
<h2>Inputs & Outputs</h2>
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
@ -121,7 +124,7 @@
<br>
<h2>Details</h2>
<h2 i18n="transaction.details">Details</h2>
<div class="box">
<table class="table table-borderless table-striped">
<tbody>
@ -142,7 +145,7 @@
<br>
<h2>Inputs & Outputs</h2>
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
<div class="box">
<div class="row">

View File

@ -43,7 +43,7 @@ export class BisqTransactionComponent implements OnInit, OnDestroy {
this.error = null;
document.body.scrollTo(0, 0);
this.txId = params.get('id') || '';
this.seoService.setTitle('Transaction: ' + this.txId);
this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`);
if (history.state.data) {
return of(history.state.data);
}

View File

@ -1,5 +1,5 @@
<div class="container-xl">
<h1 style="float: left;">Transactions</h1>
<h1 style="float: left;" i18n>Transactions</h1>
<div class="d-block float-right">
<form [formGroup]="radioGroupForm">
@ -15,11 +15,11 @@
<table class="table table-borderless table-striped">
<thead>
<th style="width: 20%;">Transaction</th>
<th class="d-none d-md-block" style="width: 100%;">Type</th>
<th style="width: 20%;">Amount</th>
<th style="width: 20%;">Confirmed</th>
<th class="d-none d-md-block">Height</th>
<th style="width: 20%;" i18n>Transaction</th>
<th class="d-none d-md-block" style="width: 100%;" i18n>Type</th>
<th style="width: 20%;" i18n>Amount</th>
<th style="width: 20%;" i18n>Confirmed</th>
<th class="d-none d-md-block" i18n>Height</th>
</thead>
<tbody *ngIf="transactions.value; else loadingTmpl">
<tr *ngFor="let tx of transactions.value[0]; trackBy: trackByFn">
@ -37,7 +37,7 @@
{{ calculateTotalOutput(tx.outputs) / 100 | number: '1.2-2' }}<span class="d-none d-md-inline"> BSQ</span>
</ng-template>
</td>
<td><app-time-since [time]="tx.time / 1000" [fastRender]="true"></app-time-since> ago</td>
<td><app-time-since [time]="tx.time / 1000" [fastRender]="true"></app-time-since></td>
<td class="d-none d-md-block"><a [routerLink]="['/block/' | relativeUrl, tx.blockHash]" [state]="{ data: { blockHeight: tx.blockHeight } }">{{ tx.blockHeight }}</a></td>
</tr>
</tbody>

View File

@ -52,7 +52,9 @@ export class BisqTransactionsComponent implements OnInit {
};
txTypeDropdownTexts: IMultiSelectTexts = {
defaultTitle: 'Filter',
defaultTitle: $localize`:@@bisq-transactions.filter:Filter`,
checkAll: $localize`:@@bisq-transactions.selectall:Select all`,
uncheckAll: $localize`:@@bisq-transactions.unselectall:Unselect all`,
};
// @ts-ignore
@ -72,7 +74,7 @@ export class BisqTransactionsComponent implements OnInit {
) { }
ngOnInit(): void {
this.seoService.setTitle('Transactions');
this.seoService.setTitle($localize`:@@add4cd82e3e38a3110fe67b3c7df56e9602644ee:Transactions`);
this.radioGroupForm = this.formBuilder.group({
txTypes: [this.txTypesDefaultChecked],

View File

@ -59,12 +59,16 @@
<div>
<div class="float-left mt-2-5" *ngIf="showConfirmations && tx.burntFee">
Burnt: {{ tx.burntFee / 100 | number: '1.2-2' }} BSQ (<app-bsq-amount [bsq]="tx.burntFee" [forceFiat]="true" [green]="true"></app-bsq-amount>)
<ng-container i18n="BSQ burnt amount">Burnt amount</ng-container>: {{ tx.burntFee / 100 | number: '1.2-2' }} BSQ (<app-bsq-amount [bsq]="tx.burntFee" [forceFiat]="true" [green]="true"></app-bsq-amount>)
</div>
<div class="float-right">
<span *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
<button type="button" class="btn btn-sm btn-success mt-2">{{ latestBlock.height - tx.blockHeight + 1 }} confirmation<ng-container *ngIf="latestBlock.height - tx.blockHeight + 1 > 1">s</ng-container></button>
<button type="button" class="btn btn-sm btn-success mt-2">
<ng-container *ngTemplateOutlet="latestBlock.height - tx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.blockHeight + 1}"></ng-container>
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
</button>
&nbsp;
</span>
<button type="button" class="btn btn-sm btn-primary mt-2" (click)="switchCurrency()">

View File

@ -12,7 +12,7 @@
<h2 i18n="about.about-the-project">About the project</h2>
<div class="row row-cols-1">
<div class="col col-md-6 offset-md-3">
<div class="col col-md-6 mx-auto">
<p i18n>The mempool open-source project aims to implement a high quality explorer and visualization website for the entire Bitcoin ecosystem, without distractions like altcoins, advertising, or third-party trackers.</p>
</div>
</div>
@ -22,7 +22,7 @@
<h2 i18n="about.maintainers">Maintainers</h2>
<div class="container text-center">
<div class="row row-cols-2">
<div class="row row-cols-2" dir="ltr">
<div class="col col-md-2 offset-md-4">
<a href="https://twitter.com/softsimon_">
<div class="profile_photo mx-auto" style="background-image: url(/resources/profile_softsimon.jpg)"></div>
@ -62,7 +62,7 @@
<button type="button" class="btn btn-primary" (click)="donationStatus = 2" [hidden]="donationStatus !== 1" i18n="about.become-a-sponsor">Become a sponsor ❤️</button>
<p *ngIf="donationStatus === 2 && !sponsorsEnabled">
<span i18n="about.navigate-to">Navigate to</span> <a href="https://mempool.space/about" target="_blank">https://mempool.space/about</a> <span i18n="about.to-sponsor">to sponsor</span>
<ng-container i18n="about.navigate-to-sponsor">Navigate to <a href="https://mempool.space/about" target="_blank">https://mempool.space/about</a> to sponsor</ng-container>
</p>
<div style="max-width: 300px;" class="mx-auto" [hidden]="donationStatus !== 2 || !sponsorsEnabled">
@ -178,19 +178,19 @@
<br><br><br><br>
<a target="_blank" class="mr-4 fw6 mb3 mt2 truncate black-80 f4 link" href="https://github.com/mempool/mempool">
<a target="_blank" class="m-2 fw6 mb3 mt2 truncate black-80 f4 link" href="https://github.com/mempool/mempool">
<span class="dib v-mid">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github fa-w-16 fa-3x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>
</span>
</a>
<a target="_blank" class="mr-4 fw6 mb3 mt2 truncate black-80 f4 link" href="https://twitter.com/mempoolspace">
<a target="_blank" class="m-2 fw6 mb3 mt2 truncate black-80 f4 link" href="https://twitter.com/mempoolspace">
<span class="dib v-mid">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="svg-inline--fa fa-twitter fa-w-16 fa-3x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>
</span>
</a>
<a target="_blank" class="fw6 mb3 mt2 truncate black-80 f4 link" href="https://keybase.io/team/mempool">
<a target="_blank" class="m-2 fw6 mb3 mt2 truncate black-80 f4 link" href="https://keybase.io/team/mempool">
<span class="dib v-mid">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="keybase" class="svg-inline--fa fa-keybase fa-w-14 fa-3x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M286.17 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18zm111.92-147.6c-9.5-14.62-39.37-52.45-87.26-73.71q-9.1-4.06-18.38-7.27a78.43 78.43 0 0 0-47.88-104.13c-12.41-4.1-23.33-6-32.41-5.77-.6-2-1.89-11 9.4-35L198.66 32l-5.48 7.56c-8.69 12.06-16.92 23.55-24.34 34.89a51 51 0 0 0-8.29-1.25c-41.53-2.45-39-2.33-41.06-2.33-50.61 0-50.75 52.12-50.75 45.88l-2.36 36.68c-1.61 27 19.75 50.21 47.63 51.85l8.93.54a214 214 0 0 0-46.29 35.54C14 304.66 14 374 14 429.77v33.64l23.32-29.8a148.6 148.6 0 0 0 14.56 37.56c5.78 10.13 14.87 9.45 19.64 7.33 4.21-1.87 10-6.92 3.75-20.11a178.29 178.29 0 0 1-15.76-53.13l46.82-59.83-24.66 74.11c58.23-42.4 157.38-61.76 236.25-38.59 34.2 10.05 67.45.69 84.74-23.84.72-1 1.2-2.16 1.85-3.22a156.09 156.09 0 0 1 2.8 28.43c0 23.3-3.69 52.93-14.88 81.64-2.52 6.46 1.76 14.5 8.6 15.74 7.42 1.57 15.33-3.1 18.37-11.15C429 443 434 414 434 382.32c0-38.58-13-77.46-35.91-110.92zM142.37 128.58l-15.7-.93-1.39 21.79 13.13.78a93 93 0 0 0 .32 19.57l-22.38-1.34a12.28 12.28 0 0 1-11.76-12.79L107 119c1-12.17 13.87-11.27 13.26-11.32l29.11 1.73a144.35 144.35 0 0 0-7 19.17zm148.42 172.18a10.51 10.51 0 0 1-14.35-1.39l-9.68-11.49-34.42 27a8.09 8.09 0 0 1-11.13-1.08l-15.78-18.64a7.38 7.38 0 0 1 1.34-10.34l34.57-27.18-14.14-16.74-17.09 13.45a7.75 7.75 0 0 1-10.59-1s-3.72-4.42-3.8-4.53a7.38 7.38 0 0 1 1.37-10.34L214 225.19s-18.51-22-18.6-22.14a9.56 9.56 0 0 1 1.74-13.42 10.38 10.38 0 0 1 14.3 1.37l81.09 96.32a9.58 9.58 0 0 1-1.74 13.44zM187.44 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18z"></path></svg>
</span>

View File

@ -34,7 +34,7 @@ export class AboutComponent implements OnInit {
ngOnInit() {
this.gitCommit$ = this.stateService.gitCommit$.pipe(map((str) => str.substr(0, 8)));
this.seoService.setTitle('About');
this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`);
this.websocketService.want(['blocks']);
this.donationForm = this.formBuilder.group({

View File

@ -43,7 +43,11 @@
<br>
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} <span i18n="shared.of">of</span>&nbsp;</ng-template>{{ txCount | number }} <span i18n="shared.transactions">transactions</span></h2>
<h2>
<ng-template [ngIf]="!transactions?.length">&nbsp;</ng-template>
<ng-template i18n="X of X Address Transaction" [ngIf]="transactions?.length === 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transaction</ng-template>
<ng-template i18n="X of X Address Transactions (Plural)" [ngIf]="transactions?.length > 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transactions</ng-template>
</h2>
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>

View File

@ -61,7 +61,7 @@ export class AddressComponent implements OnInit, OnDestroy {
this.transactions = null;
document.body.scrollTo(0, 0);
this.addressString = params.get('id') || '';
this.seoService.setTitle('Address: ' + this.addressString);
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
return merge(
of(true),

View File

@ -1,7 +1,7 @@
<ng-container *ngIf="{ val: network$ | async } as network">
<div class="container-xl">
<div class="text-center">
<h2>{{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} API Service</h2>
<h2>{{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API Service</ng-container></h2>
</div>
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { StateService } from 'src/app/services/state.service';
import { WebsocketService } from 'src/app/services/websocket.service';
import { Observable, merge, of } from 'rxjs';
import { SeoService } from 'src/app/services/seo.service';
@Component({
selector: 'app-api-docs',
@ -16,9 +17,11 @@ export class ApiDocsComponent implements OnInit {
constructor(
private stateService: StateService,
private websocketService: WebsocketService,
private seoService: SeoService,
) { }
ngOnInit(): void {
this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`);
this.network$ = merge(of(''), this.stateService.networkChanged$);
this.websocketService.want(['blocks']);

View File

@ -1,5 +1,5 @@
import { Component, HostListener, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { Component, HostListener, OnInit, Inject, LOCALE_ID, HostBinding } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { WebsocketService } from '../../services/websocket.service';
import { StateService } from 'src/app/services/state.service';
@ -17,8 +17,14 @@ export class AppComponent implements OnInit {
private websocketService: WebsocketService,
private stateService: StateService,
private location: Location,
) { }
@Inject(LOCALE_ID) private locale: string,
) {
if (this.locale.startsWith('ar') || this.locale.startsWith('fa')) {
this.dir = 'rtl';
}
}
@HostBinding('attr.dir') dir = 'ltr';
@HostListener('document:keydown', ['$event'])
handleKeyboardEvents(event: KeyboardEvent) {
if (event.target instanceof HTMLInputElement) {

View File

@ -72,7 +72,7 @@
<br>
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} of </ng-template>{{ txCount | number }} <ng-template [ngIf]="isNativeAsset" [ngIfElse]="defaultAsset">Peg In/Out and Burn Transactions</ng-template><ng-template #defaultAsset>In/Out and Burn Transactions</ng-template></h2>
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} of </ng-template>{{ txCount | number }} <ng-template [ngIf]="isNativeAsset" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template><ng-template #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template></h2>
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>

View File

@ -70,7 +70,7 @@ export class AssetComponent implements OnInit, OnDestroy {
this.transactions = null;
document.body.scrollTo(0, 0);
this.assetString = params.get('id') || '';
this.seoService.setTitle('Asset: ' + this.assetString);
this.seoService.setTitle($localize`:@@asset.component.asset-browser-title:Asset: ${this.assetString}:INTERPOLATION:`);
return merge(
of(true),

View File

@ -43,7 +43,7 @@
<tbody>
<tr *ngIf="block.medianFee !== undefined">
<td class="td-width" i18n="block.median-fee">Median fee</td>
<td>~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="block.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
<td>~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="block.medianFee * 140" digitsInfo="1.2-2" i18n-ngbTooltip="Transaction fee tooltip" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
</tr>
<ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees">
<tr>
@ -82,7 +82,11 @@
<br>
<h2 class="float-left">{{ block.tx_count | number }} <ng-template [ngIf]="block.tx_count === 1" i18n="shared.transaction-count.singular">transaction</ng-template><ng-template [ngIf]="block.tx_count !== 1" i18n="shared.transaction-count.plural">transactions</ng-template></h2>
<h2 class="float-left">
<ng-container *ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | number}"></ng-container>
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
</h2>
<ngb-pagination class="float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page)" [maxSize]="paginationMaxSize" [boundaryLinks]="true"></ngb-pagination>

View File

@ -95,7 +95,7 @@ export class BlockComponent implements OnInit, OnDestroy {
tap((block: Block) => {
this.block = block;
this.blockHeight = block.height;
this.seoService.setTitle('Block: #' + block.height + ': ' + block.id);
this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`);
this.isLoadingBlock = false;
if (block.coinbaseTx) {
this.coinbaseTx = block.coinbaseTx;

View File

@ -7,13 +7,17 @@
</div>
<div class="block-body">
<div class="fees">
~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
~{{ block.medianFee | number:'1.0-0' }} <ng-container i18n="shared.sat-vbyte|sat/vB">sat/vB</ng-container>
</div>
<div class="fee-span">
{{ block.feeRange[1] | number:'1.0-0' }} - {{ block.feeRange[block.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
{{ block.feeRange[1] | number:'1.0-0' }} - {{ block.feeRange[block.feeRange.length - 1] | number:'1.0-0' }} <ng-container i18n="shared.sat-vbyte|sat/vB">sat/vB</ng-container>
</div>
<div class="block-size">{{ block.size | bytes: 2 }}</div>
<div class="transaction-count">{{ block.tx_count | number }} <ng-template [ngIf]="block.tx_count === 1" i18n="shared.transaction">transaction</ng-template><ng-template [ngIf]="block.tx_count !== 1" i18n="shared.transactions">transactions</ng-template></div>
<div class="transaction-count">
<ng-container *ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | number}"></ng-container>
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
</div>
<div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></div>
</div>
</div>

View File

@ -1,4 +1,4 @@
<span #buttonWrapper [attr.data-tlite]="'Copied!'" style="position: relative;">
<span #buttonWrapper [attr.data-tlite]="copiedMessage" style="position: relative;">
<button #btn class="btn btn-sm btn-link pt-0" style="line-height: 0.9;" [attr.data-clipboard-text]="text">
<img src="./resources/clippy.svg" width="13">
</button>

View File

@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, Input, ChangeDetectionStrategy } from '@angular/core';
import { Component, ViewChild, ElementRef, AfterViewInit, Input, ChangeDetectionStrategy } from '@angular/core';
import * as ClipboardJS from 'clipboard';
import * as tlite from 'tlite';
@ -12,6 +12,7 @@ export class ClipboardComponent implements AfterViewInit {
@ViewChild('btn') btn: ElementRef;
@ViewChild('buttonWrapper') buttonWrapper: ElementRef;
@Input() text: string;
copiedMessage: string = $localize`:@@clipboard.copied-message:Copied!`;
clipboard: any;
@ -19,7 +20,7 @@ export class ClipboardComponent implements AfterViewInit {
ngAfterViewInit() {
this.clipboard = new ClipboardJS(this.btn.nativeElement);
this.clipboard.on('success', (e) => {
this.clipboard.on('success', () => {
tlite.show(this.buttonWrapper.nativeElement);
setTimeout(() => {
tlite.hide(this.buttonWrapper.nativeElement);

View File

@ -3,19 +3,19 @@
<td class="d-none d-md-block">
<h5 class="card-title" i18n="fees-box.low-priority">Low priority</h5>
<p class="card-text">
{{ feeEstimations.hourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.hourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
{{ feeEstimations.hourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.hourFee * 140" i18n-ngbTooltip="Transaction fee tooltip" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
</p>
</td>
<td>
<h5 class="card-title" i18n="fees-box.medium-priority">Medium priority</h5>
<p class="card-text">
{{ feeEstimations.halfHourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.halfHourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
{{ feeEstimations.halfHourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.halfHourFee * 140" i18n-ngbTooltip="Transaction fee tooltip" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
</p>
</td>
<td>
<h5 class="card-title" i18n="fees-box.high-priority">High priority</h5>
<p class="card-text">
{{ feeEstimations.fastestFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.fastestFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
{{ feeEstimations.fastestFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.fastestFee * 140" i18n-ngbTooltip="Transaction fee tooltip" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
</p>
</td>
</tr>

View File

@ -1,5 +1,5 @@
<div class="container-xl">
<h1 style="float: left;">Blocks</h1>
<h1 style="float: left;" i18n="latest-blocks.blocks">Blocks</h1>
<br>
<div class="clearfix"></div>
@ -10,7 +10,7 @@
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>
<th style="width: 20%;" i18n="latest-blocks.mined">Mined</th>
<th class="d-none d-lg-block" style="width: 15%;" i18n="latest-blocks.transactions">Transactions</th>
<th style="width: 20%;" i18n="latest-blocks.filled">Filled</th>
<th style="width: 20%;" i18n="latest-blocks.size">Size</th>
</thead>
<tbody>
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">

View File

@ -34,7 +34,7 @@ export class LatestBlocksComponent implements OnInit, OnDestroy {
) { }
ngOnInit() {
this.seoService.setTitle('Blocks');
this.seoService.setTitle($localize`:@@f4cba7faeb126346f09cc6af30124f9a343f7a28:Blocks`);
this.websocketService.want(['blocks']);
this.network$ = merge(of(''), this.stateService.networkChanged$);

View File

@ -26,37 +26,37 @@
<ul class="navbar-nav mr-auto pt-2 pb-2 pb-md-0 pt-md-0 {{ network.val }}">
<ng-template [ngIf]="network.val === 'bisq'" [ngIfElse]="notBisq">
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a class="nav-link" [routerLink]="['/bisq']" (click)="collapse()"><fa-icon [icon]="['fas', 'list']" [fixedWidth]="true" title="Transactions"></fa-icon></a>
<a class="nav-link" [routerLink]="['/bisq']" (click)="collapse()"><fa-icon [icon]="['fas', 'list']" [fixedWidth]="true" i18n-title="master-page.transactions" title="Transactions"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/bisq/blocks']" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" title="Blocks"></fa-icon></a>
<a class="nav-link" [routerLink]="['/bisq/blocks']" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/bisq/stats']" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" title="Stats"></fa-icon></a>
<a class="nav-link" [routerLink]="['/bisq/stats']" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.stats" title="Stats"></fa-icon></a>
</li>
</ng-template>
<ng-template #notBisq>
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" title="Dashboard"></fa-icon></a>
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" title="Blocks"></fa-icon></a>
<a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/graphs' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'chart-area']" [fixedWidth]="true" title="Graphs"></fa-icon></a>
<a class="nav-link" [routerLink]="['/graphs' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'chart-area']" [fixedWidth]="true" i18n-title="master-page.graphs" title="Graphs"></fa-icon></a>
</li>
<li class="nav-item d-none d-sm-block" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tv']" [fixedWidth]="true" title="Full screen TV view"></fa-icon></a>
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tv']" [fixedWidth]="true" i18n-title="master-page.tvview" title="TV view"></fa-icon></a>
</li>
</ng-template>
<li *ngIf="network.val === 'liquid'" class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/liquid/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" title="Assets"></fa-icon></a>
<a class="nav-link" [routerLink]="['/liquid/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
</li>
<li [hidden]="isMobile" class="nav-item mr-2" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/api' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cogs']" [fixedWidth]="true" title="API"></fa-icon></a>
<a class="nav-link" [routerLink]="['/api' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cogs']" [fixedWidth]="true" i18n-title="master-page.api" title="API"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/about']" (click)="collapse()"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" title="About"></fa-icon></a>
<a class="nav-link" [routerLink]="['/about']" (click)="collapse()"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" i18n-title="master-page.about" title="About"></fa-icon></a>
</li>
</ul>
<app-search-form location="top" (searchTriggered)="collapse()"></app-search-form>

View File

@ -14,11 +14,11 @@
<tbody>
<tr>
<td i18n="mempool-block.median-fee">Median fee</td>
<td>~{{ mempoolBlock.medianFee | number:'1.0-0' }} sat/vB (<app-fiat [value]="mempoolBlock.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
<td>~{{ mempoolBlock.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="mempoolBlock.medianFee * 140" digitsInfo="1.2-2" i18n-ngbTooltip="Transaction fee tooltip" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
</tr>
<tr>
<td i18n="mempool-block.fee-span">Fee span</td>
<td><span class="yellow-color">{{ mempoolBlock.feeRange[0] | number:'1.0-0' }} - {{ mempoolBlock.feeRange[mempoolBlock.feeRange.length - 1] | number:'1.0-0' }} sat/vB</span></td>
<td><span class="yellow-color">{{ mempoolBlock.feeRange[0] | number:'1.0-0' }} - {{ mempoolBlock.feeRange[mempoolBlock.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span></span></td>
</tr>
<tr>
<td i18n="mempool-block.total-fees">Total fees</td>
@ -29,7 +29,7 @@
<td>{{ mempoolBlock.nTx }}</td>
</tr>
<tr>
<td i18n="mempool-block.filled">Filled</td>
<td i18n="mempool-block.size">Size</td>
<td>
<div class="progress position-relative">
<div class="progress-bar progress-mempool {{ (network$ | async) }}" role="progressbar" [ngStyle]="{'width': (mempoolBlock.blockVSize / 1000000) * 100 + '%' }"></div>

View File

@ -68,13 +68,11 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
getOrdinal(mempoolBlock: MempoolBlock): string {
const blocksInBlock = Math.ceil(mempoolBlock.blockVSize / 1000000);
if (this.mempoolBlockIndex === 0) {
return 'Next block';
} else if (this.mempoolBlockIndex === this.stateService.env.KEEP_BLOCKS_AMOUNT - 1 && blocksInBlock > 1 ) {
return `Stack of ${blocksInBlock} blocks`;
return $localize`:@@mempool-block.next.block:Next block`;
} else if (this.mempoolBlockIndex === this.stateService.env.KEEP_BLOCKS_AMOUNT - 1 && blocksInBlock > 1) {
return $localize`:@@mempool-block.stack.of.blocks:Stack of ${blocksInBlock}:INTERPOLATION: mempool blocks`;
} else {
const s = ['th', 'st', 'nd', 'rd'];
const v = this.mempoolBlockIndex + 1 % 100;
return this.mempoolBlockIndex + 1 + (s[(v - 20) % 10] || s[v] || s[0]) + ' next block';
return $localize`:@@mempool-block.block.no:Mempool block ${this.mempoolBlockIndex + 1}:INTERPOLATION:`;
}
}
}

View File

@ -11,17 +11,24 @@
{{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</div>
<div class="block-size">{{ projectedBlock.blockSize | bytes: 2 }}</div>
<div class="transaction-count">{{ projectedBlock.nTx | number }} <ng-template [ngIf]="projectedBlock.nTx === 1" i18n="shared.transaction-count.singular">transaction</ng-template><ng-template [ngIf]="projectedBlock.nTx !== 1" i18n="shared.transaction-count.plural">transactions</ng-template></div>
<div class="transaction-count">
<ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
</div>
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= 1000000; else mergedBlock">
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
<span i18n="mempool-blocks.eta-of-next-block|Block Frequency">In</span> &lt; {{ 1 * i + 1 }} <span i18n="shared.minute">minute</span>
<ng-container *ngTemplateOutlet="1 * i + 1 === 1 ? nextBlockEta : nextBlockEtaPlural; context:{ $implicit: 1 * i + 1 }"></ng-container>
</ng-template>
<ng-template #timeDiffMainnet>
<span i18n="mempool-blocks.eta-of-next-block|Block Frequency">In</span> ~{{ 10 * i + 10 }} <span i18n="shared.minutes">minutes</span>
<ng-container *ngTemplateOutlet="nextBlockEtaPlural; context:{ $implicit: 10 * i + 10 }"></ng-container>
</ng-template>
</div>
<ng-template #mergedBlock>
<div class="time-difference"><b>({{ projectedBlock.blockVSize / 1000000 | ceil }} <span i18n="shared.blocks">blocks</span>)</b></div>
<div class="time-difference">
<ng-container *ngTemplateOutlet="blocksPlural; context: {$implicit: projectedBlock.blockVSize / 1000000 | ceil }"></ng-container>
<b>(<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} blocks</ng-template>)</b>
</div>
</ng-template>
</div>
<span class="animated-border"></span>
@ -30,3 +37,7 @@
</div>
<div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }"></div>
</div>
<ng-template let-i #nextBlockEtaPlural i18n="mempool-blocks.eta-of-next-block-plural|Block Frequency (plural)">In ~{{ i }} minutes</ng-template>
<ng-template let-i #nextBlockEta i18n="mempool-blocks.eta-of-next-block|Block Frequency">In ~{{ i }} minute</ng-template>

View File

@ -39,7 +39,7 @@ export class MinerComponent implements OnChanges {
if (pools.payout_addresses[vout.scriptpubkey_address]) {
this.miner = pools.payout_addresses[vout.scriptpubkey_address].name;
this.title = 'Identified by payout address: ' + vout.scriptpubkey_address;
this.title = $localize`:@@miner-identified-by-payout:Identified by payout address: '${vout.scriptpubkey_address}:PAYOUT_ADDRESS:'`;
this.url = pools.payout_addresses[vout.scriptpubkey_address].link;
break;
}
@ -49,7 +49,7 @@ export class MinerComponent implements OnChanges {
const coinbaseAscii = this.hex2ascii(this.coinbaseTransaction.vin[0].scriptsig);
if (coinbaseAscii.indexOf(tag) > -1) {
this.miner = pools.coinbase_tags[tag].name;
this.title = 'Identified by coinbase tag: \'' + tag + '\'';
this.title = $localize`:@@miner-identified-by-coinbase:Identified by coinbase tag: '${tag}:TAG:'`;
this.url = pools.coinbase_tags[tag].link;
break;
}

View File

@ -4,7 +4,7 @@
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" i18n-placeholder="search-form.searchbar-placeholder" placeholder="TXID, block height, hash or address">
</div>
<div>
<button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary"><fa-icon [icon]="['fas', 'search']" [fixedWidth]="true" title="Search"></fa-icon></button>
<button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary"><fa-icon [icon]="['fas', 'search']" [fixedWidth]="true" i18n-title="search-form.search-title" title="Search"></fa-icon></button>
</div>
</div>
</form>

View File

@ -40,12 +40,12 @@
<input ngbButton type="radio" [value]="'1y'" [routerLink]="['/graphs' | relativeUrl]" fragment="1y"> 1Y
</label>
</div>
<button (click)="invertGraph()" class="btn btn-primary btn-sm ml-2 d-none d-md-inline"><fa-icon [icon]="['fas', 'exchange-alt']" [rotate]="90" [fixedWidth]="true" title="Invert"></fa-icon></button>
<button (click)="invertGraph()" class="btn btn-primary btn-sm ml-2 d-none d-md-inline"><fa-icon [icon]="['fas', 'exchange-alt']" [rotate]="90" [fixedWidth]="true" i18n-title="statistics.component-invert.title" title="Invert"></fa-icon></button>
</form>
</div>
<div class="card-body">
<div style="height: 600px;">
<app-mempool-graph [data]="mempoolStats" [dateSpan]="radioGroupForm.controls.dateSpan.value"></app-mempool-graph>
<app-mempool-graph dir="ltr" [data]="mempoolStats" [dateSpan]="radioGroupForm.controls.dateSpan.value"></app-mempool-graph>
</div>
</div>
</div>

View File

@ -52,7 +52,7 @@ export class StatisticsComponent implements OnInit {
}
ngOnInit() {
this.seoService.setTitle('Graphs');
this.seoService.setTitle($localize`:@@5d4f792f048fcaa6df5948575d7cb325c9393383:Graphs`);
this.stateService.networkChanged$.subscribe((network) => this.network = network);
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
const isMobile = window.innerWidth <= 767.98;

View File

@ -5,7 +5,7 @@
</div>
<div class="chart-holder" *ngIf="mempoolStats.length">
<app-mempool-graph [data]="mempoolStats"></app-mempool-graph>
<app-mempool-graph dir="ltr" [data]="mempoolStats"></app-mempool-graph>
</div>
<div class="text-center" class="blockchain-wrapper">

View File

@ -24,7 +24,7 @@ export class TelevisionComponent implements OnInit {
) { }
ngOnInit() {
this.seoService.setTitle('TV view');
this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`);
this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']);
this.apiService.list2HStatistics$()

View File

@ -12,7 +12,11 @@
<h1 class="float-left mr-3 mb-md-3" i18n="shared.transaction">Transaction</h1>
<ng-template [ngIf]="tx?.status?.confirmed">
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">{{ latestBlock.height - tx.status.block_height + 1 }} <ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 == 1" i18n="shared.confirmation-count.singular|Transaction singular confirmation count">confirmation</ng-container><ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1" i18n="shared.confirmation-count.plural|Transaction plural confirmation count">confirmations</ng-container></button>
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">
<ng-container *ngTemplateOutlet="latestBlock.height - tx.status.block_height + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.status.block_height + 1}"></ng-container>
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
</button>
</ng-template>
<ng-template [ngIf]="tx && !tx?.status.confirmed">
<button type="button" class="btn btn-sm btn-danger float-right mr-2 mt-1 mt-md-3" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
@ -56,7 +60,7 @@
<ng-template [ngIf]="transactionTime > 0">
<tr>
<td i18n="transaction.confirmed|Transaction Confirmed state">Confirmed</td>
<td><span i18n="transaction.confirmed.after|Transaction confirmed after">After</span>&nbsp;<app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
<td><ng-container i18n="transaction.confirmed.after|Transaction confirmed after">After <app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></ng-container></td>
</tr>
</ng-template>
<tr *ngIf="network !== 'liquid'">
@ -73,7 +77,7 @@
<tbody>
<tr>
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
<td>{{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</td>
<td>{{ tx.fee | number }} <span i18n="transaction.fee.sat|Transaction Fee sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</td>
</tr>
<tr>
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
@ -123,10 +127,10 @@
</ng-template>
<ng-template #belowBlockLimit>
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeEstimateDefault">
&lt; {{ 1 * txInBlockIndex + 1 }}&nbsp;<span i18n="transaction.minutes|Transaction Minutes">minutes</span>&nbsp;<i>({{ txInBlockIndex + 1 }} <span i18n="transaction.eta.block|Transaction ETA (X blocks)">block</span>{{ txInBlockIndex > 0 ? 's' : '' }})</i>
<ng-container *ngTemplateOutlet="1 * txInBlockIndex + 1 === 1 ? nextBlockEta : nextBlockEtaPlural; context:{ $implicit: 1 * txInBlockIndex + 1 }"></ng-container>&nbsp;<i>(<ng-container *ngTemplateOutlet="txInBlockIndex === 0 ? blocksSingular : blocksPlural; context: {$implicit: txInBlockIndex + 1 }"></ng-container>)</i>
</ng-template>
<ng-template #timeEstimateDefault>
~{{ 10 * txInBlockIndex + 10 }}&nbsp;<span i18n="transaction.minutes|Transaction Minutes">minutes</span>&nbsp;<i>({{ txInBlockIndex + 1 }} <span i18n="transaction.eta.block|Transaction ETA (X blocks)">block</span>{{ txInBlockIndex > 0 ? 's' : '' }})</i>
<ng-container *ngTemplateOutlet="nextBlockEtaPlural; context:{ $implicit: 10 * txInBlockIndex + 10 }"></ng-container>&nbsp;<i>(<ng-container *ngTemplateOutlet="txInBlockIndex === 0 ? blocksSingular : blocksPlural; context: {$implicit: txInBlockIndex + 1 }"></ng-container>)</i>
</ng-template>
</ng-template>
</ng-template>
@ -177,6 +181,10 @@
<td i18n="transaction.size|Transaction Size">Size</td>
<td>{{ tx.size | bytes: 2 }}</td>
</tr>
<tr>
<td i18n="transaction.vsize|Transaction Virtual Size">Virtual size</td>
<td>{{ tx.weight / 4 | vbytes: 2 }}</td>
</tr>
<tr>
<td i18n="transaction.weight|Transaction Weight">Weight</td>
<td>{{ tx.weight | wuBytes: 2 }}</td>
@ -241,7 +249,7 @@
<br>
<h2>Details</h2>
<h2 i18n="transaction.details">Details</h2>
<div class="box">
<table class="table table-borderless table-striped">
<tbody>
@ -253,6 +261,10 @@
<td><span class="skeleton-loader"></span></td>
<td><span class="skeleton-loader"></span></td>
</tr>
<tr>
<td><span class="skeleton-loader"></span></td>
<td><span class="skeleton-loader"></span></td>
</tr>
</tbody>
</table>
</div>
@ -277,3 +289,10 @@
</div>
<br>
<ng-template let-i #nextBlockEtaPlural i18n="mempool-blocks.eta-of-next-block-plural|Block Frequency (plural)">In ~{{ i }} minutes</ng-template>
<ng-template let-i #nextBlockEta i18n="mempool-blocks.eta-of-next-block|Block Frequency">In ~{{ i }} minute</ng-template>
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} block</ng-template>
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} blocks</ng-template>

View File

@ -45,7 +45,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
this.subscription = this.route.paramMap.pipe(
switchMap((params: ParamMap) => {
this.txId = params.get('id') || '';
this.seoService.setTitle('Transaction: ' + this.txId);
this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`);
this.resetTransaction();
return merge(
of(true),

View File

@ -122,15 +122,17 @@
</a>
<ng-template #scriptpubkey_type>
<ng-template [ngIf]="vout.pegout" [ngIfElse]="defaultscriptpubkey_type">
<span i18n="transactions-list.peg-out-to">Peg-out to</span>
<a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
</a>
<ng-container i18n="transactions-list.peg-out-to">Peg-out to <ng-container *ngTemplateOutlet="pegOutLink"></ng-container></ng-container>
<ng-template #pegOutLink>
<a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
</a>
</ng-template>
</ng-template>
<ng-template #defaultscriptpubkey_type>
<ng-template [ngIf]="vout.scriptpubkey_type === 'op_return'" [ngIfElse]="otherPubkeyType">
<a placement="bottom" [ngbTooltip]="vout.scriptpubkey | hex2ascii">OP_RETURN</a>&nbsp;<span *ngIf="vout.scriptpubkey_asm !== 'OP_RETURN'" class="badge badge-secondary scriptmessage">{{ vout.scriptpubkey_asm | hex2ascii }}</span>
<a placement="bottom" [ngbTooltip]="vout.scriptpubkey_asm | hex2ascii">OP_RETURN</a>&nbsp;<span *ngIf="vout.scriptpubkey_asm !== 'OP_RETURN'" class="badge badge-secondary scriptmessage">{{ vout.scriptpubkey_asm | hex2ascii }}</span>
</ng-template>
<ng-template #otherPubkeyType>{{ vout.scriptpubkey_type | scriptpubkeyType }}</ng-template>
</ng-template>
@ -198,7 +200,11 @@
<div class="float-right">
<span *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">{{ latestBlock.height - tx.status.block_height + 1 }} <ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 == 1" i18n="shared.confirmation-count.singular">confirmation</ng-container><ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1" i18n="shared.confirmation-count.plural">confirmations</ng-container></button>
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">
<ng-container *ngTemplateOutlet="latestBlock.height - tx.status.block_height + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - tx.status.block_height + 1}"></ng-container>
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
</button>
<ng-template #unconfirmedButton>
<button type="button" class="btn btn-sm btn-danger mt-2" i18n="transactions-list.unconfirmed">Unconfirmed</button>
</ng-template>

View File

@ -1,22 +0,0 @@
<!-- this component is only used for the angular string extractor -->{{counter}}
<div>
<span i18n="@@time-since.just-now">Just now</span>
<span i18n="@@time-since.sec.ago">{{counter}} sec ago</span>
<span i18n="@@time-since.secs.ago">{{counter}} secs ago</span>
<span i18n="@@time-since.second.ago">{{counter}} second ago</span>
<span i18n="@@time-since.seconds.ago">{{counter}} seconds ago</span>
<span i18n="@@time-since.min.ago">{{counter}} min ago</span>
<span i18n="@@time-since.mins.ago">{{counter}} mins ago</span>
<span i18n="@@time-since.minute.ago">{{counter}} minute ago</span>
<span i18n="@@time-since.minutes.ago">{{counter}} minutes ago</span>
<span i18n="@@time-since.hour.ago">{{counter}} hour ago</span>
<span i18n="@@time-since.hours.ago">{{counter}} hours ago</span>
<span i18n="@@time-since.day.ago">{{counter}} day ago</span>
<span i18n="@@time-since.days.ago">{{counter}} days ago</span>
<span i18n="@@time-since.week.ago">{{counter}} week ago</span>
<span i18n="@@time-since.weeks.ago">{{counter}} weeks ago</span>
<span i18n="@@time-since.month.ago">{{counter}} month ago</span>
<span i18n="@@time-since.months.ago">{{counter}} months ago</span>
<span i18n="@@time-since.year.ago">{{counter}} year ago</span>
<span i18n="@@time-since.years.ago">{{counter}} years ago</span>
</div>

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-translation-strings',
templateUrl: './translation-strings.component.html'
})
export class TranslationStringsComponent {
counter: string;
constructor() { }
}

View File

@ -1,8 +1,8 @@
<span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains; else segwitTwo" class="badge badge-success mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
<span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains; else segwitTwo" class="badge badge-success mr-1" i18n-ngbTooltip="ngbTooltip about segwit gains" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
<ng-template #segwitTwo>
<span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains else potentialP2shGains" class="badge badge-warning mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
<span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains else potentialP2shGains" class="badge badge-warning mr-1" i18n-ngbTooltip="ngbTooltip about double segwit gains" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
<ng-template #potentialP2shGains>
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del i18n="tx-features.tag.segwit|SegWit">SegWit</del></span>
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" i18n-ngbTooltip="ngbTooltip about missed out gains" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del i18n="tx-features.tag.segwit|SegWit">SegWit</del></span>
</ng-template>
</ng-template>
<span *ngIf="isRbfTransaction" class="badge badge-success" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom" i18n="tx-features.tag.rbf|RBF">RBF</span>
<span *ngIf="isRbfTransaction" class="badge badge-success" i18n-ngbTooltip="RBF tooltip" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom" i18n="tx-features.tag.rbf|RBF">RBF</span>

View File

@ -1,3 +1,3 @@
<span *ngIf="feeRating === 1" class="badge badge-success" i18n="tx-fee-rating.optimal|TX Fee Rating is Optimal">Optimal</span>
<span *ngIf="feeRating === 2" class="badge badge-warning" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.warning|TX Fee Rating is Warning">Overpaid {{ overpaidTimes }}x</span>
<span *ngIf="feeRating === 3" class="badge badge-danger" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.danger|TX Fee Rating is Danger">Overpaid {{ overpaidTimes }}x</span>
<span *ngIf="feeRating === 2" class="badge badge-warning" placement="bottom" i18n-ngbTooltip="tx-fee-rating.warning-tooltip" ngbTooltip="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.warning|TX Fee Rating is Warning">Overpaid {{ overpaidTimes }}x</span>
<span *ngIf="feeRating === 3" class="badge badge-danger" placement="bottom" i18n-ngbTooltip="tx-fee-rating.danger-tooltip" ngbTooltip="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.danger|TX Fee Rating is Danger">Overpaid {{ overpaidTimes }}x</span>

View File

@ -128,8 +128,8 @@
<button type="button" class="btn btn-secondary btn-sm d-block mx-auto" (click)="toggleCollapsed()">
<div [ngSwitch]="collapseLevel">
<fa-icon *ngSwitchCase="'three'" [icon]="['fas', 'angle-down']" [fixedWidth]="true" title="Collapse"></fa-icon>
<fa-icon *ngSwitchDefault [icon]="['fas', 'angle-up']" [fixedWidth]="true" title="Expand"></fa-icon>
<fa-icon *ngSwitchCase="'three'" [icon]="['fas', 'angle-down']" [fixedWidth]="true" i18n-title="dashboard.expand" title="Expand"></fa-icon>
<fa-icon *ngSwitchDefault [icon]="['fas', 'angle-up']" [fixedWidth]="true" i18n-title="dashboard.collapse" title="Collapse"></fa-icon>
</div>
</button>
@ -161,7 +161,9 @@
<td>
<h5 class="card-title" i18n="dashboard.mempool-size|Mempool size">Mempool size</h5>
<p class="card-text" *ngIf="(mempoolBlocksData$ | async) as mempoolBlocksData; else loading">
{{ mempoolBlocksData.size | bytes }} ({{ mempoolBlocksData.blocks }} <span [hidden]="mempoolBlocksData.blocks == 1" i18n="dashboard.block">block</span><span [hidden]="mempoolBlocksData.blocks != 1">blocks</span>)
{{ mempoolBlocksData.size | bytes }} (<ng-container *ngTemplateOutlet="mempoolBlocksData.blocks === 1 ? blocksSingular : blocksPlural; context: {$implicit: mempoolBlocksData.blocks }"></ng-container>)
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} block</ng-template>
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} blocks</ng-template>
</p>
</td>
<td>
@ -182,7 +184,7 @@
</span>
<ng-template #inSync>
<div class="progress sub-text" style="max-width: 250px;">
<div class="progress-bar {{ mempoolInfoData.value.progressClass }}" style="padding: 4px;" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}">{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-template i18n="shared.vbytes-per-second|vB/s">vB/s</ng-template></div>
<div class="progress-bar {{ mempoolInfoData.value.progressClass }}" style="padding: 4px;" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}">{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-container i18n="shared.vbytes-per-second|vB/s">vB/s</ng-container></div>
</div>
</ng-template>
</ng-template>

View File

@ -256,9 +256,9 @@ export class DashboardComponent implements OnInit {
changeLanguage() {
const language = this.languageForm.get('language').value;
this.document.location.href = (language === 'en' ? '/' : '/' + language);
try {
document.cookie = `lang=${language}; expires=Thu, 18 Dec 2050 12:00:00 UTC; path=/`;
} catch (e) { }
this.document.location.href = `${language === 'en' ? '' : '/' + language}/${this.stateService.network}`;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -102,10 +102,12 @@ Log notice syslog
CookieAuthentication 1
CookieAuthFileGroupReadable 1
CookieAuthFile /var/db/tor/control_auth_cookie
DataDirectory /var/db/tor
DataDirectoryGroupReadable 1
HiddenServiceDir /var/db/tor/mempool
HiddenServicePort 80 127.0.0.1:81
HiddenServicePort 80 127.0.0.1:80
HiddenServiceVersion 3
```

View File

@ -33,23 +33,21 @@
# fallback for all URLs i.e. /address/foo /tx/foo /block/000
location / {
#return 302 https://mempool.space/$request_uri;
try_files /$lang/$uri /$lang/$uri/ $uri $uri/ /en-US/$uri @index;
# /$lang/index.html /en-US/index.html =404;
try_files /$lang/$uri /$lang/$uri/ $uri $uri/ /en-US/$uri @index-redirect;
}
location @index {
add_header Cache-Control must-revalidate;
try_files /$lang/index.html /en-US/index.html =404;
location @index-redirect {
add_header vary accept-language;
rewrite (.*) /$lang/index.html;
}
# location block using regex are matched in order
# used to rewrite resources from /<lang>/ to /en-US/
location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|no|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh)/resources/ {
location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|ka|no|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh)/resources/ {
rewrite ^/[a-zA-Z-]*/resources/(.*) /en-US/resources/$1;
}
# used for cookie override
location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|no|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh)/ {
location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|ka|no|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh)/ {
try_files $uri $uri/ /$1/index.html =404;
}

View File

@ -54,25 +54,37 @@ http {
proxy_cache_path /var/cache/nginx keys_zone=cache:20m levels=1:2 inactive=600s max_size=500m;
types_hash_max_size 2048;
# exempt localhost from rate limit
geo $limited_ip {
default 1;
127.0.0.1 0;
}
map $limited_ip $limited_ip_key {
1 $binary_remote_addr;
0 '';
}
# rate limit requests
limit_req_zone $binary_remote_addr zone=api:5m rate=200r/m;
limit_req_zone $binary_remote_addr zone=electrs:5m rate=2000r/m;
limit_req_zone $limited_ip_key zone=api:5m rate=200r/m;
limit_req_zone $limited_ip_key zone=electrs:5m rate=2000r/m;
limit_req_status 429;
# rate limit connections
limit_conn_zone $binary_remote_addr zone=websocket:10m;
limit_conn_zone $limited_ip_key zone=websocket:10m;
limit_conn_status 429;
map $http_accept_language $header_lang {
default en-US;
~*^en-US en-US;
~*^en en-US;
~*^ar ar;
~*^cs cs;
~*^de de;
~*^es es;
~*^fa fa;
~*^fr fr;
~*^ja ja;
~*^ka ka;
~*^nl nl;
~*^nn nn;
~*^pt pt;
@ -80,6 +92,7 @@ http {
~*^sv sv;
~*^tr tr;
~*^uk uk;
~*^vi vi;
~*^zh zh;
}
@ -87,12 +100,14 @@ http {
default $header_lang;
~*^en-US en-US;
~*^en en-US;
~*^ar ar;
~*^cs cs;
~*^de de;
~*^es es;
~*^fa fa;
~*^fr fr;
~*^ja ja;
~*^ka ka;
~*^nl nl;
~*^nn nn;
~*^pt pt;
@ -100,6 +115,7 @@ http {
~*^sv sv;
~*^tr tr;
~*^uk uk;
~*^vi vi;
~*^zh zh;
}
@ -153,7 +169,7 @@ http {
}
server {
listen 127.0.0.1:81;
listen 127.0.0.1:80;
listen 443 ssl default http2 backlog=1024;
server_name mempool.space;
ssl_certificate /usr/local/etc/letsencrypt/live/mempool.space/fullchain.pem;