Salesforce 中批量删除自定义标签翻译的高效方法 Part1

Salesforce 中批量删除自定义标签翻译的高效方法

在 Salesforce 开发或管理过程中,我们常常需要处理大量自定义标签(Custom Labels)的翻译数据。然而,Salesforce 的界面并不支持直接批量删除这些翻译记录,手动操作不仅耗时,还容易出错。本文将介绍一种基于 Tooling API 的解决方案,帮助开发者高效完成这一任务。

问题背景

Salesforce 的自定义标签翻译(Custom Label Translations)存储在 ExternalStringLocalization 对象中,每个翻译记录关联一个标签名称(ExternalString.Name)和语言(Language)。如果需要删除数百甚至上千条翻译记录,逐条操作显然不现实。而 Salesforce 的标准 API(如 REST API 或 Bulk API)对这一场景的支持有限,因此我们需要借助 Tooling API 来实现批量删除。

解决方案:利用 Tooling API 分两步操作

第一步:查询目标翻译记录的 ID

通过 Tooling API 的查询接口(/tooling/query),我们可以编写 SOQL 查询语句,筛选出需要删除的翻译记录。例如,以下请求会获取简体中文(zh_CN)语言下名为 Test_Lawrence的标签翻译:

sql 复制代码
GET Select+Id,+ExternalString.Name,+Language,+Value+from+ExternalStringLocalization+WHERE+Language +='zh_CN'+AND+ExternalString.Name+='Test_Lawrence'+LIMIT+99
  • 关键点

    • 对象名称为 ExternalStringLocalization
    • 支持按标签名称、语言、翻译值等条件筛选。
    • 若结果超过一定记录条数,需通过 nextRecordsUrl 分页查询。

第二步:逐个删除翻译记录

获取到 ID 后,再次调用 Tooling API 的删除接口(/tooling/sobjects/ExternalStringLocalization/{ID}),逐条删除记录。例如:

bash 复制代码
DELETE /services/data/v55.0/tooling/sobjects/ExternalStringLocalization/01j2M00001dFLMsQAO

Apex 代码实现

以下是一个完整的 Apex 类,封装了上述逻辑:

typescript 复制代码
public with sharing class CustomLabelTranslationDelete {
    private static final String INSTANCE_URL = Url.getOrgDomainURL().toExternalForm();
    private static final String TOOLING_ENDPOINT = '/services/data/v52.0/tooling/query/?q=';
    private static final String TOOLING_DELETE_ENDPOINT = '/services/data/v55.0/tooling/sobjects/ExternalStringLocalization/';

    //static List needed to accommodate multiple GET callouts
    private static List<Records> customLabelTranslationRecords = new List<Records>();

   public static void removeCustomLabelTranslations(Set<String> labelApiNames, String language){
        if(labelApiNames == null || labelApiNames.isEmpty() || String.isBlank(language)){
           return;
        }

        //First we find the Custom Label Translations
        String externalStringQuery = INSTANCE_URL + TOOLING_ENDPOINT + 'Select+Id+from+ExternalStringLocalization+WHERE+Language+=\'' + language + '\'+AND+ExternalString.Name+IN+' +formatLabelNamesForQuery(labelApiNames) +'+LIMIT+99';

        makeCustomLabelCallout(externalStringQuery, null);

        //If we didn't find any Translations matching the criteria
       if(customLabelTranslationRecords.isEmpty()){
           return;
       }

       //Now that we have the record Ids we can generate all the delete endpoint we need to hit
       List<String> labelTranslationEndpoints = new List<String>();
       for(Records r : customLabelTranslationRecords){
           if(String.isNotBlank(r.Id)) {
               labelTranslationEndpoints.add(INSTANCE_URL + TOOLING_DELETE_ENDPOINT + r.Id);
           }
       }


       if(!labelTranslationEndpoints.isEmpty()) {
           //Now we fire off all of the REST API calls to delete the custom labels
           customLabelTranslationDeleteCallout(labelTranslationEndpoints);
       }
   }    

   public static void makeCustomLabelCallout(String query, String nextQueryUrl) {
       HttpRequest req = new HttpRequest();
       // get Access Token
       req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
       req.setHeader('Content-Type', 'application/json');
       req.setHeader('Accept', '*/*');

       if(String.isBlank(nextQueryUrl)) {
           req.setEndpoint(query);
       }
       else{
           req.setEndpoint(INSTANCE_URL + nextQueryUrl);
       }

       req.setMethod('GET');

       Http h = new Http();
       HttpResponse res = h.send(req);

       System.debug(res.getBody());

       Response response = (Response) JSON.deserialize(res.getBody(), Response.class);
       customLabelTranslationRecords.addAll(response.records);
       if(!response.done){
           makeCustomLabelCallout(null, response.nextRecordsUrl);
       }
   }

   public static void customLabelTranslationDeleteCallout(List<String> requestUrls) {
       HttpRequest req = new HttpRequest();
       req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
       req.setHeader('Content-Type', 'application/json');
       req.setEndpoint(requestUrls[0]);
       req.setMethod('DELETE');

       Http h = new Http();
       HttpResponse res = h.send(req);

       requestUrls.remove(0);
       if(!requestUrls.isEmpty()){
           customLabelTranslationDeleteCallout(requestUrls);
       }
   }

   private static String formatLabelNamesForQuery(Set<String> customLabelNames) {
       String value = '(';

       for (String s : customLabelNames) {
           value += '\'' + s + '\'' + ',';
       }

       if (String.isNotBlank(value)) {
           value = value.removeEnd(',');
           value += ')';
       }
       return value;
   }

   public class Response {
       public Integer size;
       public Integer totalSize;
       public Boolean done;
       public String queryLocator;
       public String nextRecordsUrl;
       public String entityTypeName;
       public List<Records> records;
   }

   public class Attributes {
       public String type;
       public String url;
   }

   public class ExternalString {
       public Attributes attributes;
       public String Name;
   }

   public class Records {
       public Attributes attributes;
       public String Id;
       public String Name;
       public String Language;
       public String Value;
       public ExternalString ExternalString;
   }
}

