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 系统的维护效率。

相关推荐
UrbanJazzerati7 天前
Salesforce LWC RefreshView API 详解与示例
saas
皮皮林55110 天前
设计一个多租户 SaaS 系统,如何实现租户数据隔离与资源配额控制?
java·saas
zhoupenghui16812 天前
关于SaaS业务模式及其系统架构构建的详细解析
系统架构·saas
SaaS_Product18 天前
如何保护文件传输安全?文件传输加密
云计算·saas
蝸牛ちゃん20 天前
云计算三大服务模式深度解析:IaaS、PaaS、SaaS
云原生·系统架构·云计算·软考高级·saas·paas·iaas
UrbanJazzerati1 个月前
如何使用 Salesforce Apex 批量重置和修改用户密码
saas
潘锦1 个月前
做了 10 年SaaS 产品后,我总结的权限设计避坑指南
saas
Kookoos2 个月前
功能管理:基于 ABP 的 Feature Management 实现动态开关
c#·.net·saas·多租户·abp vnext
零起飞2 个月前
SAAS架构设计2-流程图-注册流程图
流程图·saas