diff --git a/docs/features/localization.md b/docs/features/localization.md index 4ca66a1497a..0f3c3de88ec 100644 --- a/docs/features/localization.md +++ b/docs/features/localization.md @@ -19,6 +19,7 @@ _Do not manually edit language json files in `suite-data/files/translations/` di - `id`: We don't have strict conventions for generating these IDs, although using a prefix `TR_`, or expanded variant `TR_`, where scope is, for example, "ONBOARDING" is really handy. ID must be the same as the object's key. - `defaultMessage`: Used as a source string for translator. It's also a text that is shown in the app as a fallback till someone changes/improves it in Crowdin. - `description`: Optional. Useful for describing the context in which the message occurs, especially if it is not clear from a `defaultMessage` field. +- `dynamic`: Optional. Must be set to true for programmatically constructed keys. Otherwise, the keys will be deleted by the list-duplicates script. Example: diff --git a/packages/suite-desktop-ui/src/support/DesktopUpdater/Available.tsx b/packages/suite-desktop-ui/src/support/DesktopUpdater/Available.tsx index 206eda0a7c7..8837ab00e8e 100644 --- a/packages/suite-desktop-ui/src/support/DesktopUpdater/Available.tsx +++ b/packages/suite-desktop-ui/src/support/DesktopUpdater/Available.tsx @@ -89,7 +89,7 @@ export const Available = ({ onCancel, latest }: AvailableProps) => {
diff --git a/packages/suite-desktop-ui/src/support/DesktopUpdater/JustUpdated.tsx b/packages/suite-desktop-ui/src/support/DesktopUpdater/JustUpdated.tsx index cf3d6ae449f..7aeaf5f0af0 100644 --- a/packages/suite-desktop-ui/src/support/DesktopUpdater/JustUpdated.tsx +++ b/packages/suite-desktop-ui/src/support/DesktopUpdater/JustUpdated.tsx @@ -41,7 +41,7 @@ export const JustUpdated = ({ onCancel }: AvailableProps) => { heading={ diff --git a/packages/suite/src/components/wallet/TransactionItem/InstantStakeBadge.tsx b/packages/suite/src/components/wallet/TransactionItem/InstantStakeBadge.tsx index f009af1c3c3..49963109dd7 100644 --- a/packages/suite/src/components/wallet/TransactionItem/InstantStakeBadge.tsx +++ b/packages/suite/src/components/wallet/TransactionItem/InstantStakeBadge.tsx @@ -21,9 +21,9 @@ const Wrapper = styled.div` const getTranslationId = (instantStakeType: StakeType) => { switch (instantStakeType) { case 'stake': - return 'TR_STAKING_INSTANT_STAKING'; + return 'TR_INSTANT_STAKING'; case 'unstake': - return 'TR_STAKING_INSTANT_UNSTAKING'; + return 'TR_INSTANT_UNSTAKING'; default: return null; // there is no badge for claiming } diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index 02c185ab46d..0d5e7e2684b 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -1,6 +1,24 @@ import { defineMessages } from 'react-intl'; -export default defineMessages({ +type MessageDescriptor = { + // Must correspond to the property name. + id: K; + // Default text in English. This value is only used directly if the corresponding key is missing from the JSON translation files. + defaultMessage: string; + // Not integrated into Crowdin so not really used. + description?: string; + // Must be set to true for programmatically constructed keys. Otherwise, the keys will be deleted by the list-duplicates script. + dynamic?: boolean; +}; + +/** +Checks whether id corresponds to the property name. Otherwise, text is not translated. Returns the same value that was passed in. + */ +const defineMessagesWithTypeCheck = (messages: { + [K in Key]: MessageDescriptor; +}) => messages; + +const messages = defineMessagesWithTypeCheck({ TR_404_DESCRIPTION: { defaultMessage: 'Looks like a wrong URL or broken link.', id: 'TR_404_DESCRIPTION', @@ -170,7 +188,7 @@ export default defineMessages({ id: 'TR_XPUB', }, TR_ADDRESS: { - id: 'TR_ADDRESSES', + id: 'TR_ADDRESS', defaultMessage: 'Address', }, TR_ADDRESSES_FRESH: { @@ -971,11 +989,11 @@ export default defineMessages({ id: 'TR_BUY_MODAL_TERMS_6', dynamic: true, }, - TR_VALIDATION_ERROR_MINIMUM_CRYPTO: { + TR_BUY_VALIDATION_ERROR_MINIMUM_CRYPTO: { defaultMessage: 'Minimum is {minimum}', id: 'TR_BUY_VALIDATION_ERROR_MINIMUM_CRYPTO', }, - TR_VALIDATION_ERROR_MAXIMUM_CRYPTO: { + TR_BUY_VALIDATION_ERROR_MAXIMUM_CRYPTO: { defaultMessage: 'Maximum is {maximum}', id: 'TR_BUY_VALIDATION_ERROR_MAXIMUM_CRYPTO', }, @@ -3558,12 +3576,12 @@ export default defineMessages({ defaultMessage: 'The current and most widely accepted method of generating and managing Solana addresses ensures interoperability, security, and support for SOL and SPL tokens.', }, - TR_ACCOUNT_TYPE_NORMAL_CARDANO_DESC: { + TR_ACCOUNT_TYPE_CARDANO_DESC: { id: 'TR_ACCOUNT_TYPE_CARDANO_DESC', defaultMessage: 'The current and most widely accepted method of generating and managing Cardano addresses ensures interoperability, security, and support for all types of tokens.', }, - TR_ACCOUNT_TYPE_NORMAL_XRP_DESC: { + TR_ACCOUNT_TYPE_XRP_DESC: { id: 'TR_ACCOUNT_TYPE_XRP_DESC', defaultMessage: 'XRP is a digital currency that enables fast, low-cost cross-border payments without relying on traditional mining, using a consensus ledger for quick transaction confirmations.', @@ -3668,7 +3686,7 @@ export default defineMessages({ }, TOAST_COIN_SCHEME_PROTOCOL: { id: 'TOAST_COIN_SCHEME_PROTOCOL', - describe: 'Required for current notifications. Do not change.', + description: 'Required for current notifications. Do not change.', defaultMessage: '{header}{body}', }, TOAST_COIN_SCHEME_PROTOCOL_ACTION: { @@ -4149,7 +4167,7 @@ export default defineMessages({ dynamic: true, }, TR_CHECK_RECOVERY_SEED_DESC_T3B1: { - id: 'TR_CHECK_RECOVERY_SEED_DESC_T2B1', + id: 'TR_CHECK_RECOVERY_SEED_DESC_T3B1', defaultMessage: "Use the two-button pad to enter your wallet backup. By doing this, you're keeping all your sensitive info safe and sound, away from any shady or insecure computer or web browser.", dynamic: true, @@ -4665,7 +4683,7 @@ export default defineMessages({ TR_DISABLE_WEBUSB_TRY_BRIDGE: { id: 'TR_DISABLE_WEBUSB_TRY_BRIDGE', defaultMessage: 'Disable WebUSB and use Bridge', - describe: + description: 'Bridge is a communication deamon that some users will need to download and install. So word bridge should not be translated.', }, TR_YOUR_DEVICE_IS_SEEDLESS: { @@ -5174,7 +5192,7 @@ export default defineMessages({ id: 'IMAGE_VALIDATION_ERROR_INVALID_DIMENSIONS', defaultMessage: 'Invalid dimensions (Image must be {width} x {height} px)', }, - IMAGE_VALIDATION_ERROR_INVALID_SIZE: { + IMAGE_VALIDATION_ERROR_INVALID_SIZE_JPG: { id: 'IMAGE_VALIDATION_ERROR_INVALID_SIZE_JPG', defaultMessage: 'Invalid size (Image must be less than 16KB)', }, @@ -6200,7 +6218,7 @@ export default defineMessages({ id: 'TR_MANAGE', defaultMessage: 'manage', }, - TR_VERSION_HAS_RELEASED: { + TR_VERSION_HAS_BEEN_RELEASED: { id: 'TR_VERSION_HAS_BEEN_RELEASED', defaultMessage: 'v{version} has released!', }, @@ -6824,7 +6842,7 @@ export default defineMessages({ defaultMessage: "Contact Trezor Support to figure out what's going on with your device and what to do next.", }, - TR_DEVICE_COMPROMISED_HEADING_SOFT: { + TR_PLAY_IT_SAFE: { id: 'TR_PLAY_IT_SAFE', defaultMessage: "Let's play it safe", }, @@ -7051,7 +7069,7 @@ export default defineMessages({ TR_ONBOARDING_TROUBLESHOOTING_FAILED: { id: 'TR_ONBOARDING_TROUBLESHOOTING_FAILED', defaultMessage: 'Still not working?', - decription: + description: "If troubleshooting steps for connecting a device in Onboarding didn't do the trick there is at the end link to contact a support", }, TR_STILL_DONT_SEE_YOUR_TREZOR: { @@ -7380,7 +7398,7 @@ export default defineMessages({ id: 'TR_STAKING_IS_NOT_SUPPORTED', defaultMessage: 'Staking is not supported on this network.', }, - TR_STAKING_INSTANT_STAKING: { + TR_INSTANT_STAKING: { id: 'TR_INSTANT_STAKING', defaultMessage: 'Staked instantly', }, @@ -7392,7 +7410,7 @@ export default defineMessages({ id: 'TR_STAKING_AMOUNT_UNSTAKED_INSTANTLY', defaultMessage: '{amount} {symbol} unstaked instantly!', }, - TR_STAKING_INSTANT_UNSTAKING: { + TR_INSTANT_UNSTAKING: { id: 'TR_INSTANT_UNSTAKING', defaultMessage: 'Unstaked instantly', }, @@ -7401,7 +7419,7 @@ export default defineMessages({ defaultMessage: "You've instantly staked {amount} {symbol}. {days, plural, =0 {} one {The remaining {symbol} will be staked within # day.} other { The remaining {symbol} will be staked within # days}}", }, - TR_STAKING_INSTANTLY_UNSTAKED: { + TR_STAKE_INSTANTLY_UNSTAKED_WITH_DAYS: { id: 'TR_STAKE_INSTANTLY_UNSTAKED_WITH_DAYS', defaultMessage: 'You received {amount} {symbol} "Instantly". {days, plural, =0 {} one {The rest will be payed out within # day.} other { The rest will be payed out within # days}}', @@ -8362,7 +8380,7 @@ export default defineMessages({ id: 'TR_DESKTOP_APP_PROMO_GET', defaultMessage: 'Get for desktop', }, - TR_DESKTOP_APP_PROMO_TEXT_FOOTER: { + TR_MOBILE_APP_PROMO_TEXT: { id: 'TR_MOBILE_APP_PROMO_TEXT', defaultMessage: 'With more security features', }, @@ -9235,3 +9253,5 @@ export default defineMessages({ defaultMessage: 'Trezor Connect', }, }); + +export default defineMessages(messages); diff --git a/packages/suite/src/utils/suite/homescreen.ts b/packages/suite/src/utils/suite/homescreen.ts index a8b432e50b0..815d845e41e 100644 --- a/packages/suite/src/utils/suite/homescreen.ts +++ b/packages/suite/src/utils/suite/homescreen.ts @@ -30,7 +30,7 @@ export const enum ImageValidationError { InvalidFormatOnlyPngJpg = 'IMAGE_VALIDATION_ERROR_INVALID_FORMAT_ONLY_PNG_JPG', InvalidFormatOnlyJpg = 'IMAGE_VALIDATION_ERROR_INVALID_FORMAT_ONLY_JPG', InvalidDimensions = 'IMAGE_VALIDATION_ERROR_INVALID_DIMENSIONS', - InvalidSize = 'IMAGE_VALIDATION_ERROR_INVALID_SIZE', + InvalidSize = 'IMAGE_VALIDATION_ERROR_INVALID_SIZE_JPG', ProgressiveJpgFormat = 'IMAGE_VALIDATION_ERROR_PROGRESSIVE_JPG', UnexpectedAlpha = 'IMAGE_VALIDATION_ERROR_UNEXPECTED_ALPHA', InvalidColorCombination = 'IMAGE_VALIDATION_ERROR_INVALID_COLOR_COMBINATION', diff --git a/packages/suite/src/utils/suite/validation.ts b/packages/suite/src/utils/suite/validation.ts index f44859140d1..b75f30d4fda 100644 --- a/packages/suite/src/utils/suite/validation.ts +++ b/packages/suite/src/utils/suite/validation.ts @@ -63,7 +63,7 @@ export const validateLimits = : amountLimits.minCrypto; } if (amountLimits.minCrypto && Number(value) < minCrypto) { - return translationString('TR_VALIDATION_ERROR_MINIMUM_CRYPTO', { + return translationString('TR_BUY_VALIDATION_ERROR_MINIMUM_CRYPTO', { minimum: formatter.format(amountLimits.minCrypto.toString(), { isBalance: true, symbol, @@ -78,7 +78,7 @@ export const validateLimits = : amountLimits.maxCrypto; } if (amountLimits.maxCrypto && Number(value) > maxCrypto) { - return translationString('TR_VALIDATION_ERROR_MAXIMUM_CRYPTO', { + return translationString('TR_BUY_VALIDATION_ERROR_MAXIMUM_CRYPTO', { maximum: formatter.format(amountLimits.maxCrypto.toString(), { isBalance: true, symbol, @@ -111,7 +111,7 @@ export const validateLimitsBigNum = : new BigNumber(amountLimits.minCrypto); } if (amountLimits.minCrypto && new BigNumber(value).lt(minCrypto)) { - return translationString('TR_VALIDATION_ERROR_MINIMUM_CRYPTO', { + return translationString('TR_BUY_VALIDATION_ERROR_MINIMUM_CRYPTO', { minimum: formatter.format(amountLimits.minCrypto.toString(), { isBalance: true, symbol, @@ -128,7 +128,7 @@ export const validateLimitsBigNum = : new BigNumber(amountLimits.maxCrypto); } if (amountLimits.maxCrypto && new BigNumber(value).gt(maxCrypto)) { - return translationString('TR_VALIDATION_ERROR_MAXIMUM_CRYPTO', { + return translationString('TR_BUY_VALIDATION_ERROR_MAXIMUM_CRYPTO', { maximum: formatter.format(amountLimits.maxCrypto.toString(), { isBalance: true, symbol, diff --git a/packages/suite/src/views/dashboard/PromoBanner.tsx b/packages/suite/src/views/dashboard/PromoBanner.tsx index a0efd35c073..cef26a9c845 100644 --- a/packages/suite/src/views/dashboard/PromoBanner.tsx +++ b/packages/suite/src/views/dashboard/PromoBanner.tsx @@ -212,7 +212,7 @@ export const PromoBanner = () => {
- + diff --git a/packages/suite/src/views/onboarding/steps/SecurityCheck/SecurityCheck.tsx b/packages/suite/src/views/onboarding/steps/SecurityCheck/SecurityCheck.tsx index 76b7cca8608..cbe43bfb760 100644 --- a/packages/suite/src/views/onboarding/steps/SecurityCheck/SecurityCheck.tsx +++ b/packages/suite/src/views/onboarding/steps/SecurityCheck/SecurityCheck.tsx @@ -209,7 +209,7 @@ const SecurityCheckContent = ({ return isFailed ? ( diff --git a/packages/suite/src/views/wallet/staking/components/EthStakingDashboard/components/InstantStakeBanner.tsx b/packages/suite/src/views/wallet/staking/components/EthStakingDashboard/components/InstantStakeBanner.tsx index 608ef67116a..b6d6486eedd 100644 --- a/packages/suite/src/views/wallet/staking/components/EthStakingDashboard/components/InstantStakeBanner.tsx +++ b/packages/suite/src/views/wallet/staking/components/EthStakingDashboard/components/InstantStakeBanner.tsx @@ -23,7 +23,7 @@ const IconWrapper = styled.div` const getSubheadingTranslationId = (stakeType: StakeType) => { if (stakeType === 'stake') return 'TR_STAKING_INSTANTLY_STAKED'; - return 'TR_STAKING_INSTANTLY_UNSTAKED'; + return 'TR_STAKE_INSTANTLY_UNSTAKED_WITH_DAYS'; }; const getHeadingTranslationId = (stakeType: StakeType) => { diff --git a/suite-common/wallet-utils/src/accountUtils.ts b/suite-common/wallet-utils/src/accountUtils.ts index bce45385500..64e58943e96 100644 --- a/suite-common/wallet-utils/src/accountUtils.ts +++ b/suite-common/wallet-utils/src/accountUtils.ts @@ -304,9 +304,9 @@ export const getAccountTypeDesc = ({ path, accountType, networkType }: getAccoun case 'solana': return 'TR_ACCOUNT_TYPE_NORMAL_SOLANA_DESC'; case 'cardano': - return 'TR_ACCOUNT_TYPE_NORMAL_CARDANO_DESC'; + return 'TR_ACCOUNT_TYPE_CARDANO_DESC'; case 'ripple': - return 'TR_ACCOUNT_TYPE_NORMAL_XRP_DESC'; + return 'TR_ACCOUNT_TYPE_XRP_DESC'; } const accountTypePrefix = getAccountTypePrefix(path);