使用方法

在匿名 Apex 中调用以下代码即可:

javascript 复制代码
CustomLabelTranslationDelete.removeCustomLabelTranslations(new Set<String>{'Test_Lawrence'}, 'zh_CN');

注意事项

  1. 性能限制
    每个 Apex 事务最多允许 100 次 HTTP 请求,因此单次调用最多删除 99 条记录。若需处理更多数据,可通过 QueueableBatch Apex 链式调用。
  2. 语言与标签名称匹配
    确保查询条件(如语言代码 zh_CN 和标签名称)与目标数据完全匹配,避免误删。
  3. 测试环境验证
    在生产环境执行前,务必在沙箱环境中测试代码逻辑。

总结

通过 Tooling API 的分页查询与递归删除功能,我们可以高效完成 Salesforce 中自定义标签翻译的批量清理工作。这一方法不仅适用于删除操作,还可扩展至其他复杂的数据管理场景。掌握此类技巧,能显著提升 Salesforce 系统的维护效率。

相关推荐
UrbanJazzerati9 天前
如何使用 Salesforce Apex 批量重置和修改用户密码
saas
潘锦22 天前
做了 10 年SaaS 产品后,我总结的权限设计避坑指南
saas
Kookoos1 个月前
功能管理:基于 ABP 的 Feature Management 实现动态开关
c#·.net·saas·多租户·abp vnext
零起飞1 个月前
SAAS架构设计2-流程图-注册流程图
流程图·saas
零起飞1 个月前
SAAS架构设计2-流程图-用户与租户之间对应关系图
流程图·saas·erp
MyikJ1 个月前
互联网大厂Java面试:从Spring到微服务的挑战
数据库·spring boot·微服务·saas·java面试·缓存技术
淘源码A2 个月前
小微企业SaaS ERP管理系统,SpringBoot+Vue+ElementUI+UniAPP
java·源码·saas·erp·erp系统·erp源码
淘源码d2 个月前
智慧工地:整合物联网、大数据、人工智能、BIM等技术,实现施工现场的全要素智能化管理
物联网·saas·智慧工地·数字化转型·云平台·工地管理·房建工程
zhongqu_3dnest2 个月前
什么是全景相机?
数码相机·saas·数字孪生·三维空间·全景相机·三维全景技术