Configuring webhook
Setup webhook to automatically import Smartling translations back into Uniform.
Smartling integration settings has Webhook URL input where you need to provide publicly avaiable url where Smartling is going to push completed translation job.
You can find example of implementation in this example.
Setup#
You would need Smartling integration to be installed and configured. Integration settings page has a Webhook URL settings, which will be passed down as callbackMethod
into Smartling's Create job api method
You can find a full example of implementation in this examples repo
Lets create a webhook that can handle composition or entry translations coming back from Smartling.
First we need Smartling and Uniform credentials:
# Authentication to Smartling SMARTLING_ACCOUNT_ID= SMARTLING_PROJECT_ID= SMARTLING_USER_ID= SMARTLING_USER_SECRET= # Authentication to Uniform UNIFORM_API_KEY= UNIFORM_PROJECT_ID=
Uniform API Keys
Here you can find how to create Uniform API Keys. Smartling integration requires at least Create and Update permissions for Compositions / Entries / Assets
We would also need Uniform and Smartling deps
npm i @uniformdev/canvas @uniformdev/tms-sdk smartling-api-sdk-nodejsSmartling Integration is using Job Batches v.2 flow sending JSON files for translations. So we will retrieve translated files back because they also contain some metadata required for us to correcly import back to Uniform Smartling will call our callback with
{ translationJobUid, type }
parameters when Translation Job changes its status. we only care about completed jobsconst { type, translationJobUid } = req.query; if (type !== 'job.completed') { console.log('We only react on comleted jobs'); res.status(200).send('We only react on comleted jobs'); return; }Lets retrieve latest completed batch from the job
const apiBuilder = new SmartlingApiClientBuilder() .setBaseSmartlingApiUrl('https://api.smartling.com') .authWithUserIdAndUserSecret(userId!, userSecret!); const jobsClient = apiBuilder.build(SmartlingJobsApi); const jobBatchesClient = apiBuilder.build(SmartlingJobBatchesApi); const lastBatch = await jobBatchesClient.listBatches( projectId, new ListBatchesParameters({ translationJobUid, limit: 1, status: 'COMPLETED' }) ); const batchInfo = lastBatch.items[0]; const batchDetails = await jobBatchesClient.getBatchStatus(projectId, batchInfo.batchUid);Now we can load all translated files and start processing them
const filesClient = apiBuilder.build(SmartlingFilesApi); const translationsStatus: Record<string, boolean> = {}; for (const fileInfo of batchDetails.files) { const downloadParameters = new DownloadFileParameters({ retrievalType: 'published' }); const file = await filesClient.downloadFile( projectId, fileInfo.fileUri, fileInfo.targetLocales[0].localeId, downloadParameters); try { const translationPayload = JSON.parse(file.toString()); const uniformProjectId = translationPayload.metadata.uniformProjectId; const uniformReleaseId = translationPayload.metadata.uniformReleaseId; const uniformEntityType = translationPayload.metadata.entityType; const uniformEntityId = translationPayload.metadata.entity.id; // Now we have everything we need to use tms-sdk package helpers ...Now lets merge translation into Uniform entities
const translationPayload = JSON.parse(file.toString()); const uniformProjectId = translationPayload.metadata.uniformProjectId; const uniformReleaseId = translationPayload.metadata.uniformReleaseId; const uniformEntityType = translationPayload.metadata.entityType; const uniformEntityId = translationPayload.metadata.entity.id; const canvasClient = new CanvasClient({ projectId: uniformProjectId, apiKey: uniformAPIKey, bypassCache: true, apiHost: uniformCLIBaseUrl, }); const { translationMerged } = await mergeTranslationToUniform({ canvasClient, contentClient, translationPayload, updateComposition: async ({ canvasClient, composition }) => { await canvasClient.updateComposition(composition); return true; }, onNotFound: ({ translationPayload }) => { const entityType = translationPayload.metadata.entityType; const entityId = translationPayload.metadata.entity.id; console.log(`skip: can not find ${entityType} (${entityId})`); }, onNotTranslatedResult: ({ updated, errorKind, errorText }) => { if (!updated) { console.log('Translation has no updates'); } else if (errorKind !== undefined) { console.warn(errorText || 'Unknown error'); } }, });If everything goes fine - lets close Smartling job:
await jobsClient.closeJob(projectId, translationJobUid as string, new CloseJobParameters({})); res.status(200).json('Ok');