Skip to content

[No QA] Add test for matching Translations keys #4090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 88 additions & 35 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,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',
contacts: 'Contactos',
recents: 'Recientes',
Expand Down Expand Up @@ -103,10 +107,13 @@ export default {
writeSomething: 'Escribe algo...',
blockedFromConcierge: 'Comunicación no permitida',
youAppearToBeOffline: 'Parece que estás desconectado.',
fileUploadFailed: 'Subida fallida. El archivo no es compatible.',
},
reportActionContextMenu: {
contextMenuItem: {
copyToClipboard: 'Copiar al Portapapeles',
copied: '¡Copiado!',
},
reportActionContextMenu: {
copyLink: 'Copiar Enlace',
markAsUnread: 'Marcar como no leído',
editComment: 'Editar Commentario',
Expand Down Expand Up @@ -135,6 +142,7 @@ export default {
confirm: 'Confirmar',
splitBill: 'Dividir Factura',
requestMoney: 'Pedir Dinero',
sendMoney: 'Enviar Dinero',
pay: 'Pagar',
viewDetails: 'Ver detalles',
settleExpensify: 'Pagar con Expensify',
Expand All @@ -146,6 +154,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',
},
Expand Down Expand Up @@ -230,7 +239,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',
},
paymentMethodList: {
addPaymentMethod: 'Agrega método de pago',
Expand All @@ -255,6 +265,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',
Expand All @@ -263,9 +281,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',
Expand Down Expand Up @@ -301,7 +321,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',
Expand Down Expand Up @@ -356,6 +379,69 @@ 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',
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',
},
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',
Expand Down Expand Up @@ -416,39 +502,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.',
Expand Down
39 changes: 38 additions & 1 deletion tests/unit/TranslateTest.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const _ = require('underscore');
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',
Expand Down Expand Up @@ -52,3 +54,38 @@ describe('translate', () => {
expect(translate.translate('en', ['testKeyGroup', 'testFunction'], {testVariable})).toBe(expectedValue);
});
});

describe('Translation Keys', () => {
let activeLanguage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this "global"? Can't we pass it as a regular argument to matchKeys?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is convenient.

let path = '';
function matchKeys(source, target, key) {
path += key ? `${key}.` : '';
const pathLevel = path;
if (key && !_.has(target, key)) {
console.debug(`🏹 ${path.slice(0, -1)} is missing from ${activeLanguage}.js`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a debug log line, can we make it so that the test itself fails? Ideally it would show one failed test per missing key if that's possible, but if that's hard we can have one test fail that prints all missing keys.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test will fail, if key don't match. This log is just to show what is missing.

Ideally it would show one failed test per missing key if that's possible,

I don't think its a good idea to create multiple tests that basically test the same thing.

if that's hard we can have one test fail that prints all missing keys.

This can be done. We can log all the missing keys.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok cool, my main point is that it's better to have the test failure show what keys failed instead of having to look at the logs.

Copy link
Member Author

@parasharrajat parasharrajat Jul 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to find way to add the missing key as failure message itself but I don't think its possible AFAIK. So we have to stick with logs. Could you please give me a hint about how to do that if possible?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmmm, I think that if you change the expect from toBeTruthy to something like arrayContaining would make jest to print the diff of the arrays and thus the missing keys?

return;
}
const sourceOBJ = key ? source[key] : source;
const targetOBJ = key ? target[key] : target;
if (_.isObject(sourceOBJ) && !_.isFunction(sourceOBJ)) {
return _.every(_.keys(sourceOBJ), (subKey) => {
path = pathLevel;
return matchKeys(sourceOBJ, targetOBJ, subKey);
});
}
if (key) {
path = path.slice(0, -(key.length - 1));
}
return true;
}
it('Does each locale has all the keys', () => {
const excludeLanguages = ['en', 'es-ES'];
const languages = _.without(_.keys(originalTranslations.default), ...excludeLanguages);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this smarter and instead of having to add es-ES here make it skip any file that has 2 components? ie: pick up es but not es-ES

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was not the original Idea. After Sometime, we can decide to add more Translations. and Two component locale would be completely valid in that case. So to skip regardless of specification will lead to code change. So I think its better, not to make it smarter. It is sufficient to fulfill its purpose.

const parentLanguage = originalTranslations.default.en;
const hasAllKeys = _.every(languages, (ln) => {
activeLanguage = ln;
return matchKeys(parentLanguage, originalTranslations.default[ln]);
});
expect(hasAllKeys).toBeTruthy();
});
});