はじめに
皆さんはSalesforceの翻訳データを見るときに何を使っていますか? 一般的にはSalesforceの設定画面から言語を選び、コンポーネントを選んで言語ごとに選ぶか、エクスポートしてstfファイルを確認するケースが多いかと思います。
国際化対応をしていくと、各言語の翻訳データを一覧で見ることが多々あるので、APEXでどうにかできないか色々調べてみました。
翻訳データ抽出方法
翻訳データはメタデータAPIを使うことで抽出できそうです。
※メタデータ API 開発者ガイド を参照
なので今回はメタデータAPIをつかって一覧表示をしてみたいと思います。
全体の流れ
1.事前準備
2.メタデータからマスター値の取得
3.メタデータから翻訳データの取得
という形で処理を進めます。
1.事前準備
まずは、メタデータ取得用、画面表示用などに使うインナークラス系を定義します。
各クラスの定義
・LangTypePickParam マスターラベルと翻訳値の選択リスト値を格納するクラス
・MetadataPort メタデータを読み込むためのクラス
・ReadResult readMetadata コールの結果情報インターフェイス
・ReadResponseElement メタデータのResponseインターフェイス
・SessionHeader_element セッションヘッダクラス
・ReadMetadata_element メタデータコンポーネント読み取りクラス
・ReadTranslationsResponse_element ReadTranslationsResponse_element
・ReadTranslationsResult 結果情報を取得クラス
・Metadata すべてのメタデータ型の基本クラス
・Translations 使用言語翻訳クラス
・GlobalPicklist グローバル選択リストメタデータクラス
・GlobalPicklistValue グローバル選択リスト値クラス
・GlobalPicklistTranslation グローバル選択リストの翻訳
今回未使用だがメタデータ取得時に定義しておかなければいけないクラス
・CustomApplicationTranslation カスタムアプリケーション翻訳クラス
・CustomLabelTranslation カスタム表示ラベル翻訳クラス
・CustomTabTranslation カスタムタブ翻訳クラス
・PicklistValueTranslation 選択リスト値翻訳クラス
・GlobalQuickActionTranslation グローバルなクイックアクション翻訳クラス
インナークラスサンプルコード
public class LangTypePickParam { public String master {get; private set;} public String ja {get; private set;} public String en_US {get; private set;} public String zh_CN {get; private set;} } public class MetadataPort { public String endpoint_x = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/m/37.0'; public Map<String,String> inputHttpHeaders_x; public Map<String,String> outputHttpHeaders_x; public String clientCertName_x; public String clientCert_x; public String clientCertPasswd_x; public Integer timeout_x; public SessionHeader_element SessionHeader; private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/2006/04/metadata'; private String AllOrNoneHeader_hns = 'AllOrNoneHeader=http://soap.sforce.com/2006/04/metadata'; private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata', 'Translation'}; public ReadResult ReadMetadata(String type_x,String[] fullNames) { ReadMetadata_element request_x = new ReadMetadata_element(); request_x.type_x = type_x; request_x.fullNames = fullNames; ReadResponseElement response_x; Map<String, ReadResponseElement> response_map_x = new Map<String, ReadResponseElement>(); response_map_x.put('response_x', response_x); WebServiceCallout.invoke( this, request_x, response_map_x, new String[]{endpoint_x, '', 'http://soap.sforce.com/2006/04/metadata', 'readMetadata', 'http://soap.sforce.com/2006/04/metadata', 'readMetadataResponse', 'Translation2Controller.read' + type_x + 'Response_element'} ); response_x = response_map_x.get('response_x'); return response_x.getResult(); } } public interface ReadResult { Metadata[] getRecords(); } public interface ReadResponseElement { ReadResult getResult(); } public class SessionHeader_element { public String sessionId; private String[] sessionId_type_info = new String[]{'sessionId','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'sessionId'}; } public class ReadMetadata_element { public String type_x; public String[] fullNames; private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] fullNames_type_info = new String[]{'fullNames','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'type_x','fullNames'}; } public class ReadTranslationsResponse_element implements ReadResponseElement { public ReadTranslationsResult result; public ReadResult getResult() { return result; } private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'result'}; } public class ReadTranslationsResult implements ReadResult { public Translations[] records; public Metadata[] getRecords() { return records; } private String[] records_type_info = new String[]{'records','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'records'}; } // 以下すべてのクラスは、ドキュメント「メタデータAPI開発者ガイド」に記載 /** * すべてのメタデータ型の基本クラス */ public virtual class Metadata { public String fullName; } /** * Metadata メタデータ型を拡張し、そのfullName 項目を継承します。 */ public class Translations extends Metadata { public String type = 'Translations'; public String fullName; private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'}; // カスタムアプリケーション翻訳のリスト public CustomApplicationTranslation[] customApplications; // カスタム表示ラベル翻訳のリスト public CustomLabelTranslation[] customLabels; // カスタムタブ翻訳のリスト public CustomTabTranslation[] customTabs; // グローバル選択リストのリスト public GlobalPicklistTranslation[] globalPicklists; // グローバル (オブジェクト固有ではない) クイックアクションのリスト public GlobalQuickActionTranslation[] quickActions; private String[] customApplications_type_info = new String[]{'customApplications','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] customLabels_type_info = new String[]{'customLabels','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] customTabs_type_info = new String[]{'customTabs','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] globalPicklists_type_info = new String[]{'globalPicklists','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] quickActions_type_info = new String[]{'quickActions','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] type_att_info = new String[]{'xsi:type'}; private String[] field_order_type_info = new String[]{'fullName', 'customApplications','customDataTypeTranslations','customLabels','customPageWebLinks','customTabs','globalPicklists','quickActions','reportTypes','scontrols'}; } /** * グローバル選択リストのメタデータ */ public class GlobalPicklist extends Metadata { public String type = 'GlobalPicklist'; public String fullName; private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'}; public String description; public GlobalPicklistValue[] globalPicklistValues; public String masterLabel; public Boolean sorted; private String[] description_type_info = new String[]{'description','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'}; private String[] globalPicklistValues_type_info = new String[]{'globalPicklistValues','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] masterLabel_type_info = new String[]{'masterLabel','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] sorted_type_info = new String[]{'sorted','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] type_att_info = new String[]{'xsi:type'}; private String[] field_order_type_info = new String[]{'fullName', 'description','globalPicklistValues','masterLabel','sorted'}; } public virtual class GlobalPicklistValue extends Metadata { public String color; public Boolean default_x; public String description; public Boolean isActive; } public class CustomApplicationTranslation { public String label; public String name; private String[] label_type_info = new String[]{'label','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] name_type_info = new String[]{'name','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'label','name'}; } public class CustomLabelTranslation { public String label; public String name; private String[] label_type_info = new String[]{'label','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] name_type_info = new String[]{'name','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'label','name'}; } public class CustomTabTranslation { public String label; public String name; private String[] label_type_info = new String[]{'label','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] name_type_info = new String[]{'name','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'label','name'}; } public class GlobalPicklistTranslation { public String name; public PicklistValueTranslation[] picklistValues; private String[] name_type_info = new String[]{'name','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] picklistValues_type_info = new String[]{'picklistValues','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'name','picklistValues'}; } public class PicklistValueTranslation { public String masterLabel; public String translation; private String[] masterLabel_type_info = new String[]{'masterLabel','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] translation_type_info = new String[]{'translation','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'masterLabel','translation'}; } public class GlobalQuickActionTranslation { public String label; public String name; private String[] label_type_info = new String[]{'label','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] name_type_info = new String[]{'name','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; private String[] field_order_type_info = new String[]{'label','name'}; }
2.メタデータからマスター値の取得
まずはメタデータから選択リストのマスター値の取得をします。
Translations master = (Translations) service.readMetadata('Translations',langType).getRecords()[0]; LangTypePickParam ltpp; List<LangTypePickParam> LangTypePickParamList; // 選択リストの項目名の種類だけループ for(GlobalPicklistTranslation GlobalPicklistTranslation : master.globalPicklists) { LangTypePickParamList = new List<LangTypePickParam>(); // 選択リスト値の値の数分だけループ for(PicklistValueTranslation pickListValue : GlobalPicklistTranslation.picklistValues) { ltpp = new LangTypePickParam(); ltpp.Master = pickListValue.masterLabel; LangTypePickParamList.add(ltpp); } if(!pickListMap.containsKey(GlobalPicklistTranslation.name)) { this.pickListMap.put(GlobalPicklistTranslation.name, LangTypePickParamList); this.pickListDecisionMap.put(GlobalPicklistTranslation.name, false); } }
先頭の
Translations master =
(Translations) service.readMetadata('Translations',langType).getRecords()[0];
の処理のなかで、WebService経由でメタデータを取得しています。
※このデータは翻訳データが大量に入っているのでグローバル選択リストのデータのみを更新対象としています。
3.メタデータから翻訳データの取得
各言語毎にメタデータを読み取り、表示用PickListMapに追記する形で格納していきます。
この際、翻訳データが存在しないグローバル選択リストを表示させないために、翻訳値が存在しないデータはpickListDecisionMapにも格納します。
for(Integer i = 0 ; i < langType.size() ; i++) { // メタデータから翻訳に関係する情報を取得する Translations translation; translation = (Translations) service.readMetadata('Translations',langType).getRecords()[i]; // 選択リストの項目名の種類だけループ for(GlobalPicklistTranslation GlobalPicklistTranslation : translation.globalPicklists) { if(pickListMap.containsKey(GlobalPicklistTranslation.name)) { transDecisionFlag = false; for(Integer j = 0 ; j < GlobalPicklistTranslation.picklistValues.size() ; j++) { ltpp = new LangTypePickParam(); ltpp = pickListMap.get(GlobalPicklistTranslation.name).get(j); if(langType[i] == 'ja') { ltpp.ja = GlobalPicklistTranslation.picklistValues[j].translation; if(!String.isBlank(ltpp.ja)) { transDecisionFlag = true; } } else if(langType[i] == 'en_US') { ltpp.en_US = GlobalPicklistTranslation.picklistValues[j].translation; if(!String.isBlank(ltpp.en_US)) { transDecisionFlag = true; } } else if(langType[i] == 'zh_CN') { ltpp.zh_CN = GlobalPicklistTranslation.picklistValues[j].translation; if(!String.isBlank(ltpp.zh_CN)) { transDecisionFlag = true; } } this.pickListMap.get(GlobalPicklistTranslation.name).set(j,ltpp); if(transDecisionFlag) { this.pickListDecisionMap.put(GlobalPicklistTranslation.name, transDecisionFlag); } } } } }
こうして、PickListMapに表示用の選択リストの翻訳データが格納されました。
これをページ側で表示すると以下のように表示されます。
画面
<apex:page controller="Translation2Controller" cache="false" id="thePage"> <apex:outputPanel id="errPnl"> <apex:pageMessages escape="false" /> </apex:outputPanel> <apex:form > <apex:actionStatus onstart="blockUI()" onstop="unblockUI()" id="loading"/> <apex:pageBlock title="グローバル選択リスト"> <table class="tbl"> <caption><b>グローバル選択リスト翻訳テーブル</b></caption> <tr> <th>グローバル選択リスト名</th> <th>MasterLabel</th> <th>日本語</th> <th>英語</th> <th>中国語</th> </tr> <apex:repeat value="{!pickListMap}" var="key"> <apex:outputPanel rendered="{!pickListDecisionMap[key]}"> <tr> <td bgcolor="#FFFFFF"> {!key} </td> <td bgcolor="#FFFFFF"> <apex:repeat value="{!pickListMap[key]}" var="val"> <apex:outputText value="{!val.master}<br/>" escape="false"/> </apex:repeat> </td> <td bgcolor="#FFFFFF"> <apex:repeat value="{!pickListMap[key]}" var="val"> <apex:outputText value="{!val.ja}<br/>" escape="false"/> </apex:repeat> </td> <td bgcolor="#FFFFFF"> <apex:repeat value="{!pickListMap[key]}" var="val"> <apex:outputText value="{!val.en_US}<br/>" escape="false"/> </apex:repeat> </td> <td bgcolor="#FFFFFF"> <apex:repeat value="{!pickListMap[key]}" var="val"> <apex:outputText value="{!val.zh_CN}<br/>" escape="false"/> </apex:repeat> </td> </tr> </apex:outputPanel> </apex:repeat> </table> </apex:pageBlock> </apex:form> </apex:page>
まとめ
メタデータAPIを使って、無事APEX内で翻訳データを抜き取って表示することができました。
これを応用すれば、このデータをカスタムオブジェクトにマスター化することで、データ連携時に複数の翻訳されているデータを他システムに連携したり、Apexで特定の言語で翻訳したデータをレコードに書き込むなどに使えるのではないかなあと思います。