diff --git a/src/languages/es.js b/src/languages/es.js index df6d010ee03..ac0378dd024 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -4,6 +4,7 @@ export default { cancel: 'Cancelar', yes: 'Si', no: 'No', + ok: 'OK', attachment: 'Archivo Adjunto', to: 'A', optional: 'Opcional', @@ -21,10 +22,14 @@ export default { not: 'No', signIn: 'Conectarse', continue: 'Continuar', + firstName: 'Primer nombre', + lastName: 'Apellido', phoneNumber: 'Número de teléfono', email: 'Email', and: 'y', details: 'Detalles', + privacy: 'Intimidad', + privacyPolicy: 'Política de privacidad', delete: 'Eliminar', deleted: 'eliminado', contacts: 'Contactos', @@ -105,11 +110,14 @@ export default { writeSomething: 'Escribe algo...', blockedFromConcierge: 'Comunicación no permitida', youAppearToBeOffline: 'Parece que estás desconectado.', + fileUploadFailed: 'Subida fallida. El archivo no es compatible.', roomIsArchived: 'Esta sala de chat ha sido eliminada', }, - reportActionContextMenu: { + contextMenuItem: { copyToClipboard: 'Copiar al Portapapeles', copied: '¡Copiado!', + }, + reportActionContextMenu: { copyLink: 'Copiar Enlace', markAsUnread: 'Marcar como no leído', editComment: 'Editar Commentario', @@ -138,6 +146,7 @@ export default { confirm: 'Confirmar', splitBill: 'Dividir Factura', requestMoney: 'Pedir Dinero', + sendMoney: 'Enviar Dinero', pay: 'Pagar', viewDetails: 'Ver detalles', settleExpensify: 'Pagar con Expensify', @@ -149,6 +158,7 @@ export default { owes: ({manager, owner}) => `${manager} debe a ${owner}`, paid: ({owner, manager}) => `${manager} pagó a ${owner}`, split: ({amount}) => `Dividir ${amount}`, + send: ({amount}) => `Enviar ${amount}`, choosePaymentMethod: 'Elige el método de pago:', noReimbursableExpenses: 'El monto de este informe es inválido', }, @@ -172,9 +182,7 @@ export default { profilePage: { profile: 'Perfil', tellUsAboutYourself: '¡Cuéntanos algo sobre tí, nos encantaría conocerte!', - firstName: 'Nombre', john: 'Juan', - lastName: 'Apellidos', doe: 'Nadie', preferredPronouns: 'Pronombres preferidos', selectYourPronouns: 'Selecciona tus pronombres', @@ -233,7 +241,8 @@ export default { enterYourUsernameToGetPaidViaPayPal: 'Escribe tu nombre de usuario para que otros puedan pagarte a través de PayPal.', payPalMe: 'PayPal.me/', yourPayPalUsername: 'Tu usuario de PayPal', - addPayPalAccount: 'Agregar cuenta de PayPal', + addPayPalAccount: 'Agregar Cuenta de PayPal', + growlMessageOnSave: 'Su nombre de usuario de PayPal se agregó correctamente', editPayPalAccount: 'Actualizar cuenta de PayPal', }, paymentsPage: { @@ -263,6 +272,14 @@ export default { expensifyIsOpenSource: 'Expensify.cash es open source', theCode: 'el código', openJobs: 'trabajos disponibles', + heroHeading: 'Dividir cuentas\ny chatear con amigos.', + heroDescription: { + phrase1: 'El dinero habla. Y ahora que el chat y los pagos están en un solo lugar, también es fácil. Sus pagos le llegan tan rápido como puede transmitir su punto.', + phrase2: 'New Expensify es de código abierto. Vista', + phrase3: 'el código', + phrase4: 'Vista', + phrase5: 'vacantes', + }, }, termsOfUse: { phrase1: 'Al usar Expensify.cash, estás aceptando los', @@ -271,9 +288,11 @@ export default { phrase4: 'política de privacidad', phrase5: 'El envío de dinero es brindado por Expensify Payments LLC (NMLS ID:2017010) de conformidad con sus', phrase6: 'licencias', + phrase7: 'licenses', }, passwordForm: { pleaseFillOutAllFields: 'Por favor completa todos los campos', + enterYourTwoFactorAuthenticationCodeToContinue: 'Ingrese su código de autenticación de dos factores para continuar', forgot: '¿Te has olvidado?', twoFactorCode: 'Autenticación de 2 factores', requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado', @@ -309,7 +328,10 @@ export default { }, setPasswordPage: { enterPassword: 'Escribe una contraseña', + confirmNewPassword: 'Confirma la contraseña', setPassword: 'Configura tu Contraseña', + passwordsDontMatch: 'Las contraseñas deben coincidir', + newPasswordPrompt: 'Su contraseña debe tener al menos 8 caracteres, \n1 letra mayúscula, 1 letra minúscula, 1 número.', }, bankAccount: { accountNumber: 'Número de cuenta', @@ -372,6 +394,68 @@ export default { noPhoneNumber: 'Por favor escribe un número de teléfono que incluya el código de país e.g +447814266907', maxParticipantsReached: 'Has llegado al número máximo de participantes para un grupo.', }, + onfidoStep: { + acceptTerms: 'Al continuar con la solicitud para activar su billetera Expensify, confirma que ha leído, comprende y acepta ', + facialScan: 'Política y lanzamiento de la exploración facial de Onfido', + tryAgain: 'Intentar otra vez', + verifyIdentity: 'Verificar identidad', + genericError: 'Hubo un error al procesar este paso. Inténtalo de nuevo.', + }, + additionalDetailsStep: { + headerTitle: 'Detalles adicionales', + helpText: 'Necesitamos confirmar la siguiente información antes de que podamos procesar este pago.', + helpLink: 'Obtenga más información sobre por qué necesitamos esto.', + legalFirstNameLabel: 'Primer nombre legal', + legalMiddleNameLabel: 'Segundo nombre legal', + legalLastNameLabel: 'Apellido legal', + }, + termsStep: { + headerTitle: 'Condiciones y tarifas', + haveReadAndAgree: 'He leído y acepto recibir ', + electronicDisclosures: 'divulgaciones electrónicas', + agreeToThe: 'Estoy de acuerdo con la ', + walletAgreement: 'Acuerdo de billetera', + enablePayments: 'Habilitar pagos', + termsMustBeAccepted: 'Se deben aceptar los términos', + }, + activateStep: { + headerTitle: 'Habilitar pagos', + activated: 'Su billetera Expensify está lista para usar.', + checkBackLater: 'Todavía estamos revisando tu información. Por favor, vuelva más tarde.', + }, + companyStep: { + headerTitle: 'Información de la Empresa', + subtitle: 'Dé más información sobre su empresa.', + legalBusinessName: 'Nombre Comercial Legal', + companyWebsite: 'Company Website', + taxIDNumber: 'Tax ID Number', + companyType: 'Página Web de la Empresa', + incorporationDate: 'Fecha de Incorporación', + industryClassificationCode: 'Código de Clasificación Industrial', + confirmCompanyIsNot: 'Confirmo que esta empresa no está en el', + listOfRestrictedBusinesses: 'lista de negocios restringidos', + incorporationDatePlaceholder: 'Fecha de inicio (aaaa-mm-dd)', + companyPhonePlaceholder: '10 dígitos, sin guiones', + }, + requestorStep: { + headerTitle: 'Información del solicitante', + financialRegulations: 'Las leyes fiscales y el reglamento bancario nos obliga a verificar la identidad de todo individuo que desee añadir una cuenta bancaria representando a una compañía. ', + learnMore: 'Más información', + isMyDataSafe: '¿Están seguros mis datos?', + onFidoConditions: 'Al continuar con la solicitud de añadir esta cuenta bancaria, confirma que ha leído, entiende y acepta ', + onFidoFacialScan: 'Onfido’s Facial Scan Policy and Release', + isControllingOfficer: 'Estoy autorizado a utilizar la cuenta bancaria de mi compañía para gastos de empresa', + isControllingOfficerError: 'Debe ser un oficial controlador con autorización para operar la cuenta bancaria de la compañía', + }, + validationStep: { + headerTitle: 'Validar', + buttonText: 'Finalizar Configuración', + maxAttemptError: 'Se ha inhabilitado la validación de esta cuenta bancaria, debido a demasiados intentos incorrectos. Por favor contáctenos.', + description: 'Uno o dos días después de agregar su cuenta a Expensify, enviamos tres (3) transacciones a su cuenta. Tienen una línea comercial como "Expensify, Inc. Validation"', + descriptionCTA: 'Ingrese el monto de cada transacción en los campos a continuación. Ejemplo: 1.51', + reviewingInfo: '¡Gracias! Estamos revisando tu información y nos comunicaremos contigo en breve. Consulte su chat con Concierge ', + forNextSteps: ' para conocer los próximos pasos para terminar de configurar su cuenta bancaria.', + }, beneficialOwnersStep: { beneficialOwners: 'Beneficial Owners', additionalInformation: 'Additional Information', @@ -433,39 +517,6 @@ export default { welcomeNote: ({workspaceName}) => `¡Has sido invitado a la ${workspaceName} Espacio de trabajo! Descargue la aplicación móvil Expensify para comenzar a rastrear sus gastos.`, }, }, - companyStep: { - headerTitle: 'Información de la Empresa', - subtitle: 'Dé más información sobre su empresa.', - legalBusinessName: 'Nombre Comercial Legal', - companyWebsite: 'Company Website', - taxIDNumber: 'Tax ID Number', - companyType: 'Página Web de la Empresa', - incorporationDate: 'Fecha de Incorporación', - industryClassificationCode: 'Código de Clasificación Industrial', - confirmCompanyIsNot: 'Confirmo que esta empresa no está en el', - listOfRestrictedBusinesses: 'lista de negocios restringidos', - incorporationDatePlaceholder: 'Fecha de inicio (aaaa-mm-dd)', - companyPhonePlaceholder: '10 dígitos, sin guiones', - }, - validationStep: { - headerTitle: 'Validar', - buttonText: 'Finalizar Configuración', - maxAttemptError: 'Se ha inhabilitado la validación de esta cuenta bancaria, debido a demasiados intentos incorrectos. Por favor contáctenos.', - description: 'Uno o dos días después de agregar su cuenta a Expensify, enviamos tres (3) transacciones a su cuenta. Tienen una línea comercial como "Expensify, Inc. Validation"', - descriptionCTA: 'Ingrese el monto de cada transacción en los campos a continuación. Ejemplo: 1.51', - reviewingInfo: '¡Gracias! Estamos revisando tu información y nos comunicaremos contigo en breve. Consulte su chat con Concierge ', - forNextSteps: ' para conocer los próximos pasos para terminar de configurar su cuenta bancaria.', - }, - requestorStep: { - headerTitle: 'Información del solicitante', - financialRegulations: 'Las leyes fiscales y el reglamento bancario nos obliga a verificar la identidad de todo individuo que desee añadir una cuenta bancaria representando a una compañía. ', - learnMore: 'Más información', - isMyDataSafe: '¿Están seguros mis datos?', - onFidoConditions: 'Al continuar con la solicitud de añadir esta cuenta bancaria, confirma que ha leído, entiende y acepta ', - facialScan: 'la política de reconocimiento facial y la exención de Onfido', - isControllingOfficer: 'Estoy autorizado a utilizar la cuenta bancaria de mi compañía para gastos de empresa', - isControllingOfficerError: 'Debe ser un oficial controlador con autorización para operar la cuenta bancaria de la compañía', - }, requestCallPage: { requestACall: 'Llámame por teléfono', description: '¿Necesitas ayuda configurando tu cuenta? Nuestro equipo de guías puede ayudarte.', diff --git a/tests/unit/TranslateTest.js b/tests/unit/TranslateTest.js index f46a93bc3ad..5d85bcd3f33 100644 --- a/tests/unit/TranslateTest.js +++ b/tests/unit/TranslateTest.js @@ -1,7 +1,10 @@ +const _ = require('underscore'); +const {error: AnnotationError} = require('@actions/core'); const translate = require('../../src/libs/translate'); -const translations = require('../../src/languages/translations'); const CONFIG = require('../../src/CONFIG'); +const translations = require('../../src/languages/translations'); +const originalTranslations = _.clone(translations); translations.default = { en: { testKey1: 'English', @@ -52,3 +55,44 @@ describe('translate', () => { expect(translate.translate('en', ['testKeyGroup', 'testFunction'], {testVariable})).toBe(expectedValue); }); }); + +describe('Translation Keys', () => { + function traverseKeyPath(source, path, keyPaths) { + const pathArray = keyPaths || []; + const keyPath = path ? `${path}.` : ''; + _.each(_.keys(source), (key) => { + if (_.isObject(source[key]) && !_.isFunction(source[key])) { + traverseKeyPath(source[key], keyPath + key, pathArray); + } else { + pathArray.push(keyPath + key); + } + }); + return pathArray; + } + const excludeLanguages = ['en', 'es-ES']; + const languages = _.without(_.keys(originalTranslations.default), ...excludeLanguages); + const mainLanguage = originalTranslations.default.en; + const mainLanguageKeys = traverseKeyPath(mainLanguage); + + _.each(languages, (ln) => { + const languageKeys = traverseKeyPath(originalTranslations.default[ln]); + + it(`Does ${ln} locale has all the keys`, () => { + const hasAllKeys = _.difference(mainLanguageKeys, languageKeys); + if (hasAllKeys.length) { + console.debug(`🏹 [ ${hasAllKeys.join(', ')} ] are missing from ${ln}.js`); + AnnotationError(`🏹 [ ${hasAllKeys.join(', ')} ] are missing from ${ln}.js`); + } + expect(hasAllKeys).toEqual([]); + }); + + it(`Does ${ln} locale has unused keys`, () => { + const hasAllKeys = _.difference(languageKeys, mainLanguageKeys); + if (hasAllKeys.length) { + console.debug(`🏹 [ ${hasAllKeys.join(', ')} ] are unused keys in ${ln}.js`); + AnnotationError(`🏹 [ ${hasAllKeys.join(', ')} ] are unused keys in ${ln}.js`); + } + expect(hasAllKeys).toEqual([]); + }); + }); +});