diff --git a/CHANGELOG.md b/CHANGELOG.md index 435751ac12..c32f89e39e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,15 @@ way to update this template, but currently, we follow a pattern: ## Upcoming version 2022-XX-XX +- [add] Add support for hosted translations. + + - This PR fetches "content/translation.json" from a new Asset Delivery API. The file is editable + through the Flex Console. + - It also adds all the missing translation keys to existing non-English translation files. This + means that those files might now include messages in English. + + [#1510](https://github.com/sharetribe/ftw-daily/pull/1510) + - [delete] Remove old unused translation keys. [#1511](https://github.com/sharetribe/ftw-daily/pull/1511) diff --git a/package.json b/package.json index aa4f5b4f52..dd41f90922 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "redux": "^4.1.2", "redux-thunk": "^2.4.1", "seedrandom": "^3.0.5", - "sharetribe-flex-sdk": "^1.15.0", + "sharetribe-flex-sdk": "^1.17.0", "sharetribe-scripts": "5.0.1", "smoothscroll-polyfill": "^0.4.0", "source-map-support": "^0.5.21", diff --git a/server/csp.js b/server/csp.js index 07d53de1da..5ae6345a3e 100644 --- a/server/csp.js +++ b/server/csp.js @@ -8,6 +8,10 @@ const data = 'data:'; const blob = 'blob:'; const devImagesMaybe = dev ? ['*.localhost:8000'] : []; const baseUrl = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL || 'https://flex-api.sharetribe.com'; +// Asset Delivery API is using a different domain than other Flex APIs +// cdn.st-api.com +// If assetCdnBaseUrl is used to initialize SDK (for proxy purposes), then that URL needs to be in CSP +const assetCdnBaseUrl = process.env.REACT_APP_SHARETRIBE_SDK_ASSET_CDN_BASE_URL; // Default CSP whitelist. // @@ -20,6 +24,8 @@ const defaultDirectives = { connectSrc: [ self, baseUrl, + assetCdnBaseUrl, + '*.st-api.com', 'maps.googleapis.com', '*.tiles.mapbox.com', 'api.mapbox.com', diff --git a/server/dataLoader.js b/server/dataLoader.js index 2c89426372..65595753fe 100644 --- a/server/dataLoader.js +++ b/server/dataLoader.js @@ -1,10 +1,12 @@ const url = require('url'); const log = require('./log'); -exports.loadData = function(requestUrl, sdk, matchPathname, configureStore, routeConfiguration) { +exports.loadData = function(requestUrl, sdk, appInfo) { + const { matchPathname, configureStore, routeConfiguration, config, fetchAppAssets } = appInfo; const { pathname, query } = url.parse(requestUrl); const matchedRoutes = matchPathname(pathname, routeConfiguration()); + let translations = {}; const store = configureStore({}, sdk); const dataLoadingCalls = matchedRoutes.reduce((calls, match) => { @@ -15,9 +17,18 @@ exports.loadData = function(requestUrl, sdk, matchPathname, configureStore, rout return calls; }, []); - return Promise.all(dataLoadingCalls) + // First fetch app-wide assets + // Then make loadData calls + // And return object containing preloaded state and translations + // This order supports other asset (in the future) that should be fetched before data calls. + return store + .dispatch(fetchAppAssets(config.appCdnAssets)) + .then(fetchedAssets => { + translations = fetchedAssets?.translations?.data || {}; + return Promise.all(dataLoadingCalls); + }) .then(() => { - return store.getState(); + return { preloadedState: store.getState(), translations }; }) .catch(e => { log.error(e, 'server-side-data-load-failed'); diff --git a/server/index.js b/server/index.js index a41c96e376..f0c59210d3 100644 --- a/server/index.js +++ b/server/index.js @@ -49,6 +49,7 @@ const dev = process.env.REACT_APP_ENV === 'development'; const PORT = parseInt(process.env.PORT, 10); const CLIENT_ID = process.env.REACT_APP_SHARETRIBE_SDK_CLIENT_ID; const BASE_URL = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL; +const ASSET_CDN_BASE_URL = process.env.REACT_APP_SHARETRIBE_SDK_ASSET_CDN_BASE_URL; const TRANSIT_VERBOSE = process.env.REACT_APP_SHARETRIBE_SDK_TRANSIT_VERBOSE === 'true'; const USING_SSL = process.env.REACT_APP_SHARETRIBE_USING_SSL === 'true'; const TRUST_PROXY = process.env.SERVER_SHARETRIBE_TRUST_PROXY || null; @@ -199,6 +200,7 @@ app.get('*', (req, res) => { }); const baseUrl = BASE_URL ? { baseUrl: BASE_URL } : {}; + const assetCdnBaseUrl = ASSET_CDN_BASE_URL ? { assetCdnBaseUrl: ASSET_CDN_BASE_URL } : {}; const sdk = sharetribeSdk.createInstance({ transitVerbose: TRANSIT_VERBOSE, @@ -208,6 +210,7 @@ app.get('*', (req, res) => { tokenStore, typeHandlers: sdkUtils.typeHandlers, ...baseUrl, + ...assetCdnBaseUrl, }); // Until we have a better plan for caching dynamic content and we @@ -221,12 +224,12 @@ app.get('*', (req, res) => { // Server-side entrypoint provides us the functions for server-side data loading and rendering const nodeEntrypoint = nodeExtractor.requireEntrypoint(); - const { default: renderApp, matchPathname, configureStore, routeConfiguration } = nodeEntrypoint; + const { default: renderApp, ...appInfo } = nodeEntrypoint; dataLoader - .loadData(req.url, sdk, matchPathname, configureStore, routeConfiguration) - .then(preloadedState => { - const html = renderer.render(req.url, context, preloadedState, renderApp, webExtractor); + .loadData(req.url, sdk, appInfo) + .then(data => { + const html = renderer.render(req.url, context, data, renderApp, webExtractor); if (dev) { const debugData = { diff --git a/server/renderer.js b/server/renderer.js index 6659bab9a0..db2528d5b8 100644 --- a/server/renderer.js +++ b/server/renderer.js @@ -90,11 +90,20 @@ const replacer = (key = null, value) => { return types.replacer(key, cleanedValue); }; -exports.render = function(requestUrl, context, preloadedState, renderApp, webExtractor) { +exports.render = function(requestUrl, context, data, renderApp, webExtractor) { + const { preloadedState, translations } = data; + // Bind webExtractor as "this" for collectChunks call. const collectWebChunks = webExtractor.collectChunks.bind(webExtractor); - const { head, body } = renderApp(requestUrl, context, preloadedState, collectWebChunks); + // Render the app with given route, preloaded state, hosted translations. + const { head, body } = renderApp( + requestUrl, + context, + preloadedState, + translations, + collectWebChunks + ); // Preloaded state needs to be passed for client side too. // For security reasons we ensure that preloaded state is considered as a string diff --git a/src/app.js b/src/app.js index 36f2476630..0e989fda63 100644 --- a/src/app.js +++ b/src/app.js @@ -18,10 +18,12 @@ import routeConfiguration from './routeConfiguration'; import Routes from './Routes'; import config from './config'; -// Flex template application uses English translations as default. +// Flex template application uses English translations as default translations. import defaultMessages from './translations/en.json'; -// If you want to change the language, change the imports to match the wanted locale: +// If you want to change the language of default (fallback) translations, +// change the imports to match the wanted locale: +// // 1) Change the language in the config.js file! // 2) Import correct locale rules for Moment library // 3) Use the `messagesInLocale` import to add the correct translation file. @@ -48,6 +50,11 @@ const messagesInLocale = {}; const addMissingTranslations = (sourceLangTranslations, targetLangTranslations) => { const sourceKeys = Object.keys(sourceLangTranslations); const targetKeys = Object.keys(targetLangTranslations); + + // if there's no translations defined for target language, return source translations + if (targetKeys.length === 0) { + return sourceLangTranslations; + } const missingKeys = difference(sourceKeys, targetKeys); const addMissingTranslation = (translations, missingKey) => ({ @@ -58,18 +65,15 @@ const addMissingTranslations = (sourceLangTranslations, targetLangTranslations) return missingKeys.reduce(addMissingTranslation, targetLangTranslations); }; -const isDefaultLanguageInUse = config.locale === 'en'; - -const messages = isDefaultLanguageInUse - ? defaultMessages - : addMissingTranslations(defaultMessages, messagesInLocale); - +// Get default messages for a given locale. +// +// Note: Locale should not affect the tests. We ensure this by providing +// messages with the key as the value of each message and discard the value. +// { 'My.translationKey1': 'My.translationKey1', 'My.translationKey2': 'My.translationKey2' } const isTestEnv = process.env.NODE_ENV === 'test'; - -// Locale should not affect the tests. We ensure this by providing -// messages with the key as the value of each message. -const testMessages = mapValues(messages, (val, key) => key); -const localeMessages = isTestEnv ? testMessages : messages; +const localeMessages = isTestEnv + ? mapValues(defaultMessages, (val, key) => key) + : addMissingTranslations(defaultMessages, messagesInLocale); const setupLocale = () => { if (isTestEnv) { @@ -85,10 +89,14 @@ const setupLocale = () => { }; export const ClientApp = props => { - const { store } = props; + const { store, hostedTranslations = {} } = props; setupLocale(); return ( - + @@ -105,11 +113,15 @@ const { any, string } = PropTypes; ClientApp.propTypes = { store: any.isRequired }; export const ServerApp = props => { - const { url, context, helmetContext, store } = props; + const { url, context, helmetContext, store, hostedTranslations = {} } = props; setupLocale(); HelmetProvider.canUseDOM = false; return ( - + @@ -133,7 +145,13 @@ ServerApp.propTypes = { url: string.isRequired, context: any.isRequired, store: * - {String} body: Rendered application body of the given route * - {Object} head: Application head metadata from react-helmet */ -export const renderApp = (url, serverContext, preloadedState, collectChunks) => { +export const renderApp = ( + url, + serverContext, + preloadedState, + hostedTranslations, + collectChunks +) => { // Don't pass an SDK instance since we're only rendering the // component tree with the preloaded store state and components // shouldn't do any SDK calls in the (server) rendering lifecycle. @@ -145,7 +163,13 @@ export const renderApp = (url, serverContext, preloadedState, collectChunks) => // This is needed to figure out correct chunks/scripts to be included to server-rendered page. // https://loadable-components.com/docs/server-side-rendering/#3-setup-chunkextractor-server-side const WithChunks = collectChunks( - + ); const body = ReactDOMServer.renderToString(WithChunks); const { helmet: head } = helmetContext; diff --git a/src/components/SectionHero/SectionHero.js b/src/components/SectionHero/SectionHero.js index c73374deef..5e7b312048 100644 --- a/src/components/SectionHero/SectionHero.js +++ b/src/components/SectionHero/SectionHero.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { string } from 'prop-types'; import { FormattedMessage } from '../../util/reactIntl'; import classNames from 'classnames'; @@ -7,17 +7,22 @@ import { NamedLink } from '../../components'; import css from './SectionHero.module.css'; const SectionHero = props => { + const [mounted, setMounted] = useState(false); const { rootClassName, className } = props; + useEffect(() => { + setMounted(true); + }, []); + const classes = classNames(rootClassName || css.root, className); return (
-

+

-

+

