From 9babc32f31e706c519932ab9f56de7d55295882b Mon Sep 17 00:00:00 2001 From: Randall Naar Date: Mon, 31 Aug 2026 17:00:15 -0400 Subject: [PATCH] Localized strings on dashboard and added Portuguese translations. --- client/src/components/elapsed-time.js | 56 +- client/src/components/high-value-assets.js | 2 +- client/src/lib/high-value-assets.js | 5 +- client/src/views/blocks.js | 14 +- client/src/views/difficulty-adjustment.js | 122 +++-- client/src/views/home.js | 2 +- client/src/views/overview.js | 8 +- .../src/views/pending-block-details-card.js | 2 +- client/src/views/transactions.js | 12 +- client/src/views/util.js | 29 +- lang/pt-pt.json | 157 +++++- lang/pt-pt.po | 493 +++++++++++++++++- lang/strings.txt | 115 ++++ test/dashboard-localization.test.js | 140 +++++ www/style.css | 4 +- 15 files changed, 1055 insertions(+), 106 deletions(-) create mode 100644 test/dashboard-localization.test.js diff --git a/client/src/components/elapsed-time.js b/client/src/components/elapsed-time.js index 1ba42fa34..f0f1bbe98 100644 --- a/client/src/components/elapsed-time.js +++ b/client/src/components/elapsed-time.js @@ -3,7 +3,18 @@ const MINUTES_PER_DAY = 24 * 60; const MINUTES_PER_YEAR = 365 * MINUTES_PER_DAY; const MINUTES_PER_MONTH = MINUTES_PER_YEAR / 12; -export const formatDuration = (durationMilliseconds, compact = false) => { +const passthrough = (parts, ...values) => + parts.reduce( + (result, part, index) => + result + part + (index < values.length ? values[index] : ""), + "", + ); + +export const formatDuration = ( + durationMilliseconds, + compact = false, + t = passthrough, +) => { const diffMinutes = Math.max( 0, Math.floor(durationMilliseconds / UPDATE_INTERVAL_MS), @@ -15,16 +26,16 @@ export const formatDuration = (durationMilliseconds, compact = false) => { minutesAfterYears - months * MINUTES_PER_MONTH, ); const units = [ - ["YEAR", "YEARS", "y", years], - ["MONTH", "MONTHS", "mo", months], - ["DAY", "DAYS", "d", Math.floor(minutesAfterMonths / MINUTES_PER_DAY)], + [t`YEAR`, t`YEARS`, t`y`, years], + [t`MONTH`, t`MONTHS`, t`mo`, months], + [t`DAY`, t`DAYS`, t`d`, Math.floor(minutesAfterMonths / MINUTES_PER_DAY)], [ - "HOUR", - "HOURS", - "h", + t`HOUR`, + t`HOURS`, + t`h`, Math.floor((minutesAfterMonths % MINUTES_PER_DAY) / 60), ], - ["MINUTE", "MINUTES", "m", minutesAfterMonths % 60], + [t`MINUTE`, t`MINUTES`, t`m`, minutesAfterMonths % 60], ]; const parts = units .filter((unit) => unit[3] > 0) @@ -35,31 +46,33 @@ export const formatDuration = (durationMilliseconds, compact = false) => { : `${value} ${value === 1 ? singular : plural}`, ); - if (compact) return parts.length ? parts.join(" ") : "< 1m"; + if (compact) return parts.length ? parts.join(" ") : t`< 1m`; - return parts.length ? parts.join(" ") : "< 1 MINUTE"; + return parts.length ? parts.join(" ") : t`< 1 MINUTE`; }; -const formatElapsedTime = (timestamp, compact) => { +const formatElapsedTime = (timestamp, compact, t) => { const fromDate = timestamp < 1e12 ? new Date(timestamp * 1000) : new Date(timestamp); - const duration = formatDuration(new Date() - fromDate, compact); + const duration = formatDuration(new Date() - fromDate, compact, t); if (compact) return duration; - return `${duration} AGO`; + return t`${duration} AGO`; }; const updateElapsedTime = (element) => { element.textContent = formatElapsedTime( element.elapsedTimeTimestamp, element.elapsedTimeCompact, + element.elapsedTimeTranslator, ); }; -const startElapsedTime = (vnode, timestamp, compact) => { +const startElapsedTime = (vnode, timestamp, compact, t) => { vnode.elm.elapsedTimeTimestamp = timestamp; vnode.elm.elapsedTimeCompact = compact; + vnode.elm.elapsedTimeTranslator = t; updateElapsedTime(vnode.elm); vnode.elm.elapsedTimeInterval = window.setInterval( () => updateElapsedTime(vnode.elm), @@ -67,9 +80,10 @@ const startElapsedTime = (vnode, timestamp, compact) => { ); }; -const patchElapsedTime = (_, vnode, timestamp, compact) => { +const patchElapsedTime = (_, vnode, timestamp, compact, t) => { vnode.elm.elapsedTimeTimestamp = timestamp; vnode.elm.elapsedTimeCompact = compact; + vnode.elm.elapsedTimeTranslator = t; updateElapsedTime(vnode.elm); }; @@ -77,14 +91,18 @@ const stopElapsedTime = (vnode) => { window.clearInterval(vnode.elm.elapsedTimeInterval); }; -export const ElapsedTime = ({ timestamp, compact = false } = {}) => ( +export const ElapsedTime = ({ + timestamp, + compact = false, + t = passthrough, +} = {}) => ( startElapsedTime(vnode, timestamp, compact)} + hook-insert={(vnode) => startElapsedTime(vnode, timestamp, compact, t)} hook-postpatch={(oldVnode, vnode) => - patchElapsedTime(oldVnode, vnode, timestamp, compact) + patchElapsedTime(oldVnode, vnode, timestamp, compact, t) } hook-destroy={stopElapsedTime} > - {formatElapsedTime(timestamp, compact)} + {formatElapsedTime(timestamp, compact, t)} ); diff --git a/client/src/components/high-value-assets.js b/client/src/components/high-value-assets.js index 845d9660f..1b98e6b73 100644 --- a/client/src/components/high-value-assets.js +++ b/client/src/components/high-value-assets.js @@ -55,7 +55,7 @@ export const highValueAssets = (t, assetData = {}) => {

{asset.name}

- {formatDollarAmount(dollarAmount)} + {formatDollarAmount(dollarAmount, t)}

); diff --git a/client/src/lib/high-value-assets.js b/client/src/lib/high-value-assets.js index a63283929..a977c5df0 100644 --- a/client/src/lib/high-value-assets.js +++ b/client/src/lib/high-value-assets.js @@ -1,4 +1,5 @@ const value = n => n == null ? 0 : Number(n) + , passthrough = strings => strings[0] export const getPriceFeedApiBase = (apiBase, fallbackOrigin) => { let url @@ -34,8 +35,8 @@ export const calculateCirculatingDollarAmount = (asset, price) => { return Number.isFinite(dollarAmount) ? dollarAmount : null } -export const formatDollarAmount = amount => { - if (!Number.isFinite(amount)) return 'N/A' +export const formatDollarAmount = (amount, t=passthrough) => { + if (!Number.isFinite(amount)) return t`N/A` const units = [ [ 1e12, 'T' ], diff --git a/client/src/views/blocks.js b/client/src/views/blocks.js index 81cab206f..28afbbe1e 100644 --- a/client/src/views/blocks.js +++ b/client/src/views/blocks.js @@ -77,7 +77,7 @@ export const blks = (blocks, viewMore, { t, ...S }) => {
@@ -95,12 +95,12 @@ export const blks = (blocks, viewMore, { t, ...S }) => { className="table-copy-button code-button-btn" type="button" data-clipboardCopy={"" + b.height} - aria-label={`Copy block number ${b.height}`} + aria-label={t`Copy block number ${b.height}`} > {index === 0 ? ( -
Latest
+
{t`Latest`}
) : ( "" )} @@ -111,15 +111,15 @@ export const blks = (blocks, viewMore, { t, ...S }) => { title={new Date(b.timestamp * 1000)} > - {formatRelativeTime(b.timestamp)?.toUpperCase()} + {formatRelativeTime(b.timestamp, t)?.toUpperCase()}

- +
@@ -129,7 +129,7 @@ export const blks = (blocks, viewMore, { t, ...S }) => {

diff --git a/client/src/views/difficulty-adjustment.js b/client/src/views/difficulty-adjustment.js index 0fa6413d3..c49f01f3e 100644 --- a/client/src/views/difficulty-adjustment.js +++ b/client/src/views/difficulty-adjustment.js @@ -8,16 +8,16 @@ const staticRoot = process.env.STATIC_ROOT || ""; const TARGET_BLOCK_SECONDS = 10 * 60; const HASHES_PER_DIFFICULTY = 2 ** 32; -const HASHRATE_UNITS = [ - [1e24, "YH/s", "Yottahashes per second"], - [1e21, "ZH/s", "Zettahashes per second"], - [1e18, "EH/s", "Exahashes per second"], - [1e15, "PH/s", "Petahashes per second"], - [1e12, "TH/s", "Terahashes per second"], - [1e9, "GH/s", "Gigahashes per second"], - [1e6, "MH/s", "Megahashes per second"], - [1e3, "kH/s", "Kilohashes per second"], - [1, "H/s", "Hashes per second"], +const getHashrateUnits = (t) => [ + [1e24, "YH/s", t`Yottahashes per second`], + [1e21, "ZH/s", t`Zettahashes per second`], + [1e18, "EH/s", t`Exahashes per second`], + [1e15, "PH/s", t`Petahashes per second`], + [1e12, "TH/s", t`Terahashes per second`], + [1e9, "GH/s", t`Gigahashes per second`], + [1e6, "MH/s", t`Megahashes per second`], + [1e3, "kH/s", t`Kilohashes per second`], + [1, "H/s", t`Hashes per second`], ]; const DIFFICULTY_UNITS = [ @@ -29,8 +29,8 @@ const DIFFICULTY_UNITS = [ [1, ""], ]; -const formatAdjustment = (value) => { - if (!Number.isFinite(value)) return "N/A"; +const formatAdjustment = (value, unavailable) => { + if (!Number.isFinite(value)) return unavailable; if (value === 0) return "0.00%"; return `${value > 0 ? "+" : ""}${value.toFixed(2)}%`; @@ -100,33 +100,36 @@ const previousAdjustment = (latestBlock, previousBlock) => { return (latestBlock.difficulty / previousBlock.difficulty - 1) * 100; }; -const formatBlockTime = (seconds) => { - if (!Number.isFinite(seconds)) return "N/A"; +const formatBlockTime = (seconds, unavailable, t) => { + if (!Number.isFinite(seconds)) return unavailable; const totalSeconds = Math.max(1, Math.round(seconds)); const minutes = Math.floor(totalSeconds / 60); const remainingSeconds = totalSeconds % 60; - if (!minutes) return `${remainingSeconds}s`; - if (remainingSeconds) return `${minutes}m ${remainingSeconds}s`; + if (!minutes) return `${remainingSeconds}${t`s`}`; + if (remainingSeconds) { + return `${minutes}${t`m`} ${remainingSeconds}${t`s`}`; + } - return `${minutes}m`; + return `${minutes}${t`m`}`; }; -const formatHashrate = (difficulty, averageBlockSeconds) => { +const formatHashrate = (difficulty, averageBlockSeconds, t, unavailable) => { if ( !Number.isFinite(difficulty) || difficulty < 0 || !Number.isFinite(averageBlockSeconds) || averageBlockSeconds <= 0 ) { - return { value: "N/A", footer: "Hashes per second" }; + return { value: unavailable, footer: t`Hashes per second` }; } const hashrate = (difficulty * HASHES_PER_DIFFICULTY) / averageBlockSeconds; + const hashrateUnits = getHashrateUnits(t); const unit = - HASHRATE_UNITS.find(([threshold]) => hashrate >= threshold) || - HASHRATE_UNITS[HASHRATE_UNITS.length - 1]; + hashrateUnits.find(([threshold]) => hashrate >= threshold) || + hashrateUnits[hashrateUnits.length - 1]; const [divisor, symbol, footer] = unit; const value = (hashrate / divisor).toLocaleString("en-US", { maximumSignificantDigits: 3, @@ -135,8 +138,8 @@ const formatHashrate = (difficulty, averageBlockSeconds) => { return { value: `${value} ${symbol}`, footer }; }; -const formatDifficulty = (difficulty) => { - if (!Number.isFinite(difficulty) || difficulty < 0) return "N/A"; +const formatDifficulty = (difficulty, unavailable) => { + if (!Number.isFinite(difficulty) || difficulty < 0) return unavailable; const unit = DIFFICULTY_UNITS.find(([threshold]) => difficulty >= threshold) || @@ -156,21 +159,21 @@ const formatDifficulty = (difficulty) => { })}${suffix}`; }; -const formatAdjustmentDate = (timestamp) => { - if (!Number.isFinite(timestamp)) return "N/A"; +const formatAdjustmentDate = (timestamp, locale, t, fallback = t`N/A`) => { + if (!Number.isFinite(timestamp)) return fallback; const date = new Date(timestamp * 1000); - const month = date.toLocaleString("en-US", { month: "long" }); + const month = date.toLocaleString(locale, { month: "long" }); const day = date.getDate(); const minute = String(date.getMinutes()).padStart(2, "0"); - const period = date.getHours() >= 12 ? "pm" : "am"; + const period = date.getHours() >= 12 ? t`pm` : t`am`; const hour = String(date.getHours() % 12 || 12).padStart(2, "0"); return `${month} ${day} - ${hour}:${minute} ${period}`; }; -const formatTimeUntil = (timestamp) => { - if (!Number.isFinite(timestamp)) return "N/A"; +const formatTimeUntil = (timestamp, unavailable, t) => { + if (!Number.isFinite(timestamp)) return unavailable; const totalMinutes = Math.max( 0, @@ -183,13 +186,15 @@ const formatTimeUntil = (timestamp) => { if (days >= 14) { const weeks = Math.floor(days / 7); const remainingDays = days % 7; - return remainingDays ? `${weeks}w ${remainingDays}d` : `${weeks}w`; + return remainingDays + ? `${weeks}${t`w`} ${remainingDays}${t`d`}` + : `${weeks}${t`w`}`; } - if (days) return `${days}d ${hours}h`; - if (hours) return `${hours}h ${minutes}m`; + if (days) return `${days}${t`d`} ${hours}${t`h`}`; + if (hours) return `${hours}${t`h`} ${minutes}${t`m`}`; - return totalMinutes ? `${totalMinutes}m` : "< 1m"; + return totalMinutes ? `${totalMinutes}${t`m`}` : t`< 1m`; }; const adjustmentStat = (title, value, className = "") => ( @@ -207,7 +212,9 @@ export default ({ blocks, dashboardEpochStartBlock, dashboardPreviousDifficultyBlock, + t, }) => { + const unavailable = t`N/A`; const latestBlock = blocks && blocks[0]; const epochTiming = getEpochTiming(latestBlock, dashboardEpochStartBlock); const expected = expectedAdjustment(epochTiming); @@ -217,17 +224,25 @@ export default ({ ); const averageBlockTime = formatBlockTime( epochTiming && epochTiming.averageBlockSeconds, + unavailable, + t, ); const hashrate = formatHashrate( latestBlock && latestBlock.difficulty, epochTiming && epochTiming.averageBlockSeconds, + t, + unavailable, ); const estimatedAdjustmentTimestamp = epochTiming && epochTiming.estimatedAdjustmentTimestamp; - const nextAdjustment = formatTimeUntil(estimatedAdjustmentTimestamp); + const nextAdjustment = formatTimeUntil( + estimatedAdjustmentTimestamp, + unavailable, + t, + ); const nextAdjustmentFooter = Number.isFinite(estimatedAdjustmentTimestamp) - ? `Next adj. in ${nextAdjustment}` - : "Next adjustment unavailable"; + ? t`Next adj. in ${nextAdjustment}` + : t`Next adjustment unavailable`; return (
@@ -236,33 +251,37 @@ export default ({
-

Difficulty Adjustment

+

{t`Difficulty Adjustment`}

- {adjustmentStat("AVERAGE BLOCK TIME", averageBlockTime)} + {adjustmentStat(t`AVERAGE BLOCK TIME`, averageBlockTime)} {statDivider()} {adjustmentStat( - "EXPECTED ADJ", - formatAdjustment(expected), + t`EXPECTED ADJ`, + formatAdjustment(expected, unavailable), adjustmentClass(expected), )} {statDivider("difficulty-adjustment-stat-divider-middle")} {adjustmentStat( - "PREVIOUS ADJ", - formatAdjustment(previous), + t`PREVIOUS ADJ`, + formatAdjustment(previous, unavailable), adjustmentClass(previous), )} {statDivider()} {adjustmentStat( - "EXPECTED ADJ DATE", - formatAdjustmentDate(estimatedAdjustmentTimestamp), + t`EXPECTED ADJ DATE`, + formatAdjustmentDate( + estimatedAdjustmentTimestamp, + t.lang_id || "en-US", + t, + ), )}
@@ -270,18 +289,21 @@ export default ({
diff --git a/client/src/views/home.js b/client/src/views/home.js index 826911cef..1da796eb6 100644 --- a/client/src/views/home.js +++ b/client/src/views/home.js @@ -30,7 +30,7 @@ export const dashBoard = ({ t, blocks, dashboardState, loading, ...S }) => { {!isBitcoinNetwork ? feeMarket({ t, ...S }) : ""} {isBitcoinNetwork - ? difficultyAdjustment({ blocks: dashblocks, ...S }) + ? difficultyAdjustment({ blocks: dashblocks, t, ...S }) : ""} {showHighValueAssets ? highValueAssets(t, highValueAssetData) : ""} , diff --git a/client/src/views/overview.js b/client/src/views/overview.js index 3781b09b9..596ef0638 100644 --- a/client/src/views/overview.js +++ b/client/src/views/overview.js @@ -77,13 +77,15 @@ export const overview = ({ } value={ latestBlock ? ( - + ) : ( "" ) } footer={ - latestBlock ? `BLOCK #${latestBlock.height.toLocaleString()}` : "" + latestBlock + ? t`BLOCK #${latestBlock.height.toLocaleString()}` + : "" } /> @@ -118,7 +120,7 @@ export const overview = ({ } diff --git a/client/src/views/pending-block-details-card.js b/client/src/views/pending-block-details-card.js index 700b83bc7..39bd3a6ab 100644 --- a/client/src/views/pending-block-details-card.js +++ b/client/src/views/pending-block-details-card.js @@ -539,7 +539,7 @@ const PendingBlockDetailsCard = ({ "time-since-last-block", t`Time Since Last Block`, block ? ( - + ) : ( "-" ), diff --git a/client/src/views/transactions.js b/client/src/views/transactions.js index b191098b9..6deee79b5 100644 --- a/client/src/views/transactions.js +++ b/client/src/views/transactions.js @@ -18,15 +18,15 @@ export const transactions = (txs, viewMore, { t, ...S }) => (
-

Latest Transactions

+

{t`Latest Transactions`}

-
TRANSACTION ID
-
VALUE
-
SIZE
+
{t`TRANSACTION ID`}
+
{t`VALUE`}
+
{t`SIZE`}
- FEE + {t`FEE`}
@@ -46,7 +46,7 @@ export const transactions = (txs, viewMore, { t, ...S }) => ( role="button" tabindex="0" data-clipboardCopy={txOverview.txid} - aria-label={`Copy transaction id ${txOverview.txid}`} + aria-label={t`Copy transaction id ${txOverview.txid}`} > diff --git a/client/src/views/util.js b/client/src/views/util.js index d9dd5b0a8..ee41ccf60 100644 --- a/client/src/views/util.js +++ b/client/src/views/util.js @@ -92,7 +92,18 @@ export const formatNumber = (s, precision=null) => { return whole + (dec != null ? '.'+dec : '') } -export const formatRelativeTime = (fromDate, toDate = new Date()) => { +export const formatRelativeTime = (fromDate, toDate = new Date(), t) => { + if (typeof toDate === 'function') { + t = toDate + toDate = new Date() + } + + t = t || ((parts, ...values) => parts.reduce( + (result, part, index) => + result + part + (index < values.length ? values[index] : ''), + '', + )) + if (typeof fromDate === 'number') { fromDate = fromDate < 1e12 ? new Date(fromDate * 1000) @@ -101,21 +112,27 @@ export const formatRelativeTime = (fromDate, toDate = new Date()) => { const diffSeconds = Math.floor((toDate - fromDate) / 1000) - if (diffSeconds < 5) return 'just now' - if (diffSeconds < 60) return `${diffSeconds} seconds ago` + if (diffSeconds < 5) return t`just now` + if (diffSeconds < 60) return t`${diffSeconds} seconds ago` const diffMinutes = Math.floor(diffSeconds / 60) if (diffMinutes < 60) { - return diffMinutes === 1 ? '1 minute ago' : `${diffMinutes} minutes ago` + return diffMinutes === 1 + ? t`1 minute ago` + : t`${diffMinutes} minutes ago` } const diffHours = Math.floor(diffMinutes / 60) if (diffHours < 24) { - return diffHours === 1 ? '1 hour ago' : `${diffHours} hours ago` + return diffHours === 1 + ? t`1 hour ago` + : t`${diffHours} hours ago` } const diffDays = Math.floor(diffHours / 24) - return diffDays === 1 ? '1 day ago' : `${diffDays} days ago` + return diffDays === 1 + ? t`1 day ago` + : t`${diffDays} days ago` } export const getBlockPercentageUsed = blockWeight => diff --git a/lang/pt-pt.json b/lang/pt-pt.json index 0087e9831..8b27850c2 100644 --- a/lang/pt-pt.json +++ b/lang/pt-pt.json @@ -4,7 +4,9 @@ "Block %s": "Bloco %0", "Block #%s: %s": "Bloco #%0: %1", "Confidential": "Confidencial", + "Confirmed": "Confirmada", "Details": "Detalhes", + "Fee": "Taxa", "Height": "Altura", "In best chain (%s confirmations)": [ "Na melhor cadeia (1 Confirmação)", @@ -16,6 +18,7 @@ "Loading...": "Carregando...", "Load more": "Mais", "Next": "Próximo", + "No recent transactions": "Sem transações recentes", "No results found": "Nenhum resultado encontrado", "Page Not Found": "Página Não Encontrada", "Previous": "Anterior", @@ -24,6 +27,7 @@ "%0 Confirmações" ], "Search for block height, hash, transaction, or address": "Pesquise por altura do bloco, hash, transação ou endereço", + "Size": "Tamanho", "Size (KB)": "Tamanho (KB)", "%s of %s Transactions": "%0 de %1 Confirmações", "Spent by": "Gasto por", @@ -40,5 +44,156 @@ "Unconfirmed": "Não confirmada", "Unspent": "Não gasto", "Version": "Versão", - "Weight (KWU)": "Peso (KWU)" + "Virtual size": "Tamanho virtual", + "Weight (KWU)": "Peso (KWU)", + "%s MINUTE PAST EXPECTED INTERVAL": "%0 MINUTO APÓS O INTERVALO PREVISTO", + "%s MINUTES PAST EXPECTED INTERVAL": "%0 MINUTOS APÓS O INTERVALO PREVISTO", + "%s SELECTED + COINBASE": "%0 SELECIONADAS + COINBASE", + "%s added since the last update": "%0 adicionadas desde a última atualização", + "%s days ago": "há %0 dias", + "%s hours ago": "há %0 horas", + "%s minutes ago": "há %0 minutos", + "%s removed since the last update": "%0 removidas desde a última atualização", + "%s seconds ago": "há %0 segundos", + "1 day ago": "há 1 dia", + "1 hour ago": "há 1 hora", + "1 minute ago": "há 1 minuto", + "< 1 MINUTE PAST EXPECTED INTERVAL": "< 1 MINUTO APÓS O INTERVALO PREVISTO", + "< 1m": "< 1min", + "A balanced fee rate estimated to confirm within %s blocks.": "Uma taxa de comissão equilibrada estimada para confirmação no prazo de %0 blocos.", + "A higher-priority fee rate estimated to confirm in the next block.": "Uma taxa de comissão de prioridade mais alta estimada para confirmação no próximo bloco.", + "A lower-priority fee rate estimated to confirm within %s blocks.": "Uma taxa de comissão de prioridade mais baixa estimada para confirmação no prazo de %0 blocos.", + "AMOUNT": "MONTANTE", + "AVERAGE BLOCK TIME": "TEMPO MÉDIO DO BLOCO", + "AVG FEE": "TAXA MÉDIA", + "All selected transactions are shown individually.": "Todas as transações selecionadas são apresentadas individualmente.", + "Assets vs Liabilities": "Ativos vs Passivos", + "Average": "Média", + "Average fee rate and transaction fee in the high-fee portion of this template.": "Taxa de comissão média e taxa de transação na parte de taxas altas deste modelo.", + "Average fee rate and transaction fee in the low-fee portion of this template.": "Taxa de comissão média e taxa de transação na parte de taxas baixas deste modelo.", + "Average fee rate and transaction fee in the middle-fee portion of this template.": "Taxa de comissão média e taxa de transação na parte de taxas médias deste modelo.", + "BLOCK": "BLOCO", + "BLOCK #%s": "BLOCO #%0", + "BLOCK FILLING": "PREENCHIMENTO DO BLOCO", + "Bitcoin price line chart": "Gráfico de linhas do preço do Bitcoin", + "Block #%s": "Bloco #%0", + "Block Weight": "Peso do Bloco", + "Block is %s% full": "O bloco está %0% preenchido", + "Block utilization unavailable": "Utilização do bloco indisponível", + "Blocks History": "Histórico de Blocos", + "Confirmed federation BTC holdings divided by circulating L-BTC supply.": "Reservas confirmadas de BTC da federação divididas pela oferta de L-BTC em circulação.", + "Confirmed peg-ins minus confirmed peg-outs.": "Peg-ins confirmados menos peg-outs confirmados.", + "Copy block number %s": "Copiar número do bloco %0", + "Copy transaction id %s": "Copiar ID da transação %0", + "Current template weight and serialized size compared with their block limits.": "Peso atual do modelo e tamanho serializado comparados com os respetivos limites do bloco.", + "Difficulty": "Dificuldade", + "Difficulty Adjustment": "Ajuste de Dificuldade", + "EXPECTED ADJ": "AJUSTE ESPERADO", + "EXPECTED ADJ DATE": "DATA DO AJUSTE ESPERADO", + "EXPECTED IN < 1 MINUTE": "PREVISTO EM < 1 MINUTO", + "EXPECTED IN ~%s MINUTE": "PREVISTO EM ~%0 MINUTO", + "EXPECTED IN ~%s MINUTES": "PREVISTO EM ~%0 MINUTOS", + "EXPECTED INTERVAL REACHED": "INTERVALO PREVISTO ATINGIDO", + "Elapsed time since the last block confirmed.": "Tempo decorrido desde a confirmação do último bloco.", + "Elapsed time since the last block confirmed. Bitcoin targets one every ~10 minutes.": "Tempo decorrido desde a confirmação do último bloco. A rede Bitcoin procura confirmar um bloco a cada ~10 minutos.", + "Elapsed time since the last block confirmed. Liquid targets one every ~1 minute.": "Tempo decorrido desde a confirmação do último bloco. A rede Liquid procura confirmar um bloco a cada ~1 minuto.", + "Estimated computing power securing the network.": "Poder computacional estimado que protege a rede.", + "Estimated time until the peg transaction is confirmed.": "Tempo estimado até à confirmação da transação de peg.", + "Exahashes per second": "Exahashes por segundo", + "FEE": "TAXA", + "Federation BTC Holdings": "Reservas de BTC da Federação", + "Fee Market": "Mercado de Taxas", + "Fee rate": "Taxa de comissão", + "Fee-rate estimates are unavailable; transactions use a neutral color.": "As estimativas das taxas de comissão estão indisponíveis; as transações utilizam uma cor neutra.", + "Gigahashes per second": "Gigahashes por segundo", + "HIGH": "ALTA", + "Hashes per second": "Hashes por segundo", + "Hashrate": "Taxa de Hash", + "High": "Alta", + "High-Value Assets": "Ativos de Alto Valor", + "How busy mempool activity is. More congestion means higher fees for quick confirmation.": "Nível de atividade da mempool. Mais congestionamento significa taxas mais altas para uma confirmação rápida.", + "How full this block is.": "Nível de preenchimento deste bloco.", + "How hard it is to find a valid block. Tracks hashrate.": "Dificuldade de encontrar um bloco válido. Acompanha a taxa de hash.", + "How hard it is to mine new blocks. Bitcoin retargets mining difficulty every 2,016 blocks to keep blocks near 10 minutes. Current is the projected next change; Previous was the last change.": "Dificuldade de minerar novos blocos. A rede Bitcoin reajusta a dificuldade de mineração a cada 2.016 blocos para manter o intervalo entre blocos próximo de 10 minutos. Atual é a próxima alteração projetada; Anterior foi a última alteração.", + "IN MEMPOOL": "NA MEMPOOL", + "Individually rendered transactions": "Transações apresentadas individualmente", + "Kilohashes per second": "Kilohashes por segundo", + "LOW": "BAIXA", + "Last change N/A": "Última alteração N/D", + "Last change on %s": "Última alteração em %0", + "Latest": "Mais Recente", + "Latest Blocks": "Blocos Mais Recentes", + "Latest Transactions": "Transações Mais Recentes", + "Legacy": "Legado", + "Live": "Em Direto", + "Loading block utilization": "A carregar utilização do bloco", + "Loading pending block transactions": "A carregar transações do bloco pendente", + "Low": "Baixa", + "Lower-fee transactions are summarized in the metrics because they do not fit at this resolution.": "As transações com taxas mais baixas são resumidas nas métricas porque não cabem nesta resolução.", + "Medium": "Média", + "Megahashes per second": "Megahashes por segundo", + "Mempool Congestion": "Congestionamento da Mempool", + "Moderate": "Moderada", + "N/A": "N/D", + "Next Block": "Próximo Bloco", + "Next adj. in %s": "Próx. ajuste em %0", + "Next adjustment unavailable": "Próximo ajuste indisponível", + "No recent blocks": "Sem blocos recentes", + "Overview": "Visão Geral", + "PREVIOUS ADJ": "AJUSTE ANTERIOR", + "Peg Information": "Informação de Peg", + "Peg data is currently unavailable.": "Os dados de peg estão atualmente indisponíveis.", + "Pending Transactions": "Transações Pendentes", + "Pending block transaction grid": "Grelha de transações do bloco pendente", + "Petahashes per second": "Petahashes por segundo", + "Proof of Reserves": "Prova de Reservas", + "Recent Peg-Ins/Outs": "Peg-Ins/Outs Recentes", + "Recommended Fee": "Taxa Recomendada", + "SIZE": "TAMANHO", + "See More": "Ver Mais", + "Share of selected transactions using SegWit or legacy serialization.": "Percentagem das transações selecionadas que utilizam serialização SegWit ou legada.", + "Suggested rate (sat/vB) to confirm in the next block or two.": "Taxa sugerida (sat/vB) para confirmação no próximo bloco ou nos próximos dois blocos.", + "TOTAL FEE COLLECTED": "TOTAL DE TAXAS COBRADAS", + "TRANSACTION ID": "ID DA TRANSAÇÃO", + "TRANSACTIONS": "TRANSAÇÕES", + "TX ID": "ID DA TX", + "TYPE": "TIPO", + "Terahashes per second": "Terahashes por segundo", + "This panel shows the circulating value of high-value assets on Liquid.": "Este painel apresenta o valor em circulação dos ativos de alto valor na Liquid.", + "Time Since Last Block": "Tempo Desde o Último Bloco", + "Total Fees Collected": "Total de Taxas Cobradas", + "Total transaction fees a miner would collect from the current template, shown in bitcoin and US dollars.": "Total de taxas de transação que um mineiro receberia do modelo atual, apresentado em bitcoin e dólares dos EUA.", + "Transaction Types": "Tipos de Transação", + "Transactions currently selected for the pending block, including the coinbase transaction.": "Transações atualmente selecionadas para o bloco pendente, incluindo a transação coinbase.", + "Transactions currently waiting in the node's mempool.": "Transações atualmente à espera na mempool do nó.", + "Unable to refresh — showing previous data.": "Não foi possível atualizar — a apresentar os dados anteriores.", + "Unknown": "Desconhecido", + "Use the arrow keys to inspect transactions and Enter to open one.": "Utilize as teclas de seta para inspecionar transações e Enter para abrir uma.", + "VALUE": "VALOR", + "VOLUME IN": "VOLUME DE ENTRADA", + "VOLUME OUT": "VOLUME DE SAÍDA", + "View block %s": "Ver bloco %0", + "Weight": "Peso", + "Yottahashes per second": "Yottahashes por segundo", + "Zettahashes per second": "Zettahashes por segundo", + "am": "a.m.", + "just now": "agora mesmo", + "m": "min", + "mo": "mês", + "pm": "p.m.", + "w": "sem", + "y": "a", + "~%s min": "~%0 min", + "%s AGO": "HÁ %0", + "< 1 MINUTE": "< 1 MINUTO", + "DAY": "DIA", + "DAYS": "DIAS", + "HOUR": "HORA", + "HOURS": "HORAS", + "MINUTE": "MINUTO", + "MINUTES": "MINUTOS", + "MONTH": "MÊS", + "MONTHS": "MESES", + "YEAR": "ANO", + "YEARS": "ANOS" } diff --git a/lang/pt-pt.po b/lang/pt-pt.po index 499782d19..2ffa35db1 100644 --- a/lang/pt-pt.po +++ b/lang/pt-pt.po @@ -82,7 +82,7 @@ msgid "Confidential" msgstr "Confidencial" msgid "Confirmed" -msgstr "" +msgstr "Confirmada" msgid "Confirmed received" msgstr "" @@ -106,10 +106,10 @@ msgid "Esplora is currently unavailable, please try again later." msgstr "" msgid "ETA" -msgstr "" +msgstr "ETA" msgid "Fee" -msgstr "" +msgstr "Taxa" msgid "Go" msgstr "" @@ -195,7 +195,7 @@ msgid "No outputs" msgstr "" msgid "No recent transactions" -msgstr "" +msgstr "Sem transações recentes" msgid "No reissuance" msgstr "" @@ -231,7 +231,7 @@ msgid "Page Not Found" msgstr "Página Não Encontrada" msgid "Peg-out" -msgstr "" +msgstr "Peg-out" msgid "Peg-out address" msgstr "" @@ -329,7 +329,7 @@ msgid "%s from tip" msgstr "" msgid "Size" -msgstr "" +msgstr "Tamanho" msgid "Size (KB)" msgstr "Tamanho (KB)" @@ -432,7 +432,7 @@ msgid "Transaction: %s" msgstr "Transação: %0" msgid "TXID" -msgstr "" +msgstr "TXID" msgid "txid:vout" msgstr "" @@ -474,7 +474,7 @@ msgid "Version" msgstr "Versão" msgid "Virtual size" -msgstr "" +msgstr "Tamanho virtual" msgid "We encountered an error. Please try again later." msgstr "" @@ -487,3 +487,480 @@ msgstr "" msgid "Witness" msgstr "" + +msgid "%s MINUTE PAST EXPECTED INTERVAL" +msgstr "%0 MINUTO APÓS O INTERVALO PREVISTO" + +msgid "%s MINUTES PAST EXPECTED INTERVAL" +msgstr "%0 MINUTOS APÓS O INTERVALO PREVISTO" + +msgid "%s SELECTED + COINBASE" +msgstr "%0 SELECIONADAS + COINBASE" + +msgid "%s added since the last update" +msgstr "%0 adicionadas desde a última atualização" + +msgid "%s days ago" +msgstr "há %0 dias" + +msgid "%s hours ago" +msgstr "há %0 horas" + +msgid "%s minutes ago" +msgstr "há %0 minutos" + +msgid "%s removed since the last update" +msgstr "%0 removidas desde a última atualização" + +msgid "%s seconds ago" +msgstr "há %0 segundos" + +msgid "1 day ago" +msgstr "há 1 dia" + +msgid "1 hour ago" +msgstr "há 1 hora" + +msgid "1 minute ago" +msgstr "há 1 minuto" + +msgid "< 1 MINUTE PAST EXPECTED INTERVAL" +msgstr "< 1 MINUTO APÓS O INTERVALO PREVISTO" + +msgid "< 1 min" +msgstr "< 1 min" + +msgid "< 1m" +msgstr "< 1min" + +msgid "A balanced fee rate estimated to confirm within %s blocks." +msgstr "Uma taxa de comissão equilibrada estimada para confirmação no prazo de %0 blocos." + +msgid "A higher-priority fee rate estimated to confirm in the next block." +msgstr "Uma taxa de comissão de prioridade mais alta estimada para confirmação no próximo bloco." + +msgid "A lower-priority fee rate estimated to confirm within %s blocks." +msgstr "Uma taxa de comissão de prioridade mais baixa estimada para confirmação no prazo de %0 blocos." + +msgid "AMOUNT" +msgstr "MONTANTE" + +msgid "AVERAGE BLOCK TIME" +msgstr "TEMPO MÉDIO DO BLOCO" + +msgid "AVG FEE" +msgstr "TAXA MÉDIA" + +msgid "All selected transactions are shown individually." +msgstr "Todas as transações selecionadas são apresentadas individualmente." + +msgid "Assets vs Liabilities" +msgstr "Ativos vs Passivos" + +msgid "Average" +msgstr "Média" + +msgid "Average fee rate and transaction fee in the high-fee portion of this template." +msgstr "Taxa de comissão média e taxa de transação na parte de taxas altas deste modelo." + +msgid "Average fee rate and transaction fee in the low-fee portion of this template." +msgstr "Taxa de comissão média e taxa de transação na parte de taxas baixas deste modelo." + +msgid "Average fee rate and transaction fee in the middle-fee portion of this template." +msgstr "Taxa de comissão média e taxa de transação na parte de taxas médias deste modelo." + +msgid "BLOCK" +msgstr "BLOCO" + +msgid "BLOCK #%s" +msgstr "BLOCO #%0" + +msgid "BLOCK FILLING" +msgstr "PREENCHIMENTO DO BLOCO" + +msgid "Bitcoin" +msgstr "Bitcoin" + +msgid "Bitcoin price line chart" +msgstr "Gráfico de linhas do preço do Bitcoin" + +msgid "Block #%s" +msgstr "Bloco #%0" + +msgid "Block Weight" +msgstr "Peso do Bloco" + +msgid "Block is %s% full" +msgstr "O bloco está %0% preenchido" + +msgid "Block utilization unavailable" +msgstr "Utilização do bloco indisponível" + +msgid "Blocks History" +msgstr "Histórico de Blocos" + +msgid "Confirmed federation BTC holdings divided by circulating L-BTC supply." +msgstr "Reservas confirmadas de BTC da federação divididas pela oferta de L-BTC em circulação." + +msgid "Confirmed peg-ins minus confirmed peg-outs." +msgstr "Peg-ins confirmados menos peg-outs confirmados." + +msgid "Copy block number %s" +msgstr "Copiar número do bloco %0" + +msgid "Copy transaction id %s" +msgstr "Copiar ID da transação %0" + +msgid "Current template weight and serialized size compared with their block limits." +msgstr "Peso atual do modelo e tamanho serializado comparados com os respetivos limites do bloco." + +msgid "Difficulty" +msgstr "Dificuldade" + +msgid "Difficulty Adjustment" +msgstr "Ajuste de Dificuldade" + +msgid "EXPECTED ADJ" +msgstr "AJUSTE ESPERADO" + +msgid "EXPECTED ADJ DATE" +msgstr "DATA DO AJUSTE ESPERADO" + +msgid "EXPECTED IN < 1 MINUTE" +msgstr "PREVISTO EM < 1 MINUTO" + +msgid "EXPECTED IN ~%s MINUTE" +msgstr "PREVISTO EM ~%0 MINUTO" + +msgid "EXPECTED IN ~%s MINUTES" +msgstr "PREVISTO EM ~%0 MINUTOS" + +msgid "EXPECTED INTERVAL REACHED" +msgstr "INTERVALO PREVISTO ATINGIDO" + +msgid "Elapsed time since the last block confirmed." +msgstr "Tempo decorrido desde a confirmação do último bloco." + +msgid "Elapsed time since the last block confirmed. Bitcoin targets one every ~10 minutes." +msgstr "Tempo decorrido desde a confirmação do último bloco. A rede Bitcoin procura confirmar um bloco a cada ~10 minutos." + +msgid "Elapsed time since the last block confirmed. Liquid targets one every ~1 minute." +msgstr "Tempo decorrido desde a confirmação do último bloco. A rede Liquid procura confirmar um bloco a cada ~1 minuto." + +msgid "Estimated computing power securing the network." +msgstr "Poder computacional estimado que protege a rede." + +msgid "Estimated time until the peg transaction is confirmed." +msgstr "Tempo estimado até à confirmação da transação de peg." + +msgid "Exahashes per second" +msgstr "Exahashes por segundo" + +msgid "FEE" +msgstr "TAXA" + +msgid "Federation BTC Holdings" +msgstr "Reservas de BTC da Federação" + +msgid "Fee Market" +msgstr "Mercado de Taxas" + +msgid "Fee rate" +msgstr "Taxa de comissão" + +msgid "Fee-rate estimates are unavailable; transactions use a neutral color." +msgstr "As estimativas das taxas de comissão estão indisponíveis; as transações utilizam uma cor neutra." + +msgid "Gigahashes per second" +msgstr "Gigahashes por segundo" + +msgid "HIGH" +msgstr "ALTA" + +msgid "Hashes per second" +msgstr "Hashes por segundo" + +msgid "Hashrate" +msgstr "Taxa de Hash" + +msgid "High" +msgstr "Alta" + +msgid "High-Value Assets" +msgstr "Ativos de Alto Valor" + +msgid "How busy mempool activity is. More congestion means higher fees for quick confirmation." +msgstr "Nível de atividade da mempool. Mais congestionamento significa taxas mais altas para uma confirmação rápida." + +msgid "How full this block is." +msgstr "Nível de preenchimento deste bloco." + +msgid "How hard it is to find a valid block. Tracks hashrate." +msgstr "Dificuldade de encontrar um bloco válido. Acompanha a taxa de hash." + +msgid "How hard it is to mine new blocks. Bitcoin retargets mining difficulty every 2,016 blocks to keep blocks near 10 minutes. Current is the projected next change; Previous was the last change." +msgstr "Dificuldade de minerar novos blocos. A rede Bitcoin reajusta a dificuldade de mineração a cada 2.016 blocos para manter o intervalo entre blocos próximo de 10 minutos. Atual é a próxima alteração projetada; Anterior foi a última alteração." + +msgid "IN MEMPOOL" +msgstr "NA MEMPOOL" + +msgid "Individually rendered transactions" +msgstr "Transações apresentadas individualmente" + +msgid "Kilohashes per second" +msgstr "Kilohashes por segundo" + +msgid "LOW" +msgstr "BAIXA" + +msgid "Last change N/A" +msgstr "Última alteração N/D" + +msgid "Last change on %s" +msgstr "Última alteração em %0" + +msgid "Latest" +msgstr "Mais Recente" + +msgid "Latest Blocks" +msgstr "Blocos Mais Recentes" + +msgid "Latest Transactions" +msgstr "Transações Mais Recentes" + +msgid "Legacy" +msgstr "Legado" + +msgid "Live" +msgstr "Em Direto" + +msgid "Loading block utilization" +msgstr "A carregar utilização do bloco" + +msgid "Loading pending block transactions" +msgstr "A carregar transações do bloco pendente" + +msgid "Low" +msgstr "Baixa" + +msgid "Lower-fee transactions are summarized in the metrics because they do not fit at this resolution." +msgstr "As transações com taxas mais baixas são resumidas nas métricas porque não cabem nesta resolução." + +msgid "Medium" +msgstr "Média" + +msgid "Megahashes per second" +msgstr "Megahashes por segundo" + +msgid "Mempool Congestion" +msgstr "Congestionamento da Mempool" + +msgid "Moderate" +msgstr "Moderada" + +msgid "N/A" +msgstr "N/D" + +msgid "Next Block" +msgstr "Próximo Bloco" + +msgid "Next adj. in %s" +msgstr "Próx. ajuste em %0" + +msgid "Next adjustment unavailable" +msgstr "Próximo ajuste indisponível" + +msgid "No recent blocks" +msgstr "Sem blocos recentes" + +msgid "Overview" +msgstr "Visão Geral" + +msgid "PEG-IN" +msgstr "PEG-IN" + +msgid "PEG-OUT" +msgstr "PEG-OUT" + +msgid "PREVIOUS ADJ" +msgstr "AJUSTE ANTERIOR" + +msgid "Peg Information" +msgstr "Informação de Peg" + +msgid "Peg data is currently unavailable." +msgstr "Os dados de peg estão atualmente indisponíveis." + +msgid "Peg-in" +msgstr "Peg-in" + +msgid "Pending Transactions" +msgstr "Transações Pendentes" + +msgid "Pending block transaction grid" +msgstr "Grelha de transações do bloco pendente" + +msgid "Petahashes per second" +msgstr "Petahashes por segundo" + +msgid "Proof of Reserves" +msgstr "Prova de Reservas" + +msgid "Recent Peg-Ins/Outs" +msgstr "Peg-Ins/Outs Recentes" + +msgid "Recommended Fee" +msgstr "Taxa Recomendada" + +msgid "SIZE" +msgstr "TAMANHO" + +msgid "See More" +msgstr "Ver Mais" + +msgid "SegWit" +msgstr "SegWit" + +msgid "Share of selected transactions using SegWit or legacy serialization." +msgstr "Percentagem das transações selecionadas que utilizam serialização SegWit ou legada." + +msgid "Suggested rate (sat/vB) to confirm in the next block or two." +msgstr "Taxa sugerida (sat/vB) para confirmação no próximo bloco ou nos próximos dois blocos." + +msgid "TOTAL FEE COLLECTED" +msgstr "TOTAL DE TAXAS COBRADAS" + +msgid "TRANSACTION ID" +msgstr "ID DA TRANSAÇÃO" + +msgid "TRANSACTIONS" +msgstr "TRANSAÇÕES" + +msgid "TX ID" +msgstr "ID DA TX" + +msgid "TYPE" +msgstr "TIPO" + +msgid "Terahashes per second" +msgstr "Terahashes por segundo" + +msgid "This panel shows the circulating value of high-value assets on Liquid." +msgstr "Este painel apresenta o valor em circulação dos ativos de alto valor na Liquid." + +msgid "Time Since Last Block" +msgstr "Tempo Desde o Último Bloco" + +msgid "Total Fees Collected" +msgstr "Total de Taxas Cobradas" + +msgid "Total transaction fees a miner would collect from the current template, shown in bitcoin and US dollars." +msgstr "Total de taxas de transação que um mineiro receberia do modelo atual, apresentado em bitcoin e dólares dos EUA." + +msgid "Transaction Types" +msgstr "Tipos de Transação" + +msgid "Transactions currently selected for the pending block, including the coinbase transaction." +msgstr "Transações atualmente selecionadas para o bloco pendente, incluindo a transação coinbase." + +msgid "Transactions currently waiting in the node's mempool." +msgstr "Transações atualmente à espera na mempool do nó." + +msgid "Unable to refresh — showing previous data." +msgstr "Não foi possível atualizar — a apresentar os dados anteriores." + +msgid "Unknown" +msgstr "Desconhecido" + +msgid "Use the arrow keys to inspect transactions and Enter to open one." +msgstr "Utilize as teclas de seta para inspecionar transações e Enter para abrir uma." + +msgid "VALUE" +msgstr "VALOR" + +msgid "VOLUME IN" +msgstr "VOLUME DE ENTRADA" + +msgid "VOLUME OUT" +msgstr "VOLUME DE SAÍDA" + +msgid "View block %s" +msgstr "Ver bloco %0" + +msgid "Weight" +msgstr "Peso" + +msgid "Yottahashes per second" +msgstr "Yottahashes por segundo" + +msgid "Zettahashes per second" +msgstr "Zettahashes por segundo" + +msgid "am" +msgstr "a.m." + +msgid "d" +msgstr "d" + +msgid "h" +msgstr "h" + +msgid "just now" +msgstr "agora mesmo" + +msgid "m" +msgstr "min" + +msgid "mo" +msgstr "mês" + +msgid "pm" +msgstr "p.m." + +msgid "s" +msgstr "s" + +msgid "w" +msgstr "sem" + +msgid "y" +msgstr "a" + +msgid "~%s min" +msgstr "~%0 min" + +msgid "%s AGO" +msgstr "HÁ %0" + +msgid "< 1 MINUTE" +msgstr "< 1 MINUTO" + +msgid "DAY" +msgstr "DIA" + +msgid "DAYS" +msgstr "DIAS" + +msgid "HOUR" +msgstr "HORA" + +msgid "HOURS" +msgstr "HORAS" + +msgid "MINUTE" +msgstr "MINUTO" + +msgid "MINUTES" +msgstr "MINUTOS" + +msgid "MONTH" +msgstr "MÊS" + +msgid "MONTHS" +msgstr "MESES" + +msgid "YEAR" +msgstr "ANO" + +msgid "YEARS" +msgstr "ANOS" diff --git a/lang/strings.txt b/lang/strings.txt index ade01bcdf..e02cc44f2 100644 --- a/lang/strings.txt +++ b/lang/strings.txt @@ -229,3 +229,118 @@ Yes < 1 MINUTE PAST EXPECTED INTERVAL < 1 min ~%s min +%s SELECTED + COINBASE +%s added since the last update +%s days ago +%s hours ago +%s minutes ago +%s removed since the last update +%s seconds ago +1 day ago +1 hour ago +1 minute ago +< 1m +AVERAGE BLOCK TIME +AVG FEE +All selected transactions are shown individually. +Average fee rate and transaction fee in the high-fee portion of this template. +Average fee rate and transaction fee in the low-fee portion of this template. +Average fee rate and transaction fee in the middle-fee portion of this template. +BLOCK #%s +Bitcoin +Bitcoin price line chart +Block is %s% full +Block utilization unavailable +Blocks History +Copy block number %s +Copy transaction id %s +Current template weight and serialized size compared with their block limits. +Difficulty +Difficulty Adjustment +EXPECTED ADJ +EXPECTED ADJ DATE +Elapsed time since the last block confirmed. +Elapsed time since the last block confirmed. Bitcoin targets one every ~10 minutes. +Elapsed time since the last block confirmed. Liquid targets one every ~1 minute. +Estimated computing power securing the network. +Exahashes per second +FEE +Fee rate +Fee-rate estimates are unavailable; transactions use a neutral color. +Gigahashes per second +HIGH +Hashes per second +Hashrate +High-Value Assets +How busy mempool activity is. More congestion means higher fees for quick confirmation. +How full this block is. +How hard it is to find a valid block. Tracks hashrate. +How hard it is to mine new blocks. Bitcoin retargets mining difficulty every 2,016 blocks to keep blocks near 10 minutes. Current is the projected next change; Previous was the last change. +IN MEMPOOL +Individually rendered transactions +Kilohashes per second +LOW +Latest +Latest Blocks +Latest Transactions +Legacy +Live +Loading block utilization +Loading pending block transactions +Lower-fee transactions are summarized in the metrics because they do not fit at this resolution. +Medium +Megahashes per second +Mempool Congestion +Moderate +Next Block +Next adj. in %s +Next adjustment unavailable +No recent blocks +Overview +PREVIOUS ADJ +Pending Transactions +Pending block transaction grid +Petahashes per second +Recommended Fee +SIZE +SegWit +Share of selected transactions using SegWit or legacy serialization. +Suggested rate (sat/vB) to confirm in the next block or two. +TOTAL FEE COLLECTED +TRANSACTION ID +TRANSACTIONS +TX ID +Terahashes per second +This panel shows the circulating value of high-value assets on Liquid. +Total Fees Collected +Total transaction fees a miner would collect from the current template, shown in bitcoin and US dollars. +Transaction Types +Transactions currently selected for the pending block, including the coinbase transaction. +Transactions currently waiting in the node's mempool. +Use the arrow keys to inspect transactions and Enter to open one. +VALUE +View block %s +Yottahashes per second +Zettahashes per second +am +d +h +just now +m +mo +pm +s +w +y +%s AGO +< 1 MINUTE +DAY +DAYS +HOUR +HOURS +MINUTE +MINUTES +MONTH +MONTHS +YEAR +YEARS diff --git a/test/dashboard-localization.test.js b/test/dashboard-localization.test.js new file mode 100644 index 000000000..3bdee741f --- /dev/null +++ b/test/dashboard-localization.test.js @@ -0,0 +1,140 @@ +const test = require("node:test"); +const assert = require("node:assert/strict"); +const render = require("snabbdom-to-html"); + +const l10n = require("../client/src/l10n").default; +const { blks } = require("../client/src/views/blocks"); +const { + ElapsedTime, + formatDuration, +} = require("../client/src/components/elapsed-time"); +const { + highValueAssets, +} = require("../client/src/components/high-value-assets"); +const difficultyAdjustment = + require("../client/src/views/difficulty-adjustment").default; +const { overview } = require("../client/src/views/overview"); +const { transactions } = require("../client/src/views/transactions"); +const portuguese = require("../lang/pt-pt.json"); + +const t = l10n["pt-pt"]; + +test("localizes overview and recent block copy in Portuguese", () => { + const block = { + height: 123, + id: "block-id", + size: 1_000_000, + timestamp: Math.floor(Date.now() / 1000), + tx_count: 456, + weight: 2_000_000, + }; + const overviewHtml = render(overview({ + blocks: [block], + mempool: { vsize: 0 }, + t, + })); + const blocksHtml = render(blks([block], false, { t })); + + assert.match(overviewHtml, /Visão Geral/); + assert.match(overviewHtml, /Tempo Desde o Último Bloco/); + assert.match(overviewHtml, /BLOCO #123/); + assert.match( + overviewHtml, + /aria-label="Gráfico de linhas do preço do Bitcoin"/, + ); + assert.match(blocksHtml, /Blocos Mais Recentes/); + assert.match(blocksHtml, /Mais Recente/); + assert.match(blocksHtml, /AGORA MESMO/); + assert.match(blocksHtml, /TRANSAÇÕES/); + assert.match(blocksHtml, /TAMANHO/); + assert.match(blocksHtml, /aria-label="Ver bloco 123"/); + assert.match(blocksHtml, /aria-label="Copiar número do bloco 123"/); +}); + +test("localizes recent transaction and difficulty copy in Portuguese", () => { + const txid = "a".repeat(64); + const transactionsHtml = render(transactions([{ + fee: 100, + txid, + value: 100_000_000, + vsize: 100, + }], false, { t })); + const difficultyHtml = render(difficultyAdjustment({ blocks: [], t })); + + assert.match(transactionsHtml, /Transações Mais Recentes/); + assert.match(transactionsHtml, /ID DA TRANSAÇÃO/); + assert.match(transactionsHtml, /VALOR/); + assert.match(transactionsHtml, /TAMANHO/); + assert.match(transactionsHtml, /TAXA/); + assert.match( + transactionsHtml, + new RegExp(`aria-label="Copiar ID da transação ${txid}"`), + ); + assert.match(difficultyHtml, /Ajuste de Dificuldade/); + assert.match(difficultyHtml, /TEMPO MÉDIO DO BLOCO/); + assert.match(difficultyHtml, /AJUSTE ESPERADO/); + assert.match(difficultyHtml, /AJUSTE ANTERIOR/); + assert.match(difficultyHtml, /DATA DO AJUSTE ESPERADO/); + assert.match(difficultyHtml, /Taxa de Hash/); + assert.match(difficultyHtml, /Dificuldade/); + assert.match(difficultyHtml, /Próximo ajuste indisponível/); + assert.match(difficultyHtml, /N\/D/); +}); + +test("localizes elapsed times and formatter-owned fallbacks", () => { + const oneYear = 365 * 24 * 60 * 60 * 1000; + const elapsedHtml = render(ElapsedTime({ + timestamp: Date.now() - 90 * 1000, + t, + })); + const highValueAssetsHtml = render(highValueAssets(t)); + + assert.equal(formatDuration(0, false, t), "< 1 MINUTO"); + assert.equal(formatDuration(oneYear, false, t), "1 ANO"); + assert.match(elapsedHtml, /HÁ 1 MINUTO/); + assert.match(highValueAssetsHtml, /N\/D/); +}); + +test("preserves all-caps dashboard label casing in Portuguese", () => { + [ + "AMOUNT", + "AVERAGE BLOCK TIME", + "AVG FEE", + "%s AGO", + "BLOCK", + "BLOCK #%s", + "BLOCK FILLING", + "DAY", + "DAYS", + "EXPECTED ADJ", + "EXPECTED ADJ DATE", + "FEE", + "HIGH", + "HOUR", + "HOURS", + "IN MEMPOOL", + "LOW", + "MINUTE", + "MINUTES", + "MONTH", + "MONTHS", + "PEG-IN", + "PEG-OUT", + "PREVIOUS ADJ", + "SIZE", + "TOTAL FEE COLLECTED", + "TRANSACTION ID", + "TRANSACTIONS", + "TX ID", + "TXID", + "TYPE", + "VALUE", + "VOLUME IN", + "VOLUME OUT", + "YEAR", + "YEARS", + ].forEach((key) => { + const translated = portuguese[key] || key; + assert.equal(translated, translated.toUpperCase(), key); + }); +}); diff --git a/www/style.css b/www/style.css index cedc867b6..9f12b75d5 100644 --- a/www/style.css +++ b/www/style.css @@ -3868,10 +3868,12 @@ a.back-link img{ justify-content: center; background-color: rgba(23, 201, 100, 0.2); color: rgba(23, 201, 100); - width: 50px; + width: fit-content; height: 18px; + padding: 0 8px; font-weight: 400; font-size: 11px; + white-space: nowrap; border-radius: 18px; }