diff --git a/package.json b/package.json
index 6f4dc668f..0fec4efac 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"@oclif/config": "^1.18.1",
"@salesforce/command": "^4.2.0",
"@salesforce/core": "^2.31.0",
- "@salesforce/source-deploy-retrieve": "^5.9.3",
+ "@salesforce/source-deploy-retrieve": "^5.9.5",
"@salesforce/source-tracking": "^1.0.0",
"chalk": "^4.1.2",
"cli-ux": "^5.6.3",
@@ -173,8 +173,9 @@
"test:nuts:manifest:create": "mocha \"test/nuts/create.nut.ts\" --slow 4500 --timeout 600000 --retries 0",
"test:nuts:mdapi": "mocha \"test/nuts/mdapi.nut.ts\" --slow 3000 --timeout 600000 --retries 0",
"test:nuts:retrieve": "cross-env PLUGIN_SOURCE_SEED_FILTER=retrieve ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0",
- "test:nuts:specialTypes": "mocha \"test/nuts/territory2.nut.ts\" \"test/nuts/folderTypes.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel",
+ "test:nuts:specialTypes": "mocha \"test/nuts/territory2.nut.ts\" \"test/nuts/folderTypes.nut.ts\" \"test/nuts/translation.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel",
"test:nuts:territory2": "mocha \"test/nuts/territory2.nut.ts\" --slow 4500 --timeout 600000 --retries 0",
+ "test:nuts:translations": "mocha \"test/nuts/translation.nut.ts\" --slow 4500 --timeout 600000 --retries 0",
"test:nuts:tracking": "mocha \"test/nuts/trackingCommands/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
"test:nuts:tracking:basics": "mocha \"test/nuts/trackingCommands/basics.nut.ts\" --slow 3000 --timeout 600000 --retries 0",
"test:nuts:tracking:conflicts": "mocha \"test/nuts/trackingCommands/conflicts.nut.ts\" --slow 3000 --timeout 600000 --retries 0",
diff --git a/test/nuts/customTranslationProject/config/project-scratch-def.json b/test/nuts/customTranslationProject/config/project-scratch-def.json
new file mode 100644
index 000000000..1e8b4448e
--- /dev/null
+++ b/test/nuts/customTranslationProject/config/project-scratch-def.json
@@ -0,0 +1,16 @@
+{
+ "orgName": "Custom Fields company",
+ "edition": "Developer",
+ "features": ["EnableSetPasswordInApi"],
+ "settings": {
+ "lightningExperienceSettings": {
+ "enableS1DesktopEnabled": true
+ },
+ "mobileSettings": {
+ "enableS1EncryptedStoragePref2": false
+ },
+ "languageSettings": {
+ "enableTranslationWorkbench": true
+ }
+ }
+}
diff --git a/test/nuts/customTranslationProject/force-app/main/default/layouts/customObject__c-customObject Layout.layout-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/layouts/customObject__c-customObject Layout.layout-meta.xml
new file mode 100644
index 000000000..fdd4bb50d
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/layouts/customObject__c-customObject Layout.layout-meta.xml
@@ -0,0 +1,59 @@
+
+
+
+ false
+ false
+ true
+
+
+
+ Required
+ Name
+
+
+ Readonly
+ customField__c
+
+
+
+
+ Edit
+ OwnerId
+
+
+
+
+
+ false
+ false
+ true
+
+
+
+ Readonly
+ CreatedById
+
+
+
+
+ Readonly
+ LastModifiedById
+
+
+
+
+
+ false
+ false
+ true
+
+
+
+
+
+ false
+ false
+ false
+ false
+ false
+
diff --git a/test/nuts/customTranslationProject/force-app/main/default/objectTranslations/customObject__c-es/customField__c.fieldTranslation-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/objectTranslations/customObject__c-es/customField__c.fieldTranslation-meta.xml
new file mode 100644
index 000000000..125d3cd88
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/objectTranslations/customObject__c-es/customField__c.fieldTranslation-meta.xml
@@ -0,0 +1,5 @@
+
+
+
+ customField__c
+
diff --git a/test/nuts/customTranslationProject/force-app/main/default/objectTranslations/customObject__c-es/customObject__c-es.objectTranslation-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/objectTranslations/customObject__c-es/customObject__c-es.objectTranslation-meta.xml
new file mode 100644
index 000000000..5283bb64b
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/objectTranslations/customObject__c-es/customObject__c-es.objectTranslation-meta.xml
@@ -0,0 +1,13 @@
+
+
+
+ false
+
+
+
+ true
+
+
+
+
+
diff --git a/test/nuts/customTranslationProject/force-app/main/default/objects/customObject__c/customObject__c.object-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/objects/customObject__c/customObject__c.object-meta.xml
new file mode 100644
index 000000000..a309f2bfd
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/objects/customObject__c/customObject__c.object-meta.xml
@@ -0,0 +1,165 @@
+
+
+
+ Accept
+ Default
+
+
+ Accept
+ Large
+ Default
+
+
+ Accept
+ Small
+ Default
+
+
+ CancelEdit
+ Default
+
+
+ CancelEdit
+ Large
+ Default
+
+
+ CancelEdit
+ Small
+ Default
+
+
+ Clone
+ Default
+
+
+ Clone
+ Large
+ Default
+
+
+ Clone
+ Small
+ Default
+
+
+ Delete
+ Default
+
+
+ Delete
+ Large
+ Default
+
+
+ Delete
+ Small
+ Default
+
+
+ Edit
+ Default
+
+
+ Edit
+ Large
+ Default
+
+
+ Edit
+ Small
+ Default
+
+
+ List
+ Default
+
+
+ List
+ Large
+ Default
+
+
+ List
+ Small
+ Default
+
+
+ New
+ Default
+
+
+ New
+ Large
+ Default
+
+
+ New
+ Small
+ Default
+
+
+ SaveEdit
+ Default
+
+
+ SaveEdit
+ Large
+ Default
+
+
+ SaveEdit
+ Small
+ Default
+
+
+ Tab
+ Default
+
+
+ Tab
+ Large
+ Default
+
+
+ Tab
+ Small
+ Default
+
+
+ View
+ Default
+
+
+ View
+ Large
+ Default
+
+
+ View
+ Small
+ Default
+
+ false
+ SYSTEM
+ Deployed
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ Private
+
+
+
+ Text
+
+ customObjects
+
+ ReadWrite
+ Public
+
diff --git a/test/nuts/customTranslationProject/force-app/main/default/objects/customObject__c/fields/customField__c.field-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/objects/customObject__c/fields/customField__c.field-meta.xml
new file mode 100644
index 000000000..76c0b9db7
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/objects/customObject__c/fields/customField__c.field-meta.xml
@@ -0,0 +1,8 @@
+
+
+ customField__c
+ false
+
+ false
+ AutoNumber
+
diff --git a/test/nuts/customTranslationProject/force-app/main/default/profiles/Admin.profile-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/profiles/Admin.profile-meta.xml
new file mode 100644
index 000000000..50d1d565e
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/profiles/Admin.profile-meta.xml
@@ -0,0 +1,734 @@
+
+
+ false
+
+ false
+ customObject__c.customField__c
+ true
+
+
+ customObject__c-customObject Layout
+
+
+ true
+ true
+ true
+ true
+ true
+
+ true
+
+ Salesforce
+
+ true
+ AIViewInsightObjects
+
+
+ true
+ ActivateContract
+
+
+ true
+ ActivateOrder
+
+
+ true
+ ActivitiesAccess
+
+
+ true
+ AddDirectMessageMembers
+
+
+ true
+ AllowUniversalSearch
+
+
+ true
+ AllowViewKnowledge
+
+
+ true
+ ApexRestServices
+
+
+ true
+ ApiEnabled
+
+
+ true
+ AssignPermissionSets
+
+
+ true
+ AssignTopics
+
+
+ true
+ AuthorApex
+
+
+ true
+ BulkMacrosAllowed
+
+
+ true
+ CanInsertFeedSystemFields
+
+
+ true
+ CanUseNewDashboardBuilder
+
+
+ true
+ CanVerifyComment
+
+
+ true
+ ChangeDashboardColors
+
+
+ true
+ ChatterEditOwnPost
+
+
+ true
+ ChatterEditOwnRecordPost
+
+
+ true
+ ChatterFileLink
+
+
+ true
+ ChatterInternalUser
+
+
+ true
+ ChatterInviteExternalUsers
+
+
+ true
+ ChatterOwnGroups
+
+
+ true
+ ConnectOrgToEnvironmentHub
+
+
+ true
+ ConsentApiUpdate
+
+
+ true
+ ContentAdministrator
+
+
+ true
+ ContentWorkspaces
+
+
+ true
+ ConvertLeads
+
+
+ true
+ CreateCustomizeDashboards
+
+
+ true
+ CreateCustomizeFilters
+
+
+ true
+ CreateCustomizeReports
+
+
+ true
+ CreateDashboardFolders
+
+
+ true
+ CreateLtngTempFolder
+
+
+ true
+ CreateReportFolders
+
+
+ true
+ CreateTopics
+
+
+ true
+ CreateWorkBadgeDefinition
+
+
+ true
+ CreateWorkspaces
+
+
+ true
+ CustomizeApplication
+
+
+ true
+ DataExport
+
+
+ true
+ DelegatedTwoFactor
+
+
+ true
+ DeleteActivatedContract
+
+
+ true
+ DeleteTopics
+
+
+ true
+ DistributeFromPersWksp
+
+
+ true
+ EditActivatedOrders
+
+
+ true
+ EditBrandTemplates
+
+
+ true
+ EditCaseComments
+
+
+ true
+ EditEvent
+
+
+ true
+ EditHtmlTemplates
+
+
+ true
+ EditKnowledge
+
+
+ true
+ EditMyDashboards
+
+
+ true
+ EditMyReports
+
+
+ true
+ EditOppLineItemUnitPrice
+
+
+ true
+ EditPublicDocuments
+
+
+ true
+ EditPublicFilters
+
+
+ true
+ EditPublicTemplates
+
+
+ true
+ EditReadonlyFields
+
+
+ true
+ EditTask
+
+
+ true
+ EditTopics
+
+
+ true
+ EmailMass
+
+
+ true
+ EmailSingle
+
+
+ true
+ EnableCommunityAppLauncher
+
+
+ true
+ EnableNotifications
+
+
+ true
+ ExportReport
+
+
+ true
+ FieldServiceAccess
+
+
+ true
+ GiveRecognitionBadge
+
+
+ true
+ ImportCustomObjects
+
+
+ true
+ ImportLeads
+
+
+ true
+ ImportPersonal
+
+
+ true
+ InstallPackaging
+
+
+ true
+ LightningConsoleAllowedForUser
+
+
+ true
+ LightningExperienceUser
+
+
+ true
+ ListEmailSend
+
+
+ true
+ ManageAnalyticSnapshots
+
+
+ true
+ ManageAuthProviders
+
+
+ true
+ ManageBusinessHourHolidays
+
+
+ true
+ ManageC360AConnections
+
+
+ true
+ ManageCMS
+
+
+ true
+ ManageCallCenters
+
+
+ true
+ ManageCases
+
+
+ true
+ ManageCategories
+
+
+ true
+ ManageCertificates
+
+
+ true
+ ManageContentPermissions
+
+
+ true
+ ManageContentProperties
+
+
+ true
+ ManageContentTypes
+
+
+ true
+ ManageCustomPermissions
+
+
+ true
+ ManageCustomReportTypes
+
+
+ true
+ ManageDashbdsInPubFolders
+
+
+ true
+ ManageDataCategories
+
+
+ true
+ ManageDataIntegrations
+
+
+ true
+ ManageDynamicDashboards
+
+
+ true
+ ManageEmailClientConfig
+
+
+ true
+ ManageEntitlements
+
+
+ true
+ ManageExchangeConfig
+
+
+ true
+ ManageHealthCheck
+
+
+ true
+ ManageHubConnections
+
+
+ true
+ ManageInteraction
+
+
+ true
+ ManageInternalUsers
+
+
+ true
+ ManageIpAddresses
+
+
+ true
+ ManageKnowledge
+
+
+ true
+ ManageKnowledgeImportExport
+
+
+ true
+ ManageLeads
+
+
+ true
+ ManageLoginAccessPolicies
+
+
+ true
+ ManageMobile
+
+
+ true
+ ManageNetworks
+
+
+ true
+ ManagePackageLicenses
+
+
+ true
+ ManagePasswordPolicies
+
+
+ true
+ ManageProfilesPermissionsets
+
+
+ true
+ ManagePropositions
+
+
+ true
+ ManagePvtRptsAndDashbds
+
+
+ true
+ ManageRecommendationStrategies
+
+
+ true
+ ManageReleaseUpdates
+
+
+ true
+ ManageRemoteAccess
+
+
+ true
+ ManageReportsInPubFolders
+
+
+ true
+ ManageRoles
+
+
+ true
+ ManageSearchPromotionRules
+
+
+ true
+ ManageSharing
+
+
+ true
+ ManageSolutions
+
+
+ true
+ ManageSubscriptions
+
+
+ true
+ ManageSynonyms
+
+
+ true
+ ManageTranslation
+
+
+ true
+ ManageUnlistedGroups
+
+
+ true
+ ManageUsers
+
+
+ true
+ MassInlineEdit
+
+
+ true
+ MergeTopics
+
+
+ true
+ ModerateChatter
+
+
+ true
+ ModifyAllData
+
+
+ true
+ ModifyDataClassification
+
+
+ true
+ ModifyMetadata
+
+
+ true
+ NewReportBuilder
+
+
+ true
+ OmnichannelInventorySync
+
+
+ true
+ Packaging2
+
+
+ true
+ Packaging2Delete
+
+
+ true
+ PrivacyDataAccess
+
+
+ true
+ RemoveDirectMessageMembers
+
+
+ true
+ ResetPasswords
+
+
+ true
+ RunReports
+
+
+ true
+ ScheduleReports
+
+
+ true
+ SelectFilesFromSalesforce
+
+
+ true
+ SendCustomNotifications
+
+
+ true
+ SendExternalEmailAvailable
+
+
+ true
+ SendSitRequests
+
+
+ true
+ ShareInternalArticles
+
+
+ true
+ ShowCompanyNameAsUserBadge
+
+
+ true
+ SolutionImport
+
+
+ true
+ SubmitMacrosAllowed
+
+
+ true
+ SubscribeDashboardRolesGrps
+
+
+ true
+ SubscribeDashboardToOtherUsers
+
+
+ true
+ SubscribeReportRolesGrps
+
+
+ true
+ SubscribeReportToOtherUsers
+
+
+ true
+ SubscribeReportsRunAsUser
+
+
+ true
+ SubscribeToLightningDashboards
+
+
+ true
+ SubscribeToLightningReports
+
+
+ true
+ TransactionalEmailSend
+
+
+ true
+ TransferAnyCase
+
+
+ true
+ TransferAnyEntity
+
+
+ true
+ TransferAnyLead
+
+
+ true
+ UseOmnichannelInventoryAPIs
+
+
+ true
+ UseTeamReassignWizards
+
+
+ true
+ UseWebLink
+
+
+ true
+ ViewAllData
+
+
+ true
+ ViewAllProfiles
+
+
+ true
+ ViewAllUsers
+
+
+ true
+ ViewDataAssessment
+
+
+ true
+ ViewDataCategories
+
+
+ true
+ ViewDataLeakageEvents
+
+
+ true
+ ViewDeveloperName
+
+
+ true
+ ViewEventLogFiles
+
+
+ true
+ ViewFlowUsageAndFlowEventData
+
+
+ true
+ ViewHealthCheck
+
+
+ true
+ ViewHelpLink
+
+
+ true
+ ViewMyTeamsDashboards
+
+
+ true
+ ViewPlatformEvents
+
+
+ true
+ ViewPublicDashboards
+
+
+ true
+ ViewPublicReports
+
+
+ true
+ ViewRoles
+
+
+ true
+ ViewSetup
+
+
+ true
+ ViewUserPII
+
+
+ true
+ WorkCalibrationUser
+
+
diff --git a/test/nuts/customTranslationProject/force-app/main/default/translations/es.translation-meta.xml b/test/nuts/customTranslationProject/force-app/main/default/translations/es.translation-meta.xml
new file mode 100644
index 000000000..3f0ee803e
--- /dev/null
+++ b/test/nuts/customTranslationProject/force-app/main/default/translations/es.translation-meta.xml
@@ -0,0 +1,60 @@
+
+
+
+
+ LogACall
+
+
+
+ NewAccount
+
+
+
+ NewCase
+
+
+
+ NewContact
+
+
+
+ NewEvent
+
+
+
+ NewGroup
+
+
+
+ NewLead
+
+
+
+ NewNote
+
+
+
+ NewOpportunity
+
+
+
+ NewTask
+
+
+
+ SendEmail
+
+
+
+
+ screen_flows_prebuilt_crt
+
+
+ Flow Interview Log Entries
+
+
+
+ Flow Interview Logs
+
+
+
diff --git a/test/nuts/customTranslationProject/sfdx-project.json b/test/nuts/customTranslationProject/sfdx-project.json
new file mode 100644
index 000000000..21b701641
--- /dev/null
+++ b/test/nuts/customTranslationProject/sfdx-project.json
@@ -0,0 +1,12 @@
+{
+ "packageDirectories": [
+ {
+ "path": "force-app",
+ "default": true
+ }
+ ],
+ "name": "default",
+ "namespace": "",
+ "sfdcLoginUrl": "https://login.salesforce.com",
+ "sourceApiVersion": "51.0"
+}
diff --git a/test/nuts/translation.nut.ts b/test/nuts/translation.nut.ts
new file mode 100644
index 000000000..db9df9818
--- /dev/null
+++ b/test/nuts/translation.nut.ts
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2020, salesforce.com, inc.
+ * All rights reserved.
+ * Licensed under the BSD 3-Clause license.
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
+ */
+import * as fs from 'fs';
+import * as path from 'path';
+import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit';
+import { expect } from 'chai';
+import { DeployCommandResult } from '../../src/formatters/deployResultFormatter';
+import { RetrieveCommandResult } from '../../src/formatters/retrieveResultFormatter';
+
+describe('translations', () => {
+ let session: TestSession;
+ let projectPath: string;
+ let translationPath: string;
+
+ before(async () => {
+ session = await TestSession.create({
+ project: {
+ sourceDir: path.join(process.cwd(), 'test', 'nuts', 'customTranslationProject'),
+ },
+ setupCommands: [
+ 'sfdx force:org:create -f config/project-scratch-def.json --setdefaultusername --wait 10 --durationdays 1',
+ ],
+ });
+ projectPath = path.join(session.project.dir, 'force-app', 'main', 'default');
+ translationPath = path.join(projectPath, 'objectTranslations', 'customObject__c-es');
+ });
+
+ after(async () => {
+ await session?.clean();
+ });
+
+ describe('manifest', () => {
+ after(async () => {
+ await fs.promises.unlink(path.join(session.project.dir, 'package.xml'));
+ });
+
+ it('can generate manifest for translation types', async () => {
+ execCmd('force:source:manifest:create -p force-app --json', { ensureExitCode: 0 });
+ expect(fs.existsSync(path.join(session.project.dir, 'package.xml'))).to.be.true;
+ });
+
+ it('deploy', () => {
+ const deployResults = execCmd('force:source:deploy -x package.xml --json', {
+ ensureExitCode: 0,
+ }).jsonOutput.result;
+ expect(deployResults.deployedSource.length).to.equal(6);
+ });
+
+ it('retrieve without local metadata', async () => {
+ // delete and recreate an empty dir
+ await fs.promises.rmdir(path.join(session.project.dir, 'force-app'), { recursive: true });
+ await fs.promises.mkdir(path.join(session.project.dir, 'force-app'));
+ const retrieveResults = execCmd('force:source:retrieve -x package.xml --json', {
+ ensureExitCode: 0,
+ }).jsonOutput.result;
+ expect(retrieveResults.inboundFiles).to.have.length(7);
+ });
+ });
+
+ describe('metadata', () => {
+ describe('deploy', () => {
+ it('can deploy all metadata items', async () => {
+ execCmd('force:source:deploy -m CustomFieldTranslation,CustomObjectTranslation --json', {
+ ensureExitCode: 0,
+ });
+ });
+ });
+
+ describe('retrieve', () => {
+ it('can retrieve all metadata items', async () => {
+ execCmd('force:source:retrieve -m CustomFieldTranslation,CustomObjectTranslation --json', {
+ ensureExitCode: 0,
+ });
+ });
+ });
+ });
+
+ describe('sourcepath', () => {
+ describe('deploy', () => {
+ it('can deploy the whole project', async () => {
+ execCmd('force:source:deploy -p force-app --json', {
+ ensureExitCode: 0,
+ });
+ });
+
+ describe('individual type deploys', () => {
+ it('can deploy COT', async () => {
+ execCmd(`force:source:deploy -p ${translationPath} --json`, {
+ ensureExitCode: 0,
+ });
+ });
+
+ it('can "deploy" CFTs', async () => {
+ // should not actually deploy anything
+ execCmd(
+ `force:source:deploy -p ${path.join(translationPath, 'customField__c.fieldTranslation-meta.xml')} --json`,
+ {
+ ensureExitCode: 0,
+ }
+ );
+ });
+
+ it('can deploy COT', async () => {
+ execCmd(
+ `force:source:deploy -p ${path.join(translationPath, 'customField__c.fieldTranslation-meta.xml')} --json`,
+ {
+ ensureExitCode: 0,
+ }
+ );
+ });
+ });
+ });
+
+ describe('retrieve', () => {
+ it('can retrieve the whole project', async () => {
+ execCmd('force:source:retrieve -p force-app --json', {
+ ensureExitCode: 0,
+ });
+ });
+
+ describe('individual type retrieves', () => {
+ it('can retrieve COT', async () => {
+ execCmd(`force:source:retrieve -p ${translationPath} --json`, {
+ ensureExitCode: 0,
+ });
+ });
+
+ it('can retrieve COT from directory', async () => {
+ execCmd(
+ `force:source:retrieve -p ${path.join(
+ translationPath,
+ 'customObject__c-es.objectTranslation-meta.xml'
+ )} --json`,
+ {
+ ensureExitCode: 0,
+ }
+ );
+ });
+ });
+ });
+ });
+
+ describe('mdapi format', () => {
+ it('can convert COT/CFTs correctly', () => {
+ execCmd('force:source:convert --outputdir mdapi', { ensureExitCode: 0 });
+ // the CFTs shouldn't be written to mdapi format
+ expect(fs.existsSync(path.join(session.project.dir, 'mdapi', 'fields'))).to.be.false;
+ expect(fs.existsSync(path.join(session.project.dir, 'mdapi', 'objectTranslations'))).to.be.true;
+ });
+ });
+});