{ search: 'address=Finland&bounds=70.0922932%2C31.5870999%2C59.693623%2C20.456500199999937', }} - className={css.heroButton} + className={classNames(css.heroButton, { [css.heroButtonFEDelay]: mounted })} > diff --git a/src/components/SectionHero/SectionHero.module.css b/src/components/SectionHero/SectionHero.module.css index b297ba7bba..5de76e91bf 100644 --- a/src/components/SectionHero/SectionHero.module.css +++ b/src/components/SectionHero/SectionHero.module.css @@ -21,6 +21,7 @@ animation-duration: 0.5s; animation-timing-function: ease-out; -webkit-animation-fill-mode: forwards; + animation-delay: 3s; visibility: hidden; opacity: 1; @@ -61,38 +62,41 @@ .heroMainTitle { @apply --marketplaceHeroTitleFontStyles; color: var(--matterColorLight); - composes: animation; - animation-delay: 0.5s; @media (--viewportMedium) { max-width: var(--SectionHero_desktopTitleMaxWidth); } } +.heroMainTitleFEDelay { + animation-delay: 0s; +} .heroSubTitle { @apply --marketplaceH4FontStyles; color: var(--matterColorLight); margin: 0 0 32px 0; - composes: animation; - animation-delay: 0.65s; @media (--viewportMedium) { max-width: var(--SectionHero_desktopTitleMaxWidth); margin: 0 0 47px 0; } } +.heroSubTitleFEDelay { + animation-delay: 0.15s; +} .heroButton { @apply --marketplaceButtonStyles; composes: animation; - animation-delay: 0.8s; - @media (--viewportMedium) { display: block; width: 260px; } } +.heroButtonFEDelay { + animation-delay: 0.3s; +} diff --git a/src/components/SectionHero/__snapshots__/SectionHero.test.js.snap b/src/components/SectionHero/__snapshots__/SectionHero.test.js.snap index 231cc2afdf..278f3dc3a9 100644 --- a/src/components/SectionHero/__snapshots__/SectionHero.test.js.snap +++ b/src/components/SectionHero/__snapshots__/SectionHero.test.js.snap @@ -8,21 +8,21 @@ exports[`SectionHero matches snapshot 1`] = ` className="heroContent" >

SectionHero.title

SectionHero.subTitle

({ type: ASSETS_REQUEST }); +export const assetsSuccess = (assets, version) => ({ + type: ASSETS_SUCCESS, + payload: { assets, version }, +}); +export const assetsError = error => ({ + type: ASSETS_ERROR, + payload: error, +}); + +// ================ Thunks ================ // + +export const fetchAppAssets = (assets, version) => (dispatch, getState, sdk) => { + dispatch(assetsRequested()); + + // If version is given fetch assets by the version, + // otherwise default to "latest" alias + const fetchAssets = version + ? assetPath => sdk.assetByVersion({ path: assetPath, version }) + : assetPath => sdk.assetByAlias({ path: assetPath, alias: 'latest' }); + const assetEntries = Object.entries(assets); + const sdkAssets = assetEntries.map(([key, assetPath]) => fetchAssets(assetPath)); + + return Promise.all(sdkAssets) + .then(responses => { + const version = responses[0]?.data?.meta?.version; + dispatch(assetsSuccess(assets, version)); + + // Returned value looks like this for a single asset with name: "translations": + // { + // translations: { + // path: 'content/translations.json', // an example path in Asset Delivery API + // data, // translation key & value pairs + // }, + // } + return assetEntries.reduce((collectedAssets, assetEntry, i) => { + const [name, path] = assetEntry; + return { ...collectedAssets, [name]: { path, data: responses[i].data.data } }; + }, {}); + }) + .catch(e => { + log.error(e, 'app-asset-fetch-failed', { assets, version }); + dispatch(assetsError(storableError(e))); + }); +}; diff --git a/src/ducks/index.js b/src/ducks/index.js index 97363c2b2d..a4cf8de49b 100644 --- a/src/ducks/index.js +++ b/src/ducks/index.js @@ -9,6 +9,7 @@ import EmailVerification from './EmailVerification.duck'; import LocationFilter from './LocationFilter.duck'; import Routing from './Routing.duck'; import UI from './UI.duck'; +import hostedAssets from './hostedAssets.duck'; import marketplaceData from './marketplaceData.duck'; import paymentMethods from './paymentMethods.duck'; import stripe from './stripe.duck'; @@ -21,6 +22,7 @@ export { LocationFilter, Routing, UI, + hostedAssets, marketplaceData, paymentMethods, stripe, diff --git a/src/index.js b/src/index.js index 96afb7878c..c2160a7a8e 100644 --- a/src/index.js +++ b/src/index.js @@ -28,6 +28,7 @@ import * as sample from './util/sample'; import * as apiUtils from './util/api'; import config from './config'; import { authInfo } from './ducks/Auth.duck'; +import { fetchAppAssets } from './ducks/hostedAssets.duck'; import { fetchCurrentUser } from './ducks/user.duck'; import routeConfiguration from './routeConfiguration'; import * as log from './util/log'; @@ -39,18 +40,32 @@ const render = (store, shouldHydrate) => { // If the server already loaded the auth information, render the app // immediately. Otherwise wait for the flag to be loaded and render // when auth information is present. - const authInfoLoaded = store.getState().Auth.authInfoLoaded; + const state = store.getState(); + const cdnAssetsVersion = state.hostedAssets.version; + const authInfoLoaded = state.Auth.authInfoLoaded; const info = authInfoLoaded ? Promise.resolve({}) : store.dispatch(authInfo()); info .then(() => { store.dispatch(fetchCurrentUser()); - return loadableReady(); + // Ensure that Loadable Components is ready + // and fetch hosted assets in parallel before initializing the ClientApp + return Promise.all([ + loadableReady(), + store.dispatch(fetchAppAssets(config.appCdnAssets, cdnAssetsVersion)), + ]); }) - .then(() => { + .then(([_, fetchedAssets]) => { + const translations = fetchedAssets?.translations?.data || {}; if (shouldHydrate) { - ReactDOM.hydrate(, document.getElementById('root')); + ReactDOM.hydrate( + , + document.getElementById('root') + ); } else { - ReactDOM.render(, document.getElementById('root')); + ReactDOM.render( + , + document.getElementById('root') + ); } }) .catch(e => { @@ -87,6 +102,9 @@ if (typeof window !== 'undefined') { log.setup(); const baseUrl = config.sdk.baseUrl ? { baseUrl: config.sdk.baseUrl } : {}; + const assetCdnBaseUrl = config.sdk.assetCdnBaseUrl + ? { assetCdnBaseUrl: config.sdk.assetCdnBaseUrl } + : {}; // eslint-disable-next-line no-underscore-dangle const preloadedState = window.__PRELOADED_STATE__ || '{}'; @@ -97,6 +115,7 @@ if (typeof window !== 'undefined') { secure: config.usingSSL, typeHandlers: apiUtils.typeHandlers, ...baseUrl, + ...assetCdnBaseUrl, }); const analyticsHandlers = setupAnalyticsHandlers(); const store = configureStore(initialState, sdk, analyticsHandlers); @@ -137,4 +156,4 @@ export default renderApp; // exporting matchPathname and configureStore for server side rendering. // matchPathname helps to figure out which route is called and if it has preloading needs // configureStore is used for creating initial store state for Redux after preloading -export { matchPathname, configureStore, routeConfiguration, config }; +export { matchPathname, configureStore, routeConfiguration, config, fetchAppAssets }; diff --git a/src/translations/de.json b/src/translations/de.json index 9621c27066..456cc63696 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -1,5 +1,6 @@ { "ActivityFeed.deletedListing": "gelöschter Eintrag", + "ActivityFeed.deletedReviewContent": "This review is deleted.", "ActivityFeed.leaveAReview": "Eine Bewertung an { displayName } abgeben.", "ActivityFeed.leaveAReviewSecond": "Eine Bewertung an { displayName } abgeben, um seine/ihre Bewertung zu sehen.", "ActivityFeed.ownTransitionAccept": "Du hast die Buchungsanfrage angenommen.", @@ -16,11 +17,15 @@ "ActivityFeed.transitionExpire": "Buchungsanfrage abgelaufen. { displayName } hat nicht rechtzeitig reagiert.", "ActivityFeed.transitionRequest": "{ displayName } hat angefragt, { listingTitle } zu buchen.", "ActivityFeed.transitionReview": "{ displayName } hat eine Bewertung abgegeben. { reviewLink }", - "AuthenticationPage.emailAlreadyInUse": "Ein Konto mit dieser Email ist bereits vorhanden. Bitte versuche, dich einzuloggen.", + "AuthenticationPage.confirmSignupInfoText": "Please check that your information is correct.", + "AuthenticationPage.confirmSignupWithIdpTitle": "Sign up with {idp}", "AuthenticationPage.fixEmail": "Hoppla! Ein Tippfehler in deiner Email-Adresse? {fixEmailLink}.", "AuthenticationPage.fixEmailLinkText": "Problem Beheben", "AuthenticationPage.loginFailed": "Diese Email-Passwort-Kombination wurde nicht erkannt. Bitte überprüfen und erneut versuchen.", "AuthenticationPage.loginLinkText": "Anmelden", + "AuthenticationPage.loginWithFacebook": "Log in with Facebook", + "AuthenticationPage.loginWithGoogle": "Log in with Google", + "AuthenticationPage.or": "or", "AuthenticationPage.resendEmail": "Email nicht erhalten? {resendEmailLink}.", "AuthenticationPage.resendEmailLinkText": "Erneut senden", "AuthenticationPage.resendFailed": "Verifizierungs-Email konnte nicht erneut gesendet werden. Bitte erneut versuchen.", @@ -31,16 +36,21 @@ "AuthenticationPage.signupFailed": "Registrierung fehlgeschlagen. Bitte sicherstellen, dass alle Informationen richtig sind, und danach erneut versuchen.", "AuthenticationPage.signupFailedEmailAlreadyTaken": "Ein Konto mit dieser Email ist bereits vorhanden. Bitte versuche, dich anzumelden.", "AuthenticationPage.signupLinkText": "Registrieren", + "AuthenticationPage.signupWithFacebook": "Sign up with Facebook", + "AuthenticationPage.signupWithGoogle": "Sign up with Google", "AuthenticationPage.termsHeading": "Allgemeine Geschäftsbedingungen", "AuthenticationPage.verifyEmailClose": "SPÄTER", "AuthenticationPage.verifyEmailText": "Danke fürs Registrieren! Ein kleiner Schritt noch. Wir müssen deine Email verifizieren, um dich kontaktieren zu können. Bitte klicke auf den Link, den wir an {email} geschickt haben.", "AuthenticationPage.verifyEmailTitle": "{name}, bitte prüfe deine Email-Inbox um deine Email zu verifizieren", "Avatar.bannedUserDisplayName": "Gesperrter Benutzer", "Avatar.deletedUserDisplayName": "Gelöschter Benutzer", + "BookingBreakdown.baseUnitDay": "{unitPrice} * {quantity, number} {quantity, plural, one {day} other {days}}", + "BookingBreakdown.baseUnitNight": "{unitPrice} * {quantity, number} {quantity, plural, one {night} other {nights}}", + "BookingBreakdown.baseUnitQuantity": "{unitPrice} * {quantity, number} {quantity, plural, one {person} other {persons}}", + "BookingBreakdown.bookingEnd": "Booking end", + "BookingBreakdown.bookingStart": "Booking start", "BookingBreakdown.commission": "Saunatime Gebühr *", "BookingBreakdown.commissionFeeNote": "* Die Gebühr hilft uns, die Plattform zu betreiben und dir den bestmöglichen Service zu bieten!", - "BookingBreakdown.dayCount": "{count, number} {count, plural, one {Tag} other {Tage}}", - "BookingBreakdown.nightCount": "{count, number} {count, plural, one {Nacht} other {Nächte}}", "BookingBreakdown.pricePerDay": "Preis pro Tag", "BookingBreakdown.pricePerNight": "Preis pro Nacht", "BookingBreakdown.pricePerQuantity": "Preis pro Person", @@ -57,10 +67,10 @@ "BookingBreakdown.total": "Gesamtpreis", "BookingDatesForm.bookingEndTitle": "Enddatum", "BookingDatesForm.bookingStartTitle": "Startdatum", + "BookingDatesForm.fetchLineItemsError": "Oops, something went wrong. Please refresh the page and try again.", "BookingDatesForm.listingCurrencyInvalid": "Hoppla! Die Währung des Eintrags ist nicht die Währung des Marktplatzes.", "BookingDatesForm.listingPriceMissing": "Ups, dieser Eintrag hat keinen Preis!", "BookingDatesForm.ownListing": "Du kannst deinen eigenen Eintrag nicht buchen.", - "BookingDatesForm.placeholder": "TT.MM.JJJJ", "BookingDatesForm.priceBreakdownTitle": "Aufschlüsselung der Buchung", "BookingDatesForm.requestToBook": "Buchung anfragen", "BookingDatesForm.requiredDate": "Ups! Stelle bitte sicher, dass dein Datum richtig ist.", @@ -86,18 +96,29 @@ "CheckoutPage.initiateOrderError": "Buchungsanfrage gescheitert. Bitte gehe zurück zu {listingLink} und erneut versuchen. Wenn das Problem besteht, bitte lade die Seite neu oder kontaktiere den Marktplatz-Administrator.", "CheckoutPage.initiateOrderStripeError": "Das Zahlungssystem hat die folgenden Fehler gemeldet: {stripeErrors}", "CheckoutPage.listingNotFoundError": "Leider ist der Eintrag nicht mehr Verfügbar.", - "CheckoutPage.loadingData": "Checkout-Daten werden geladen…", "CheckoutPage.paymentExpiredMessage": "Die Zahlung wurde nicht innerhalb von 15 Minuten abgeschlossen. Bitte gehe zurück zu {listingLink} und erneut versuchen.", "CheckoutPage.paymentInfo": "Deine Karte wird belastet nur wenn die Anfrage vom Anbieter angenommen wird.", "CheckoutPage.perDay": "pro Tag", "CheckoutPage.perNight": "pro Nacht", "CheckoutPage.perUnit": "pro Einheit", - "CheckoutPage.priceBreakdownTitle": "Aufschlüsselung der Buchung", "CheckoutPage.providerStripeAccountMissingError": "Der/die Autor(in) hat seine/ihre Zahlungsdaten noch nicht hinzugefügt, und der Eintrag kann im Moment nicht gebucht werden.", "CheckoutPage.retrievingStripePaymentIntentFailed": "Hoppla! Etwas ist schiefgelaufen. Bitte Seite neu laden. Wenn der Fehler wieder geschieht, gehe zurück zu {listingLink} und erneut nach 15 Minuten versuchen", "CheckoutPage.speculateFailedMessage": "Hoppla, etwas ist schiefgelaufen. Bitte Seite neu laden und erneut versuchen.", "CheckoutPage.speculateTransactionError": "Aufschlüsselung konnte nicht geladen werden.", "CheckoutPage.title": "Buche {listingTitle}", + "ConfirmSignupForm.emailInvalid": "A valid email address is required", + "ConfirmSignupForm.emailLabel": "Email", + "ConfirmSignupForm.emailPlaceholder": "john.doe@example.com", + "ConfirmSignupForm.emailRequired": "You need to add an email.", + "ConfirmSignupForm.firstNameLabel": "First name", + "ConfirmSignupForm.firstNamePlaceholder": "John", + "ConfirmSignupForm.firstNameRequired": "You need to add a first name.", + "ConfirmSignupForm.lastNameLabel": "Last name", + "ConfirmSignupForm.lastNamePlaceholder": "Doe", + "ConfirmSignupForm.lastNameRequired": "You need to add a last name.", + "ConfirmSignupForm.signUp": "Continue with {idp}", + "ConfirmSignupForm.termsAndConditionsAcceptText": "By signing up you accept the {termsLink}", + "ConfirmSignupForm.termsAndConditionsLinkText": "terms and conditions", "ContactDetailsForm.confirmChangesInfo": "Um deine Email-Adresse zu ändern, bitte dein aktuelles Passwort eingeben.", "ContactDetailsForm.confirmChangesTitle": "Änderungen bestätigen", "ContactDetailsForm.emailInvalid": "Eine gültige Email-Adresse wird benötigt", @@ -120,6 +141,10 @@ "ContactDetailsForm.phoneLabel": "Telefonnummer", "ContactDetailsForm.phonePlaceholder": "Bitte deine Telefonnummer eingeben", "ContactDetailsForm.resendEmailVerificationText": "Verifizierungs-Email erneut senden.", + "ContactDetailsForm.resetPasswordInfo": "Forgot your password or don't have one? {resetPasswordLink}", + "ContactDetailsForm.resetPasswordLinkSent": "The instructions for resetting your password have been sent to {email}.", + "ContactDetailsForm.resetPasswordLinkText": "Send reset instructions.", + "ContactDetailsForm.resendPasswordLinkText": "Resend instructions.", "ContactDetailsForm.saveChanges": "Änderungen speichern", "ContactDetailsForm.tooManyVerificationRequests": "Zu viele Email-Verifizierungsanfragen gesendet.", "ContactDetailsPage.heading": "Kontaktdaten", @@ -127,10 +152,6 @@ "CookieConsent.continue": "Weiter", "CookieConsent.cookieLink": "Cookies", "CookieConsent.message": "Willkommen bei Saunatime, Freund! Wir verwenden {cookieLink} um deine Surferfahrung zu verbessern.", - "DateInput.clearDate": "Datum löschen", - "DateInput.closeDatePicker": "Schließen", - "DateInput.defaultPlaceholder": "Datumseingabe", - "DateInput.screenReaderInputMessage": "Datumseingabe", "EditListingAvailabilityForm.fetchMonthDataFailed": "Ups! Die Daten für {month} konnten nicht geladen werden, bitte erneut versuchen.", "EditListingAvailabilityForm.availableDay": "Verfügbar", "EditListingAvailabilityForm.blockedDay": "Nicht verfügbar", @@ -172,12 +193,11 @@ "EditListingPage.titleCreateListing": "Eintrag erstellen", "EditListingPage.titleEditListing": "Eintrag bearbeiten", "EditListingPhotosForm.addImagesTip": "Tipp: Wähle die besten 2-3 Fotos von deiner Sauna, aus verschiedenen Winkeln und mit gutem Licht, die den ganzen Raum zeigen.", - "EditListingPhotosForm.bankAccountNumberRequired": "Du must eine Bankkontonummer hinzufügen.", "EditListingPhotosForm.chooseImage": "+ Wähle ein Bild…", "EditListingPhotosForm.imageRequired": "Du musst mindestens ein Bild hinzufügen.", - "EditListingPhotosForm.imageTypes": ".JPG, .GIF oder .PNG. Max. 20 MB", + "EditListingPhotosForm.imageTypes": ".JPG, .GIF oder .PNG. Max. 10 MB", "EditListingPhotosForm.imageUploadFailed.uploadFailed": "Fehler beim Hochladen der Bilddatei. Bitte überprüfe, dass die Bilddatei nicht zu groß ist, und erneut versuchen.", - "EditListingPhotosForm.imageUploadFailed.uploadOverLimit": "Die maximale Dateigröße beträgt 20 MB. Bitte erneut versuchen.", + "EditListingPhotosForm.imageUploadFailed.uploadOverLimit": "Die maximale Dateigröße beträgt 10 MB. Bitte erneut versuchen.", "EditListingPhotosForm.publishListingFailed": "Fehler beim Veröffentlichen einer neuen Sauna", "EditListingPhotosForm.savedImageAltText": "Gespeichertes Eintragsbild", "EditListingPhotosForm.showListingFailed": "Fehler beim Laden der Eintragsdaten", @@ -201,6 +221,10 @@ "EditListingPricingPanel.createListingTitle": "Wie viel kostet es?", "EditListingPricingPanel.listingPriceCurrencyInvalid": "Die Währung des Eintrags entspricht nicht die Währung des Marktplatzes. Du kannst den Preis nicht ändern.", "EditListingPricingPanel.title": "Den Preis von {listingTitle} ändern", + "EditListingWizard.payoutModalInfo": "Almost done! In order to send you money, we need to know a bit more about you. Next, we will guide you through verifying your account. The details can also be edited from Account Setting page on Payout details tab. ", + "EditListingWizard.payoutModalTitleOneMoreThing": "One more thing:", + "EditListingWizard.payoutModalTitlePayoutPreferences": "Payout preferences", + "EditListingWizard.redirectingToStripe": "You returned early from Stripe. Redirecting you back to Stripe…", "EditListingWizard.saveEditDescription": "Beschreibung speichern", "EditListingWizard.saveEditFeatures": "Ausstattung speichern", "EditListingWizard.saveEditLocation": "Standort speichern", @@ -223,6 +247,8 @@ "EditListingWizard.tabLabelPolicy": "Sauna-Regeln", "EditListingWizard.tabLabelPricing": "Preis", "EmailVerificationForm.finishAccountSetup": "Wir müssen dir Email-Benachrichtigungen schicken. Um dies zu erlauben, bitte verifiziere deine Email-Adresse {email}.", + "EmailVerificationForm.noPendingText": "Everything looks good. Your current email address is {email}. {breakline}Time to get out there – have a great day.", + "EmailVerificationForm.noPendingTitle": "You didn't have any pending email address, {name}.", "EmailVerificationForm.successButtonText": "Weiter erkunden", "EmailVerificationForm.successText": "Alles sieht gut aus. Es ist an der Zeit, loszuziehen - einen schönen Tag noch!", "EmailVerificationForm.successTitle": "Danke fürs Verifizieren deiner Email-Adresse, {name}.", @@ -242,8 +268,6 @@ "FieldBoolean.yes": "Ja", "FieldDateInput.clearDate": "Datum löschen", "FieldDateInput.closeDatePicker": "Schließen", - "FieldDateInput.invalidDate": "Anfangsdatum nicht gültig", - "FieldDateInput.placeholderText": "Anfangsdatum hinzufügen", "FieldDateInput.screenReaderInputMessage": "Datumseingabe", "FieldDateRangeInput.clearDate": "Datum löschen", "FieldDateRangeInput.closeDatePicker": "Schließen", @@ -308,13 +332,14 @@ "KeywordFilter.labelSelected": "\"{labelText}\"", "KeywordFilter.placeholder": "Schlüsselwort eingeben…", "KeywordFilterPlainForm.labelSelected": "Schlüsselwort", - "KeywordFilterPlainForm.placeholder": "Schlüsselwort eingeben…", "LandingPage.schemaDescription": "Buche eine Sauna mit Saunatime oder verdiene etwas Geld durch das Vermieten deiner eigenen Sauna", "LandingPage.schemaTitle": "Saunas überall buchen | {siteTitle}", "LayoutWrapperAccountSettingsSideNav.contactDetailsTabTitle": "Kontaktdaten", "LayoutWrapperAccountSettingsSideNav.passwordTabTitle": "Passwort", "LayoutWrapperAccountSettingsSideNav.paymentsTabTitle": "Zahlungseinstellungen", "LayoutWrapperAccountSettingsSideNav.paymentMethodsTabTitle": "Zahlungseinstellungen", + "LimitedAccessBanner.message": "Your are logged in as {firstName} {lastName}. You have limited rights for actions.", + "LimitedAccessBanner.logout": "Log out", "ListingCard.hostedBy": "Gehostet von {authorName}.", "ListingCard.perDay": "pro Tag", "ListingCard.perNight": "pro Nacht", @@ -349,6 +374,9 @@ "ListingPage.schemaTitle": "{title} - {price} | {siteTitle}", "ListingPage.viewImagesButton": "Fotos anzeigen ({count})", "ListingPage.yourHostHeading": "Dein(e) Gastgeber(in)", + "LoadableComponentErrorBoundaryPage.heading": "Oops, we couldn't fetch the page.", + "LoadableComponentErrorBoundaryPage.description": "It looks like the app needs to be updated! Please, refresh the current page or go back to {link}.", + "LoadableComponentErrorBoundaryPage.landingPageLink": "landing page", "LocationAutocompleteInput.currentLocation": "Aktueller Standort", "LocationSearchForm.placeholder": "Saunas suchen…", "LoginForm.emailInvalid": "Eine gültige Email-Adresse wird benötigt", @@ -385,7 +413,6 @@ "ManageListingsPage.title": "Einträge verwalten", "ManageListingsPage.youHaveListings": "Du hast {count} {count, plural, one {Eintrag} other {Einträge}}", "ManageListingsPage.yourListings": "Deine Einträge", - "MapPriceMarker.unsupportedPrice": "({currency})", "Modal.close": "SCHLIEßEN", "Modal.closeModal": "Modal schließen", "ModalMissingInformation.checkInbox": "Bitte deine Inbox überprüfen und deine Email-Adresse {email} verifizieren", @@ -422,6 +449,10 @@ "PasswordChangeForm.passwordRequired": "Aktuelles Passwort wird benötigt", "PasswordChangeForm.passwordTooLong": "Das Passwort sollte höchstens {maxLength} Zeichen umfassen", "PasswordChangeForm.passwordTooShort": "Das Passwort sollte mindestens {minLength} Zeichen umfassen", + "PasswordChangeForm.resetPasswordInfo": "Forgot your password or don't have one? {resetPasswordLink}", + "PasswordChangeForm.resetPasswordLinkSent": "The instructions for resetting your password have been sent to {email}.", + "PasswordChangeForm.resetPasswordLinkText": "Send reset instructions.", + "PasswordChangeForm.resendPasswordLinkText": "Resend instructions.", "PasswordChangeForm.saveChanges": "Änderungen speichern", "PasswordChangePage.heading": "Passworteinstellungen", "PasswordChangePage.title": "Passwort", @@ -465,12 +496,8 @@ "PaymentMethodsForm.billingDetailsNamePlaceholder": "Name eingeben…", "PaymentMethodsForm.paymentCardDetails": "Zahlungskartendaten", "PaymentMethodsForm.genericError": "Zahlungsdaten konnten nicht verarbeitet werden. Bitte erneut versuchen.", - "PaymentMethodsForm.confirmCardPaymentError": "Deine Zahlungsmethode konnte nicht authentifiziert werden. Bitte deine Authentifizierungsdetails prüfen und erneut versuchen.", "PaymentMethodsForm.infoText": "Ich authorisiere Saunatime, dem Finanzinstitut das meine Karte ausgegeben hat, Zahlungen von meinem Kartenkonto in Übereinstimmung mit den Bedingungen meines Vertrags mit Ihnen zu entnehmen.", - "PaymentMethodsForm.paymentHeading": "Zahlung", "PaymentMethodsPage.heading": "Zahlungsmethoden", - "PaymentMethodsPage.loadingData": "Daten werden geladen…", - "PaymentMethodsPage.savedPaymentMethodTitle": "Kreditkartenangaben", "PaymentMethodsForm.submitPaymentInfo": "Zahlungskarte speichern", "PaymentMethodsPage.title": "Zahlungsmethoden", "PayoutDetailsForm.accountOpenerInfoText": "Der Kontoeröffner muss eine Person sein, die die erforderliche Zeichnungsbefugnis bei der Organisation hat.", @@ -481,7 +508,6 @@ "PayoutDetailsForm.additionalPersonLink": "Besitzer und Chefs hinzufügen", "PayoutDetailsForm.additionalPersonRemove": "Person entfernen", "PayoutDetailsForm.additionalPersonTitle": "Besitzer(in) oder Chef(in)", - "PayoutDetailsForm.addressTitle": "Adresse", "PayoutDetailsForm.bankDetails": "Bankverbindung", "PayoutDetailsForm.birthdayDatePlaceholder": "TT", "PayoutDetailsForm.birthdayLabel": "Geburtsdatum", @@ -577,7 +603,6 @@ "PayoutDetailsForm.firstNamePlaceholder": "Max", "PayoutDetailsForm.firstNameRequired": "Dieses Feld wird benötigt", "PayoutDetailsForm.individualAccount": "Ich bin eine Einzelperson", - "PayoutDetailsForm.information": "Weil dieser dein erster Eintrag ist, brauchen wir von dir etwas mehr Informationen, um dir Geld für den eventuellen Verkauf schicken zu können. Diese Informationen werden nur einmal gefragt.", "PayoutDetailsForm.lastNameLabel": "Nachname", "PayoutDetailsForm.lastNamePlaceholder": "Mustermann", "PayoutDetailsForm.lastNameRequired": "Dieses Feld wird benötigt", @@ -587,7 +612,6 @@ "PayoutDetailsForm.owner": "Besitzer (25% oder mehr)", "PayoutDetailsForm.ownershipPercentageLabel": "Eigentumsanteil (%)", "PayoutDetailsForm.ownershipPercentagePlaceholder": "100", - "PayoutDetailsForm.personalDetailsAdditionalOwnerTitle": "Zusätzliche Eigentümersinformationen", "PayoutDetailsForm.personalDetailsTitle": "Personalangaben", "PayoutDetailsForm.personalEmailLabel": "Email", "PayoutDetailsForm.personalEmailPlaceholder": "Email-Adresse", @@ -599,7 +623,6 @@ "PayoutDetailsForm.personalIdNumberPlaceholder.SG": "S8888888A", "PayoutDetailsForm.personalIdNumberPlaceholder.US": "1234", "PayoutDetailsForm.personalIdNumberRequired": "Dieses Feld wird benötigt", - "PayoutDetailsForm.personalIdNumberTitle": "Persönliche Identitätsnummer", "PayoutDetailsForm.personalIdNumberValid": "Ungültiger Wert", "PayoutDetailsForm.personalPhoneLabel": "Telefonnummer", "PayoutDetailsForm.personalPhonePlaceholder": "0873 376461", @@ -617,7 +640,6 @@ "PayoutDetailsForm.stripeConnectedAccountTermsLink": "Stripe Connected Kontobedingungen", "PayoutDetailsForm.stripeToSText": "Beim Speichern, zustimmst du dem {stripeConnectedAccountTermsLink}", "PayoutDetailsForm.submitButtonText": "Angaben speichern & Eintrag veröffentlichen", - "PayoutDetailsForm.title": "Noch etwas: Auszahlungseinstellungen", "PriceFilter.clear": "Löschen", "PriceFilter.label": "Preis", "PriceFilter.labelSelectedPlain": "Preis: {minPrice} - {maxPrice}", @@ -647,12 +669,12 @@ "ProfileSettingsForm.bioLabel": "Biographie", "ProfileSettingsForm.bioPlaceholder": "Erzähle uns etwas über dich…", "ProfileSettingsForm.changeAvatar": "Ändern", - "ProfileSettingsForm.fileInfo": ".JPG, .GIF oder .PNG. Max. 20 MB", + "ProfileSettingsForm.fileInfo": ".JPG, .GIF oder .PNG. Max. 10 MB", "ProfileSettingsForm.firstNameLabel": "Vorname", "ProfileSettingsForm.firstNamePlaceholder": "Max", "ProfileSettingsForm.firstNameRequired": "Dieses Feld wird benötigt", "ProfileSettingsForm.imageUploadFailed": "Hoppla! Etwas ist schiefgelaufen. Bitte prüfe, dass die Bilddatei nicht zu groß war, und erneut versuchen.", - "ProfileSettingsForm.imageUploadFailedFileTooLarge": "Die Bilddatei war zu groß. Maximale Dateigröße ist 20 MB.", + "ProfileSettingsForm.imageUploadFailedFileTooLarge": "Die Bilddatei war zu groß. Maximale Dateigröße ist 10 MB.", "ProfileSettingsForm.lastNameLabel": "Nachname", "ProfileSettingsForm.lastNamePlaceholder": "Mustermann", "ProfileSettingsForm.lastNameRequired": "Dieses Feld wird benötigt", @@ -687,29 +709,26 @@ "SavedCardDetails.savedPaymentMethodPlaceholderDesktop": "•••• •••• •••• {last4Digits}", "SavedCardDetails.savedPaymentMethodPlaceholderMobile": "…{last4Digits}", "SearchFiltersMobile.cancel": "ABBRECHEN", - "SearchFiltersMobile.filtersButtonLabel": "Filter", - "SearchFiltersMobile.foundResults": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}", + "SearchFiltersMobile.filtersButtonLabel": "Filters", + "SearchFiltersMobile.foundResults": "{count, number} {count, plural, one {result} other {results}}", "SearchFiltersMobile.heading": "Filter", - "SearchFiltersMobile.loadingResults": "Lade…", - "SearchFiltersMobile.openMapView": "Karte", - "SearchFiltersMobile.noResults": "Keine Ergebnisse.", - "SearchFiltersMobile.keywordLabel": "Schlüsselwort", + "SearchFiltersMobile.loadingResults": "Loading…", + "SearchFiltersMobile.openMapView": "Map", + "SearchFiltersMobile.noResults": "No results.", "SearchFiltersMobile.resetAll": "Alles zurücksetzen", "SearchFiltersMobile.showListings": "Zeige {count, number} {count, plural, one {Sauna} other {Saunas}}", - "SearchFiltersPrimary.foundResults": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}", - "SearchFiltersPrimary.loadingResults": "Lade Suchergebnisse…", - "SearchFiltersPrimary.moreFiltersButton": "{count, plural, =0 {Andere Filter} other {Andere Filter • #}}", - "SearchFiltersPrimary.noResults": "Keine Einträge gefunden.", - "SearchFiltersSecondary.apply": "Anwenden", - "SearchFiltersSecondary.cancel": "Abbrechen", - "SearchFiltersSecondary.resetAll": "Alles zurücksetzen", + "SearchFiltersPrimary.foundResults": "{count, number} {count, plural, one {result} other {results}}", + "SearchFiltersPrimary.loadingResults": "Loading search results…", + "SearchFiltersPrimary.moreFiltersButton": "{count, plural, =0 {More filters} other {More filters • #}}", + "SearchFiltersPrimary.noResults": "Could not find any listings.", + "SearchFiltersSecondary.apply": "Apply", + "SearchFiltersSecondary.cancel": "Cancel", + "SearchFiltersSecondary.resetAll": "Reset all", "SearchMapInfoCard.noImage": "Kein Bild", "SearchPage.schemaDescription": "Suchergebnisse werden angezeigt", "SearchPage.schemaMapSearch": "Kartensuche", "SearchPage.schemaTitle": "Suchergebnisse für {searchAddress} | {siteTitle}", "SearchPage.searchError": "Fehler beim Suchen. Bitte erneut versuchen.", - "SearchResultsPanel.nextPage": "Nächste Seite", - "SearchResultsPanel.previousPage": "Vorherige Seite", "SectionHero.browseButton": "Saunas durchstöbern", "SectionHero.subTitle": "Die größte Online-Community fürs Saunamieten in Finnland.", "SectionHero.title": "Saunas überall buchen.", @@ -748,19 +767,36 @@ "SignupForm.signUp": "Registrieren", "SignupForm.termsAndConditionsAcceptText": "Durch Registrieren zustimmst du die {termsLink}", "SignupForm.termsAndConditionsLinkText": "allgemeinen Geschätfsbdingungen", + "SortBy.heading": "Sort by", "StripeBankAccountTokenInputField.accountNumber.inline": "Kontonummer", "StripeBankAccountTokenInputField.accountNumber.label": "Bankkontonummer", "StripeBankAccountTokenInputField.accountNumber.placeholder": "Bankkontonummer eingeben…", "StripeBankAccountTokenInputField.accountNumber.required": "Bankkontonummer wird benötigt", + "StripeBankAccountTokenInputField.accountOwnerName.inline": "account owner name", + "StripeBankAccountTokenInputField.accountOwnerName.label": "Bank account owner name", + "StripeBankAccountTokenInputField.accountOwnerName.placeholder": "John Doe", + "StripeBankAccountTokenInputField.accountOwnerName.required": "Bank account owner name is required", "StripeBankAccountTokenInputField.andBeforeLastItemInAList": " und", "StripeBankAccountTokenInputField.bankCode.inline": "Bankleitzahl", "StripeBankAccountTokenInputField.bankCode.label": "Bankleitzahl", "StripeBankAccountTokenInputField.bankCode.placeholder": "Bankleitzahl eingeben…", "StripeBankAccountTokenInputField.bankCode.required": "Bankleitzahl wird benötigt", + "StripeBankAccountTokenInputField.bankName.inline": "bank name", + "StripeBankAccountTokenInputField.bankName.label": "Bank name", + "StripeBankAccountTokenInputField.bankName.placeholder": "Type in bank name…", + "StripeBankAccountTokenInputField.bankName.required": "Bank name is required", "StripeBankAccountTokenInputField.branchCode.inline": "Filialcode", "StripeBankAccountTokenInputField.branchCode.label": "Filialcode", "StripeBankAccountTokenInputField.branchCode.placeholder": "Filialcode eingeben…", "StripeBankAccountTokenInputField.branchCode.required": "Filialcode wird benötigt", + "StripeBankAccountTokenInputField.branchName.inline": "branch name", + "StripeBankAccountTokenInputField.branchName.label": "Branch name", + "StripeBankAccountTokenInputField.branchName.placeholder": "Type in branch name…", + "StripeBankAccountTokenInputField.branchName.required": "Branch name is required", + "StripeBankAccountTokenInputField.clabe.inline": "CLABE", + "StripeBankAccountTokenInputField.clabe.label": "CLABE", + "StripeBankAccountTokenInputField.clabe.placeholder": "Type in CLABE…", + "StripeBankAccountTokenInputField.clabe.required": "CLABE is required", "StripeBankAccountTokenInputField.bsb.inline": "BSB", "StripeBankAccountTokenInputField.bsb.label": "BSB", "StripeBankAccountTokenInputField.bsb.placeholder": "BSB eingeben…", @@ -814,7 +850,7 @@ "StripePaymentForm.billingDetailsNamePlaceholder": "Name eingeben…", "StripePaymentForm.confirmPaymentError": "Die Zahlung ist erfolgt, aber wir konnten die Buchung nicht bestätigen. Bitte versuche, die Buchungsanfrage erneut zu bestätigen! Wenn die Buchung nicht rechtzeitigt bestätigt ist, wird die Zahlung vollständig erstattet.", "StripePaymentForm.genericError": "Zahlungsinformationen konnten nicht verarbeitet werden. Bitte erneut versuchen.", - "StripePaymentForm.confirmCardPaymentError": "Deine Zahlungsmethode konnte nicht authentifiziert werden. Bitte deine Authentifizierungsdetails prüfen und erneut versuchen.", + "StripePaymentForm.confirmCardPaymentError": "We are unable to authenticate your payment method. Please check your authentication details and try again.", "StripePaymentForm.messageHeading": "Nachricht", "StripePaymentForm.messageLabel": "Hallo an deine(n) Gastgeber(in) {messageOptionalText} sagen", "StripePaymentForm.messageOptionalText": "• optional", @@ -848,6 +884,68 @@ "StripePaymentForm.stripe.validation_error.processing_error": "Fehler bei Verarbeitung der Karte.", "StripePaymentForm.submitConfirmPaymentInfo": "Anfrage bestätigen", "StripePaymentForm.submitPaymentInfo": "Anfrage senden", + "StripeConnectAccountForm.accountTypeTitle": "Account type", + "StripeConnectAccountForm.bankAccountLabel": "Bank account", + "StripeConnectAccountForm.companyAccount": "I represent a company", + "StripeConnectAccountForm.countryLabel": "Country", + "StripeConnectAccountForm.countryNames.AT": "Austria", + "StripeConnectAccountForm.countryNames.AU": "Australia", + "StripeConnectAccountForm.countryNames.BE": "Belgium", + "StripeConnectAccountForm.countryNames.BG": "Bulgaria", + "StripeConnectAccountForm.countryNames.CA": "Canada", + "StripeConnectAccountForm.countryNames.CY": "Cyprus", + "StripeConnectAccountForm.countryNames.CZ": "Czech Republic", + "StripeConnectAccountForm.countryNames.CH": "Switzerland", + "StripeConnectAccountForm.countryNames.DE": "Germany", + "StripeConnectAccountForm.countryNames.GR": "Greece", + "StripeConnectAccountForm.countryNames.DK": "Denmark", + "StripeConnectAccountForm.countryNames.EE": "Estonia", + "StripeConnectAccountForm.countryNames.ES": "Spain", + "StripeConnectAccountForm.countryNames.FI": "Finland", + "StripeConnectAccountForm.countryNames.FR": "France", + "StripeConnectAccountForm.countryNames.GB": "United Kingdom", + "StripeConnectAccountForm.countryNames.HK": "Hong Kong", + "StripeConnectAccountForm.countryNames.IE": "Ireland", + "StripeConnectAccountForm.countryNames.IT": "Italy", + "StripeConnectAccountForm.countryNames.JP": "Japan", + "StripeConnectAccountForm.countryNames.LT": "Lithuania", + "StripeConnectAccountForm.countryNames.LV": "Latvia", + "StripeConnectAccountForm.countryNames.LU": "Luxembourg", + "StripeConnectAccountForm.countryNames.MT": "Malta", + "StripeConnectAccountForm.countryNames.MX": "Mexico", + "StripeConnectAccountForm.countryNames.NL": "Netherlands", + "StripeConnectAccountForm.countryNames.NO": "Norway", + "StripeConnectAccountForm.countryNames.NZ": "New Zealand", + "StripeConnectAccountForm.countryNames.PL": "Poland", + "StripeConnectAccountForm.countryNames.PT": "Portugal", + "StripeConnectAccountForm.countryNames.RO": "Romania", + "StripeConnectAccountForm.countryNames.SE": "Sweden", + "StripeConnectAccountForm.countryNames.SI": "Slovenia", + "StripeConnectAccountForm.countryNames.SK": "Slovakia", + "StripeConnectAccountForm.countryNames.SG": "Singapore", + "StripeConnectAccountForm.countryNames.US": "United States", + "StripeConnectAccountForm.countryPlaceholder": "Select your country…", + "StripeConnectAccountForm.countryRequired": "This field is required", + "StripeConnectAccountForm.createStripeAccountFailed": "Whoops, something went wrong. Please try again. If the problem persists, contact your marketplace administrators.", + "StripeConnectAccountForm.createStripeAccountFailedWithStripeError": "Whoops, something went wrong. Stripe returned an error message: \"{stripeMessage}\"", + "StripeConnectAccountForm.individualAccount": "I'm an individual", + "StripeConnectAccountForm.createStripeAccountLinkFailed": "Whoops, something went wrong. Please contact your marketplace administrators.", + "StripeConnectAccountForm.createStripeAccountLinkFailedWithStripeError": "Whoops, something went wrong. Stripe returned an error message: \"{stripeMessage}\"", + "StripeConnectAccountForm.loadingStripeAccountData": "Fetching payout details…", + "StripeConnectAccountForm.stripeToSText": "By saving details, you agree to the {stripeConnectedAccountTermsLink}", + "StripeConnectAccountForm.stripeConnectedAccountTermsLink": "Stripe Connected Account Agreement", + "StripePayoutPage.heading": "Payout details", + "StripePayoutPage.loadingData": "Loading data…", + "StripePayoutPage.redirectingToStripe": "You returned early from Stripe. Redirecting you back to Stripe…", + "StripePayoutPage.submitButtonText": "Save details", + "StripePayoutPage.title": "Payout details", + "StripeConnectAccountStatusBox.editAccountButton": "Edit Stripe account", + "StripeConnectAccountStatusBox.getVerifiedButton": "Get verified", + "StripeConnectAccountStatusBox.verificationFailedText": "In order for you to receive payments, you need to add your banking details and verify your account.", + "StripeConnectAccountStatusBox.verificationFailedTitle": "Something went wrong - please try again", + "StripeConnectAccountStatusBox.verificationNeededText": "In order for you to receive payments you need to add few more details to your Stripe account to verify your account.", + "StripeConnectAccountStatusBox.verificationNeededTitle": "Stripe needs more information", + "StripeConnectAccountStatusBox.verificationSuccessTitle": "Your Stripe account is up to date!", "TermsOfServicePage.heading": "Allgemeine Geschäftsbedingungen", "TermsOfServicePage.privacyTabTitle": "Datenschutzbestimmungen", "TermsOfServicePage.schemaTitle": "Allgemeine Geschätfsbedingungen | {siteTitle}", @@ -878,7 +976,6 @@ "TopbarMobileMenu.unauthorizedGreeting": "Hallo,{lineBreak}möchtest du {signupOrLogin}?", "TopbarMobileMenu.yourListingsLink": "Deine Einträge", "TopbarSearchForm.placeholder": "Saunas suchen…", - "TopbarSearchForm.searchHelp": "Tipp: Du kannst auch nach Saunas mit PLZ suchen, zum Beispiel \"00500\" oder Stadtteil \"Sörnäinen\".", "TransactionPage.deletedListing": "gelöschter Eintrag", "TransactionPage.fetchOrderFailed": "Fehler beim Holen der Bestellungsdaten.", "TransactionPage.fetchSaleFailed": "Fehler beim Holen der Verkaufsdaten.", @@ -888,7 +985,6 @@ "TransactionPanel.acceptButton": "Annehmen", "TransactionPanel.acceptSaleFailed": "Hoppla, annehmen fehlgeschlagen. Bitte erneut versuchen.", "TransactionPanel.activityHeading": "Aktivität", - "TransactionPanel.bookingBreakdownTitle": "Aufschlüsselung der Buchung", "TransactionPanel.customerBannedStatus": "Der Nutzer hat die Anfrage gemacht, wurde aber später gesperrt.", "TransactionPanel.declineButton": "Ablehnen", "TransactionPanel.declineSaleFailed": "Hoppla, Fehler beim Ablehnen. Bitte erneut versuchen.", @@ -898,7 +994,6 @@ "TransactionPanel.messageDeletedListing": "Der Eintrag wurde jedoch gelöscht und kann nicht mehr angezeigt werden.", "TransactionPanel.messageLoadingFailed": "Etwas ist schiefgelaufen beim Laden der Nachrichten. Bitte Seite neu laden und erneut versuchen.", "TransactionPanel.orderAcceptedSubtitle": "Deine Buchung für {listingLink} wurde angenommen.", - "TransactionPanel.orderAcceptedTitle": "Juhu {customerName}!", "TransactionPanel.orderCancelledTitle": "{customerName}, deine Buchung von {listingLink} wurde storniert.", "TransactionPanel.orderDeclinedTitle": "{customerName}, deine Buchung von {listingLink} wurde abgelehnt.", "TransactionPanel.orderDeliveredTitle": "{customerName}, deine Buchung von {listingLink} wurde abgeschlossen.", @@ -912,7 +1007,6 @@ "TransactionPanel.perDay": "pro Tag", "TransactionPanel.perNight": "pro Nacht", "TransactionPanel.perUnit": "pro Einheit", - "TransactionPanel.requestToBook": "Buchung anfragen", "TransactionPanel.saleAcceptedTitle": "Du hast eine Anfrage von {customerName} angenommen für das Buchen von {listingLink}.", "TransactionPanel.saleCancelledTitle": "Die Buchung von {customerName} für {listingLink} wurde storniert.", "TransactionPanel.saleDeclinedTitle": "Die Anfrage von {customerName} für das Buchen von {listingLink} wurde abgelehnt.", diff --git a/src/translations/es.json b/src/translations/es.json index 63450f8c09..0c0a6a2a11 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -19,7 +19,6 @@ "ActivityFeed.transitionReview": "{ displayName } ha dejado una valoración sobre ti. { reviewLink }", "AuthenticationPage.confirmSignupInfoText": "Por favor, verifica que tu información sea correcta", "AuthenticationPage.confirmSignupWithIdpTitle": "Registrarse con {idp}", - "AuthenticationPage.emailAlreadyInUse": "Parece ser que ya tienes una cuenta. Por favor, intenta iniciar sesión.", "AuthenticationPage.fixEmail": "¿Quizás te has equivocado con tu cuenta de correo? {fixEmailLink}.", "AuthenticationPage.fixEmailLinkText": "Vuelve a intentarlo", "AuthenticationPage.loginFailed": "Esta información de usuario no coincide con nuestros registros. Por favor, introduce la información correcta.", @@ -52,8 +51,6 @@ "BookingBreakdown.bookingStart": "Inicio de la reservación", "BookingBreakdown.commission": "Comisión de Saunatime *", "BookingBreakdown.commissionFeeNote": "* La comisión ayuda a mantener la comunidad y nos permite ofrecerte un buen servicio.", - "BookingBreakdown.dayCount": "{count, number} {count, plural, one {día} other {días}}", - "BookingBreakdown.nightCount": "{count, number} {count, plural, one {noche} other {noches}}", "BookingBreakdown.pricePerDay": "Precio por día", "BookingBreakdown.pricePerNight": "Precio por noche", "BookingBreakdown.pricePerQuantity": "Precio por persona", @@ -74,7 +71,6 @@ "BookingDatesForm.listingCurrencyInvalid": "Ups, la moneda del anuncio no se corresponde con la moneda del marketplace.", "BookingDatesForm.listingPriceMissing": "Oops, a tu anuncio le falta el precio!", "BookingDatesForm.ownListing": "No puedes reservar tu propio anuncio.", - "BookingDatesForm.placeholder": "dd/mm/yyyy", "BookingDatesForm.priceBreakdownTitle": "Resumen de la reserva", "BookingDatesForm.requestToBook": "Solicitar reserva", "BookingDatesForm.requiredDate": "Ups, asegúrate de que la fecha es correcta!", @@ -100,13 +96,11 @@ "CheckoutPage.initiateOrderError": "No se ha conseguido realizar el pago. Por favor, regresa al {listingLink} y vuelve a intentarlo. Si el problema persiste, actualiza la página o contacta al administrador del marketplace.", "CheckoutPage.initiateOrderStripeError": "El procesador de pago regresó los siguientes errores: {stripeErrors}", "CheckoutPage.listingNotFoundError": "Desafortunadamente, el anuncio ya no está disponible.", - "CheckoutPage.loadingData": "Cargando información…", "CheckoutPage.paymentExpiredMessage": "El pago no se completó en 15 minutos. Por favor regresa a {listingLink} e inténtalo de nuevo.", "CheckoutPage.paymentInfo": "Sólo se te cobrará si el proveedor acepta la solicitud.", "CheckoutPage.perDay": "por día", "CheckoutPage.perNight": "por noche", "CheckoutPage.perUnit": "por unidad", - "CheckoutPage.priceBreakdownTitle": "Resumen de la reserva", "CheckoutPage.providerStripeAccountMissingError": "El autor del anuncio no ha configurado sus datos de pago y el anuncio aún no admite reservas.", "CheckoutPage.retrievingStripePaymentIntentFailed": "Ups, algo ha salido mal. Por favor actualiza la pagina. Si el error persiste, regresa a {listingLink} e intenta de nuevo en 15 minutos.", "CheckoutPage.speculateFailedMessage": "Ups, algo ha salido mal. Por favor, actualiza la página y vuelve a intentarlo.", @@ -158,10 +152,6 @@ "CookieConsent.continue": "Continuar", "CookieConsent.cookieLink": "cookies", "CookieConsent.message": "Bienvenido a Saunatime. Usamos {cookieLink} para mejorar tu experiencia.", - "DateInput.clearDate": "Borrar fecha", - "DateInput.closeDatePicker": "Cerrar", - "DateInput.defaultPlaceholder": "Fecha", - "DateInput.screenReaderInputMessage": "Fecha", "EditListingAvailabilityForm.fetchMonthDataFailed": "Ups, no se pudo cargar la información de {month}, por favor intenta de nuevo.", "EditListingAvailabilityForm.availableDay": "Disponible", "EditListingAvailabilityForm.blockedDay": "No disponible", @@ -203,7 +193,6 @@ "EditListingPage.titleCreateListing": "Crear un anuncio", "EditListingPage.titleEditListing": "Editar anuncio", "EditListingPhotosForm.addImagesTip": "Consejo: Selecciona 2 o 3 fotos de tu sauna desde distinto ángulos y con buena luz para mostrar bien el espacio.", - "EditListingPhotosForm.bankAccountNumberRequired": "Necesitas agregar tu número de cuenta bancaria.", "EditListingPhotosForm.chooseImage": "+ Añadir imagen…", "EditListingPhotosForm.imageRequired": "Necesitas añadir al menos una imagen.", "EditListingPhotosForm.imageTypes": ".JPG, .GIF o .PNG. Máx. 20 MB", @@ -258,6 +247,8 @@ "EditListingWizard.tabLabelPolicy": "Reglas del sauna", "EditListingWizard.tabLabelPricing": "Precio", "EmailVerificationForm.finishAccountSetup": "Necesitaremos enviarte notificaciones por correo electrónico. Para permitirlo, por favor verifica tu cuenta {email}.", + "EmailVerificationForm.noPendingText": "Todo se ve bien. La dirección de correo actual es {email}. {breakline}Que tengas un excelente día.", + "EmailVerificationForm.noPendingTitle": "No tienes direcciones de correo por verificar, {name}.", "EmailVerificationForm.successButtonText": "Continúa explorando", "EmailVerificationForm.successText": "Todo listo. Es hora de seguir navegando – que tengas un buen día.", "EmailVerificationForm.successTitle": "{name}, gracias por verificar tu cuenta de correo electrónico!", @@ -277,8 +268,6 @@ "FieldBoolean.yes": "Sí", "FieldDateInput.clearDate": "Borrar fecha", "FieldDateInput.closeDatePicker": "Cerrar", - "FieldDateInput.invalidDate": "La fecha inicial no es válida", - "FieldDateInput.placeholderText": "Introduce la fecha inicial", "FieldDateInput.screenReaderInputMessage": "Fecha", "FieldDateRangeInput.clearDate": "Borrar fecha", "FieldDateRangeInput.closeDatePicker": "Cerrar", @@ -343,7 +332,6 @@ "KeywordFilter.labelSelected": "\"{labelText}\"", "KeywordFilter.placeholder": "Escribe una palabra clave…", "KeywordFilterPlainForm.labelSelected": "Palabra clave", - "KeywordFilterPlainForm.placeholder": "Escribe una palabra clave…", "LandingPage.schemaDescription": "Alquila un sauna usando Saunatime or gana un dinero extra al compartir tu sauna", "LandingPage.schemaTitle": "Alquila saunas en cualquier parte | {siteTitle}", "LayoutWrapperAccountSettingsSideNav.contactDetailsTabTitle": "Información de contacto", @@ -386,7 +374,6 @@ "ListingPage.schemaTitle": "{title} - {price} | {siteTitle}", "ListingPage.viewImagesButton": "Ver fotos ({count})", "ListingPage.yourHostHeading": "Tu anfitrión", - "LoadableComponentErrorBoundaryPage.title": "Página no localizada", "LoadableComponentErrorBoundaryPage.heading": "Ups, no pudimos localizar la página.", "LoadableComponentErrorBoundaryPage.description": "Parece ser que la aplicación necesita una actualización. Por favor actualiza la página o regresa a {link}.", "LoadableComponentErrorBoundaryPage.landingPageLink": "Página de Destino", @@ -426,7 +413,6 @@ "ManageListingsPage.title": "Administrar anuncios", "ManageListingsPage.youHaveListings": "Tienes {count} {count, plural, one {anuncio} other {anuncios}}", "ManageListingsPage.yourListings": "Tus anuncios", - "MapPriceMarker.unsupportedPrice": "({currency})", "Modal.close": "CERRAR", "Modal.closeModal": "Cerrar", "ModalMissingInformation.checkInbox": "Por favor, revisa tu buzón y verifica tu cuenta de correo electrónico {email}", @@ -510,12 +496,8 @@ "PaymentMethodsForm.billingDetailsNamePlaceholder": "Escribe el nombre…", "PaymentMethodsForm.paymentCardDetails": "Datos de la tarjeta de crédito", "PaymentMethodsForm.genericError": "Ha habido algún problema con la información de pagos. Por favor, inténtalo de nuevo.", - "PaymentMethodsForm.confirmCardPaymentError": "No hemos podido autenticar el método de pago. Por favor checa los detalles de autenticación e intenta de nuevo.", "PaymentMethodsForm.infoText": "Autorizo a Saunatime a mandar instrucciones a la institución bancaria que expidió mi tarjeta para cargar montos adicionales a mi cuenta de acuerdo a los términos del acuerdo con Saunatime.", - "PaymentMethodsForm.paymentHeading": "Pago", "PaymentMethodsPage.heading": "Formas de pago", - "PaymentMethodsPage.loadingData": "Cargando información…", - "PaymentMethodsPage.savedPaymentMethodTitle": "Datos de la tarjeta de crédito", "PaymentMethodsForm.submitPaymentInfo": "Guardar tarjeta", "PaymentMethodsPage.title": "Formas de pago", "PayoutDetailsForm.accountOpenerInfoText": "El titular de la cuenta debe ser un individuo con autorización para firmar en nombre de la organización.", @@ -526,7 +508,6 @@ "PayoutDetailsForm.additionalPersonLink": "Añadir propietarios o directores", "PayoutDetailsForm.additionalPersonRemove": "Eliminar persona", "PayoutDetailsForm.additionalPersonTitle": "Propietario o director", - "PayoutDetailsForm.addressTitle": "Dirección", "PayoutDetailsForm.bankDetails": "Datos bancarios", "PayoutDetailsForm.birthdayDatePlaceholder": "dd", "PayoutDetailsForm.birthdayLabel": "Fecha de nacimiento", @@ -622,7 +603,6 @@ "PayoutDetailsForm.firstNamePlaceholder": "Juan", "PayoutDetailsForm.firstNameRequired": "Este campo es necesario", "PayoutDetailsForm.individualAccount": "Soy una persona física", - "PayoutDetailsForm.information": "!Casi listo! Necesitamos saber un poco más de ti para poder depositarte dinero. A continuación te guiaremos por el proceso para verificar tu cuenta. También puedes modificar esta información en la página de Configuración de la cuenta en la sección de Configuración de pagos.", "PayoutDetailsForm.lastNameLabel": "Apellido", "PayoutDetailsForm.lastNamePlaceholder": "Perez", "PayoutDetailsForm.lastNameRequired": "Este campo es necesario", @@ -632,7 +612,6 @@ "PayoutDetailsForm.owner": "Proprietario (25% o más)", "PayoutDetailsForm.ownershipPercentageLabel": "Porcentaje de participación", "PayoutDetailsForm.ownershipPercentagePlaceholder": "100", - "PayoutDetailsForm.personalDetailsAdditionalOwnerTitle": "Detalles del propietario adicional", "PayoutDetailsForm.personalDetailsTitle": "Datos personales", "PayoutDetailsForm.personalEmailLabel": "Correo electrónico", "PayoutDetailsForm.personalEmailPlaceholder": "Dirección de correo electrónico", @@ -644,7 +623,6 @@ "PayoutDetailsForm.personalIdNumberPlaceholder.SG": "S8888888A", "PayoutDetailsForm.personalIdNumberPlaceholder.US": "1234", "PayoutDetailsForm.personalIdNumberRequired": "Este campo es obligatorio", - "PayoutDetailsForm.personalIdNumberTitle": "Número de identificación personal", "PayoutDetailsForm.personalIdNumberValid": "Valor inválido", "PayoutDetailsForm.personalPhoneLabel": "Teléfono", "PayoutDetailsForm.personalPhonePlaceholder": "202-555-0102", @@ -662,7 +640,6 @@ "PayoutDetailsForm.stripeConnectedAccountTermsLink": "acuerdo de \"Cuenta Conectada de Stripe\"", "PayoutDetailsForm.stripeToSText": "Al guardar los datos, estás aceptando el {stripeConnectedAccountTermsLink}", "PayoutDetailsForm.submitButtonText": "Confirmar y publicar anuncio", - "PayoutDetailsForm.title": "Un detalle más: Preferencias de pago", "PriceFilter.clear": "Borrar", "PriceFilter.label": "Precio", "PriceFilter.labelSelectedPlain": "Precio: {minPrice} - {maxPrice}", @@ -735,7 +712,6 @@ "SearchFiltersMobile.filtersButtonLabel": "Filtros", "SearchFiltersMobile.foundResults": "{count, number} {count, plural, one {resultado} other {resultados}}", "SearchFiltersMobile.heading": "Filtrar saunas", - "SearchFiltersMobile.keywordLabel": "Palabra clave", "SearchFiltersMobile.loadingResults": "Cargando…", "SearchFiltersMobile.openMapView": "Mapa", "SearchFiltersMobile.noResults": "No hay resultados.", @@ -753,8 +729,6 @@ "SearchPage.schemaMapSearch": "búsqueda con mapa", "SearchPage.schemaTitle": "Resultados de búsqueda para {searchAddress} | {siteTitle}", "SearchPage.searchError": "La búsqueda ha fallado. Por favor, inténtalo de nuevo.", - "SearchResultsPanel.nextPage": "Página siguiente", - "SearchResultsPanel.previousPage": "Página anterior", "SectionHero.browseButton": "Ver todos los saunas", "SectionHero.subTitle": "La mayor comunidad en línea para rentar saunas en Finlandia.", "SectionHero.title": "Alquila saunas en cualquier parte", @@ -963,7 +937,6 @@ "StripePayoutPage.heading": "Detalles de pago", "StripePayoutPage.loadingData": "Cargando la información…", "StripePayoutPage.redirectingToStripe": "Has regresado prematuramente de Stripe. Redirigiéndote de vuelta…", - "StripePayoutPage.stripeNotConnected": "La información de pagos no se ha guardado. Por favor llena la forma para poder aceptar pagos en de tus anuncios.", "StripePayoutPage.submitButtonText": "Guardar", "StripePayoutPage.title": "Detalles de pago", "StripeConnectAccountStatusBox.editAccountButton": "Editar la cuenta de Stripe", @@ -1003,7 +976,6 @@ "TopbarMobileMenu.unauthorizedGreeting": "Hola,{lineBreak}¿quieres {signupOrLogin}?", "TopbarMobileMenu.yourListingsLink": "Tus anuncios", "TopbarSearchForm.placeholder": "Buscar saunas…", - "TopbarSearchForm.searchHelp": "Consejo: También puedes buscar saunas por código postal, por ejemplo \"00500\" o por barrio \"Sörnainen\".", "TransactionPage.deletedListing": "anuncio eliminado", "TransactionPage.fetchOrderFailed": "Error al obtener información de la orden.", "TransactionPage.fetchSaleFailed": "Error al obtener información de la transacción.", @@ -1013,7 +985,6 @@ "TransactionPanel.acceptButton": "Aceptar", "TransactionPanel.acceptSaleFailed": "No se ha podido aceptar la solicitud. Por favor, inténtalo de nuevo.", "TransactionPanel.activityHeading": "Actividad", - "TransactionPanel.bookingBreakdownTitle": "Resumen de la reserva", "TransactionPanel.customerBannedStatus": "El usuario que hizo la solicitud ha sido bloqueado.", "TransactionPanel.declineButton": "Rechazar", "TransactionPanel.declineSaleFailed": "No se ha podido rechazar la solicitud. Por favor, inténtalo de nuevo.", @@ -1023,7 +994,6 @@ "TransactionPanel.messageDeletedListing": "Sin embargo, el anuncio ha sido cerrado y ya no está disponible.", "TransactionPanel.messageLoadingFailed": "Error al cargar los mensajes. Por favor, actualiza la página e inténtalo otra vez.", "TransactionPanel.orderAcceptedSubtitle": "Tu solicitud de reserva de {listingLink} ha sido aceptada.", - "TransactionPanel.orderAcceptedTitle": "¡Enhorabuena, {customerName}!", "TransactionPanel.orderCancelledTitle": "{customerName}, tu reserva de {listingLink} ha sido cancelada.", "TransactionPanel.orderDeclinedTitle": "{customerName}, tu solicitud de reserva de {listingLink} ha sido rechazada.", "TransactionPanel.orderDeliveredTitle": "{customerName}, tu reserva de {listingLink} se ha completado.", @@ -1037,7 +1007,6 @@ "TransactionPanel.perDay": "por día", "TransactionPanel.perNight": "por noche", "TransactionPanel.perUnit": "por unidad", - "TransactionPanel.requestToBook": "Solicitud de reserva", "TransactionPanel.saleAcceptedTitle": "Has aceptado la solicitud de {customerName} para reservar {listingLink}.", "TransactionPanel.saleCancelledTitle": "La reserva de {customerName} para {listingLink} ha sido cancelada.", "TransactionPanel.saleDeclinedTitle": "La solicitud de {customerName} para reservar {listingLink} ha sido rechazada.", diff --git a/src/translations/fr.json b/src/translations/fr.json index 7c2ad35454..dbd9a54096 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -19,7 +19,6 @@ "ActivityFeed.transitionReview": "{ displayName } vous a laissé un commentaire. { reviewLink }", "AuthenticationPage.confirmSignupInfoText": "Veuillez vérifier que vos informations sont correctes.", "AuthenticationPage.confirmSignupWithIdpTitle": "S'inscrire avec {idp}", - "AuthenticationPage.emailAlreadyInUse": "Un compte utilisant cette adresse email existe déjà. Peut-être pourriez-vous essayer de vous connecter ?", "AuthenticationPage.fixEmail": "Oups, une erreur dans votre email ? {fixEmailLink}.", "AuthenticationPage.fixEmailLinkText": "La corriger", "AuthenticationPage.loginFailed": "Cette combinaison d'email et mot de passe n'existe pas. Pourriez-vous vérifier et essayer de nouveau ?", @@ -52,8 +51,6 @@ "BookingBreakdown.bookingStart": "Début de la réservation", "BookingBreakdown.commission": "Frais de service Saunatime *", "BookingBreakdown.commissionFeeNote": "* Ces frais nous aident à faire vivre le site et vous offrir le meilleur service possible !", - "BookingBreakdown.dayCount": "{count, number} {count, plural, one {jour} other {jours}}", - "BookingBreakdown.nightCount": "{count, number} {count, plural, one {nuit} other {nuits}}", "BookingBreakdown.pricePerDay": "Prix par jour", "BookingBreakdown.pricePerNight": "Prix par nuit", "BookingBreakdown.pricePerQuantity": "Prix par personne", @@ -74,7 +71,6 @@ "BookingDatesForm.listingCurrencyInvalid": "Oups, la devise de cette annone ne correspond pas à la devise de la place de marché.", "BookingDatesForm.listingPriceMissing": "Oups, cette annonce n'a pas de prix.", "BookingDatesForm.ownListing": "Vous ne pourrez pas réserver votre propre annonce.", - "BookingDatesForm.placeholder": "jj/mm/aaaa", "BookingDatesForm.priceBreakdownTitle": "Détails de la réservation", "BookingDatesForm.requestToBook": "Réserver", "BookingDatesForm.requiredDate": "Oups, vérifiez que la date est correcte", @@ -100,13 +96,11 @@ "CheckoutPage.initiateOrderError": "Le paiement a échoué. Veuillez retourner sur {listingLink} et essayer de nouveau. Si cela persiste, essayez de rafraîchir la page ou contactez les administrateurs de la place de marché.", "CheckoutPage.initiateOrderStripeError": "Le système de paiement a rencontré les erreurs suivantes : {stripeErrors}", "CheckoutPage.listingNotFoundError": "Hélas, cette annonce n'est plus disponible.", - "CheckoutPage.loadingData": "Chargement des données…", "CheckoutPage.paymentExpiredMessage": "Le paiement n'a pas été finalisé sous 15 minutes. Veuillez retourner à {listingLink} et essayer de nouveau.", "CheckoutPage.paymentInfo": "Vous ne serez facturé que si votre demande est acceptée par l'hôte.", "CheckoutPage.perDay": "par jour", "CheckoutPage.perNight": "par nuit", "CheckoutPage.perUnit": "par unité", - "CheckoutPage.priceBreakdownTitle": "Détails de la réservation", "CheckoutPage.providerStripeAccountMissingError": "L'auteur de l'annonce n'a pas ajouté ses coordonnées bancaires et l'annonce ne peut pas être réserver pour le moment.", "CheckoutPage.retrievingStripePaymentIntentFailed": "Oups, quelque chose n'a pas fonctionné. Veuillez rafraîchir la page et essayer de nouveau. Si l'erreur se reproduit, veuillez retourner sur {listingLink} et essayer de nouveau après 15 minutes.", "CheckoutPage.speculateFailedMessage": "Oups, quelque chose n'a pas fonctionné. Veuillez rafraîchir la page et essayer de nouveau.", @@ -158,10 +152,6 @@ "CookieConsent.continue": "OK", "CookieConsent.cookieLink": "des cookies", "CookieConsent.message": "Bienvenue sur Saunatime ! Nous utilisons {cookieLink} pour rendre votre visite plus agréable.", - "DateInput.clearDate": "Effacer la date", - "DateInput.closeDatePicker": "Fermer", - "DateInput.defaultPlaceholder": "Date", - "DateInput.screenReaderInputMessage": "Date", "EditListingAvailabilityForm.fetchMonthDataFailed": "Oups, impossible de charger les données pour {month}, veuillez essayer de nouveau.", "EditListingAvailabilityForm.availableDay": "Disponible", "EditListingAvailabilityForm.blockedDay": "Non disponible", @@ -203,7 +193,6 @@ "EditListingPage.titleCreateListing": "Créer une annonce", "EditListingPage.titleEditListing": "Modifier l'annonce", "EditListingPhotosForm.addImagesTip": "Un conseil : choisissez au moins 2 ou 3 photos présentant votre sauna sous différents angles pour bien montrer le lieu.", - "EditListingPhotosForm.bankAccountNumberRequired": "Vous devez ajouter un compte bancaire.", "EditListingPhotosForm.chooseImage": "+ Choisir une image…", "EditListingPhotosForm.imageRequired": "Vous devez ajouter au moins une image.", "EditListingPhotosForm.imageTypes": ".JPG, .GIF or .PNG. Max. 20 Mb", @@ -258,6 +247,8 @@ "EditListingWizard.tabLabelPolicy": "Règlement", "EditListingWizard.tabLabelPricing": "Prix", "EmailVerificationForm.finishAccountSetup": "Nous devons pouvoir vous envoyer des notifications par email. Pour cela, veuillez vérifier votre adresse email {email}.", + "EmailVerificationForm.noPendingText": "Tout semble parfait. Votre adresse email actuelle est {email}.{breakline}Il n'y a plus de raisons de rester ici – passez une bonne journée.", + "EmailVerificationForm.noPendingTitle": "Vous n'aviez aucune adresse email en attente de confirmation, {name}.", "EmailVerificationForm.successButtonText": "Continuer et explorer", "EmailVerificationForm.successText": "Cette fois c'est bon, tout est correct !", "EmailVerificationForm.successTitle": "Merci d'avoir vérifié votre adresse email, {name}.", @@ -277,8 +268,6 @@ "FieldBoolean.yes": "Oui", "FieldDateInput.clearDate": "Effacer la date", "FieldDateInput.closeDatePicker": "Fermer", - "FieldDateInput.invalidDate": "La date de début n'est pas valide", - "FieldDateInput.placeholderText": "Ajouter une date de début", "FieldDateInput.screenReaderInputMessage": "Date", "FieldDateRangeInput.clearDate": "Effacer la date", "FieldDateRangeInput.closeDatePicker": "Fermer", @@ -343,7 +332,6 @@ "KeywordFilter.labelSelected": "\"{labelText}\"", "KeywordFilter.placeholder": "Entrez un mot-clé…", "KeywordFilterPlainForm.labelSelected": "Mot-clé", - "KeywordFilterPlainForm.placeholder": "Entrez un mot-clé…", "LandingPage.schemaDescription": "Louez un sauna avec Saunatime ou gagnez de l'argent en partageant le vôtre !", "LandingPage.schemaTitle": "Louez un sauna où que vous soyez | {siteTitle}", "LayoutWrapperAccountSettingsSideNav.contactDetailsTabTitle": "Information de contact", @@ -386,7 +374,6 @@ "ListingPage.schemaTitle": "{title} - {price} | {siteTitle}", "ListingPage.viewImagesButton": "Voir les images ({count})", "ListingPage.yourHostHeading": "Votre hôte", - "LoadableComponentErrorBoundaryPage.title": "La page n'a pu être récupérée", "LoadableComponentErrorBoundaryPage.heading": "Oops, nous n'avons pas pu récupérer la page.", "LoadableComponentErrorBoundaryPage.description": "Il semble que l'application doive être mise à jour ! Veuillez rafraîchir la page ou retourner sur {link}.", "LoadableComponentErrorBoundaryPage.landingPageLink": "la page d'accueil", @@ -426,7 +413,6 @@ "ManageListingsPage.title": "Mes annonces", "ManageListingsPage.youHaveListings": "Vous avez {count} {count, plural, one {annonce} other {annonces}}", "ManageListingsPage.yourListings": "Mes annonces", - "MapPriceMarker.unsupportedPrice": "({currency})", "Modal.close": "FERMER", "Modal.closeModal": "Fermer la fenêtre", "ModalMissingInformation.checkInbox": "Jetez un œil à votre boîte email et vérifiez votre adresse {email}", @@ -510,12 +496,8 @@ "PaymentMethodsForm.billingDetailsNamePlaceholder": "Entrez votre nom…", "PaymentMethodsForm.paymentCardDetails": "Détails de la carte de paiement", "PaymentMethodsForm.genericError": "Erreur dans les informations de paiement. Veuillez essayer de nouveau.", - "PaymentMethodsForm.confirmCardPaymentError": "Nous n'avons pas pu valider votre méthode de paiement. Veuillez vérifier vos informations et essayer de nouveau.", "PaymentMethodsForm.infoText": "J'autorise Saunatime à envoyer des instructions à l'institution financière ayant fourni ma carte bancaire pour prélever des paiements depuis mon compte, en accord avec les termes d'utilisations avec vous.", - "PaymentMethodsForm.paymentHeading": "Paiement", "PaymentMethodsPage.heading": "Moyens de paiement", - "PaymentMethodsPage.loadingData": "Chargement des données…", - "PaymentMethodsPage.savedPaymentMethodTitle": "Détails de la carte de crédit", "PaymentMethodsForm.submitPaymentInfo": "Enregistrer la carte bancaire", "PaymentMethodsPage.title": "Moyens de paiement", "PayoutDetailsForm.accountOpenerInfoText": "Le propriétaire du compte doit être un individu ayant l'autorisation de signer au nom de l'entreprise.", @@ -526,7 +508,6 @@ "PayoutDetailsForm.additionalPersonLink": "Ajouter des propriétaires et directeurs", "PayoutDetailsForm.additionalPersonRemove": "Enlever un individu", "PayoutDetailsForm.additionalPersonTitle": "Propriétaire ou directeur", - "PayoutDetailsForm.addressTitle": "Adresse", "PayoutDetailsForm.bankDetails": "Détail du compte bancaire", "PayoutDetailsForm.birthdayDatePlaceholder": "jj", "PayoutDetailsForm.birthdayLabel": "Date de naissance", @@ -622,7 +603,6 @@ "PayoutDetailsForm.firstNamePlaceholder": "Prénom", "PayoutDetailsForm.firstNameRequired": "Ce champ est requis", "PayoutDetailsForm.individualAccount": "Je suis un particulier", - "PayoutDetailsForm.information": "Vous y êtes presque ! Pour pouvoir vous transférer vos gains, nous devons en apprendre un peu plus sur vous. Nous vous guiderons tout au long de la vérification de votre compte. Ces informations pourront ensuite être modifiées depuis les paramètres de votre compte dans l'onglet Détails de paiement.", "PayoutDetailsForm.lastNameLabel": "Nom", "PayoutDetailsForm.lastNamePlaceholder": "Nom", "PayoutDetailsForm.lastNameRequired": "Ce champ est requis", @@ -632,7 +612,6 @@ "PayoutDetailsForm.owner": "Propriétaire (25% ou plus)", "PayoutDetailsForm.ownershipPercentageLabel": "Pourcentage des parts", "PayoutDetailsForm.ownershipPercentagePlaceholder": "100", - "PayoutDetailsForm.personalDetailsAdditionalOwnerTitle": "Détails des autres propriétaires", "PayoutDetailsForm.personalDetailsTitle": "Détails", "PayoutDetailsForm.personalEmailLabel": "Email", "PayoutDetailsForm.personalEmailPlaceholder": "Adresse email", @@ -644,7 +623,6 @@ "PayoutDetailsForm.personalIdNumberPlaceholder.SG": "S8888888A", "PayoutDetailsForm.personalIdNumberPlaceholder.US": "1234", "PayoutDetailsForm.personalIdNumberRequired": "Ce champ est requis", - "PayoutDetailsForm.personalIdNumberTitle": "Numéro d'identité", "PayoutDetailsForm.personalIdNumberValid": "Valeur incorrecte", "PayoutDetailsForm.personalPhoneLabel": "Numéro de téléphone", "PayoutDetailsForm.personalPhonePlaceholder": "202-555-0102", @@ -662,7 +640,6 @@ "PayoutDetailsForm.stripeConnectedAccountTermsLink": "Stripe Connected Account Agreement", "PayoutDetailsForm.stripeToSText": "En enregistrant les informations, vous acceptez le {stripeConnectedAccountTermsLink}", "PayoutDetailsForm.submitButtonText": "Enregistrer et publier l'annonce", - "PayoutDetailsForm.title": "Encore une petite chose : vos préférences de paiements", "PriceFilter.clear": "Effacer", "PriceFilter.label": "Prix", "PriceFilter.labelSelectedPlain": "Prix : {minPrice} - {maxPrice}", @@ -735,7 +712,6 @@ "SearchFiltersMobile.filtersButtonLabel": "Filtres", "SearchFiltersMobile.foundResults": "{count, number} {count, plural, one {résultat} other {résultats}}", "SearchFiltersMobile.heading": "Filtrer les saunas", - "SearchFiltersMobile.keywordLabel": "Mot-clé", "SearchFiltersMobile.loadingResults": "Chargement…", "SearchFiltersMobile.openMapView": "Carte", "SearchFiltersMobile.noResults": "Aucun résultat.", @@ -753,8 +729,6 @@ "SearchPage.schemaMapSearch": "recherche sur la carte", "SearchPage.schemaTitle": "Résultats pour {searchAddress} | {siteTitle}", "SearchPage.searchError": "La recherché a échoué. Veuillez essayer de nouveau.", - "SearchResultsPanel.nextPage": "Page suivante", - "SearchResultsPanel.previousPage": "Page précédente", "SectionHero.browseButton": "Voir tous les saunas", "SectionHero.subTitle": "Le premier site de location de saunas en Finlande.", "SectionHero.title": "Louez un sauna où que vous soyez.", @@ -963,7 +937,6 @@ "StripePayoutPage.heading": "Détails de paiement", "StripePayoutPage.loadingData": "Chargement des données…", "StripePayoutPage.redirectingToStripe": "Vous êtes revenu un peu vite. Nous vous redirigeons vers Stripe…", - "StripePayoutPage.stripeNotConnected": "Les coordonnées bancaires n'ont pas pu être enregistrées. Veuillez remplir le formulaire pour pouvoir recevoir des paiements pour vos annonces.", "StripePayoutPage.submitButtonText": "Enregistrer", "StripePayoutPage.title": "Détails de paiement", "StripeConnectAccountStatusBox.editAccountButton": "Editer le compte Stripe", @@ -1003,7 +976,6 @@ "TopbarMobileMenu.unauthorizedGreeting": "Bonjour !{lineBreak}Il est possible de {signupOrLogin}.", "TopbarMobileMenu.yourListingsLink": "Mes annonces", "TopbarSearchForm.placeholder": "Recherchez un sauna…", - "TopbarSearchForm.searchHelp": "Un conseil : vous pouvez aussi rechercher des saunas par code postal, par exemple \"00500\" ou par ville \"Sörnäinen\".", "TransactionPage.deletedListing": "annonce supprimée", "TransactionPage.fetchOrderFailed": "Impossible de récupérer les informations de la réservation.", "TransactionPage.fetchSaleFailed": "Impossible de récupérer les informations de paiement.", @@ -1013,7 +985,6 @@ "TransactionPanel.acceptButton": "Accepter", "TransactionPanel.acceptSaleFailed": "Oups, impossible d'accepter. Veuillez essayer de nouveau.", "TransactionPanel.activityHeading": "Activité", - "TransactionPanel.bookingBreakdownTitle": "Détails de la réservation", "TransactionPanel.customerBannedStatus": "L'utilisateur a fait la demande mais a depuis été banni.", "TransactionPanel.declineButton": "Refuser", "TransactionPanel.declineSaleFailed": "Oups, impossible de refuser. Veuillez essayer de nouveau..", @@ -1023,7 +994,6 @@ "TransactionPanel.messageDeletedListing": "Cependant l'annonce est supprimée et ne peux plus être vue.", "TransactionPanel.messageLoadingFailed": "Quelque chose n'a pas fonctionné lors du chargement des messages. Veuillez rafraîchir la page et essayer de nouveau.", "TransactionPanel.orderAcceptedSubtitle": "Votre demande de réservation pour {listingLink} a été acceptée.", - "TransactionPanel.orderAcceptedTitle": "Bravo, {customerName} !", "TransactionPanel.orderCancelledTitle": "{customerName}, votre réservation pour {listingLink} a été annulée.", "TransactionPanel.orderDeclinedTitle": "{customerName}, votre demande de réservation pour {listingLink} a été refusée.", "TransactionPanel.orderDeliveredTitle": "{customerName}, votre réservation pour {listingLink} a été réalisée.", @@ -1037,7 +1007,6 @@ "TransactionPanel.perDay": "par jour", "TransactionPanel.perNight": "par nuit", "TransactionPanel.perUnit": "par unité", - "TransactionPanel.requestToBook": "Réserver", "TransactionPanel.saleAcceptedTitle": "Vous avez accepté la demande de réservation de {customerName} pour {listingLink}.", "TransactionPanel.saleCancelledTitle": "La réservation de {customerName} pour {listingLink} a été annulée.", "TransactionPanel.saleDeclinedTitle": "La demande de réservation de {customerName} pour {listingLink} a été rejetée.", diff --git a/yarn.lock b/yarn.lock index dcc82727d2..b08a5adac7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12797,10 +12797,10 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== -sharetribe-flex-sdk@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/sharetribe-flex-sdk/-/sharetribe-flex-sdk-1.15.0.tgz#847ca0d4ee45618532f82755c552b9ada8876105" - integrity sha512-J8kCzQfqf0L8d6VqrpCMqz589smsQeWDYjvQ9hoGcai7SOH/LlLZ2NFH2DgVMCvxnkImO2qplteLgfn89yYbVQ== +sharetribe-flex-sdk@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sharetribe-flex-sdk/-/sharetribe-flex-sdk-1.17.0.tgz#afda913f7b777fd1ed18b9891438b6b64be886fd" + integrity sha512-E7I6k9S9s5EOV3oCdBwsJOThIqSUaNLju8cAO0HE/YeHBaK2/5wiQrHhZe5dZdAgcKOkR+jVEfcj5XLVL00p8A== dependencies: axios "^0.21.1" js-cookie "^2.1.3"