Month: July 2017

SOSL Queries in Salesforce

Salesforce Object Search Language (SOSL) is a Salesforce search language that is used to perform text searches in records. Use SOSL to search fields across multiple standard and custom object records in Salesforce.

This is an example of a SOSL query that searches for accounts and contacts that have any fields with the word ‘SFDC’.

List searchList = [FIND ‘SFDC’ IN ALL FIELDS                                                                                                         RETURNING Account(Name), Contact(FirstName,LastName)];

SOSL Syntax –

FIND ‘SearchQuery‘ [IN SearchGroup] [RETURNING ObjectsAndFields]

SearchQuery is the text to search for (a single word or a phrase). Search terms can be grouped with logical operators (AND, OR) and parentheses. Also, search terms can include wildcard characters (*, ?). The * wildcard matches zero or more characters at the middle or end of the search term. The ? wildcard matches only one character at the middle or end of the search term.

Text searches are case-insensitive. For example, searching for Customer, customer, or CUSTOMER all return the same results.

SearchGroup is optional. It is the scope of the fields to search. If not specified, the default search scope is all fields. SearchGroup can take one of the following values.


ObjectsAndFields is optional. It is the information to return in the search result—a list of one or more sObjects and, within each sObject, list of one or more fields. If not specified, the search results contain the IDs of all objects found.

Using SOSL in Apex

List<List<sObject>> searchList = [FIND 'Wingo OR SFDC' IN ALL FIELDS 
                        RETURNING Account(Name),Contact(FirstName,LastName,Department)];
Account[] searchAccounts = (Account[])searchList[0];
Contact[] searchContacts = (Contact[])searchList[1];

System.debug('Found the following accounts.');
for (Account a : searchAccounts) {

System.debug('Found the following contacts.');
for (Contact c : searchContacts) {
 System.debug(c.LastName + ', ' + c.FirstName);

This SOSL query combines two search terms by using the OR logical operator—it searches for Wingo or SFDC in any field. This example returns all the sample accounts because they each have a field containing one of the words. The SOSL search results are returned in a list of lists. Each list contains an array of the returned records. In this case, the list has two elements. At index 0, the list contains the array of accounts. At index 1, the list contains the array of contacts.

Using Filter, Reorder and Limit in SOSL 

You can use WHERE clause, ORDER BY, and LIMIT the returned results of a SOSL query. Because SOSL queries can return multiple sObjects, those filters are applied within each sObject inside the RETURNING clause.

For example, this results in only accounts whose industry is Apparel to be returned:     RETURNING Account(Name, Industry WHERE Industry=’Apparel’)

For example this causes the returned accounts to be ordered by the Name field:             RETURNING Account(Name, Industry ORDER BY Name).

This example limits the returned accounts to 10 only:                                                           RETURNING Account(Name, Industry LIMIT 10)

Validation Rule to Prevent Deletion of Child in Master-Details

You have 2 custom objects, Job Applications and Reviews that have a master-detail relationship. Users should NOT be allowed to delete review records after job application records have been approved. How would a developer meet this requirement?

Ans – Use a validation rule in conjunction with a roll-up summary field

Validation rule will fire only on Insert or Update operation but not on Delete. But in Master-Details when you have a roll-up summary field, the delete in Child will be update the Master and will fire the Validation rule in Master object.

Validation Rule –

AND( PRIORVALUE( Number_of_Reviews__c ) > Number_of_Reviews__c , TEXT(Picklist__c ) == ‘Approved’ )

If you delete the Child record(i.e. the review record) it will delete the record and will initiate the rollup summary field calculation in the Master object record(i.e Job Application) which is an update in Master, so it will fire the Validation  rule of Master object and will prevent the child record to be deleted from the system. This all operation happen in a single execution.

You can follow the Execution order as below –

1. Old record loaded from database (or initialized for new inserts)
2. New record values overwrite old values
3. System Validation Rules (If inserting Opportunity Products, Custom Validation                          Rules will also fire in addition to System Validation Rules)
4. All Apex before triggers (EE / UE only)
5. Custom Validation Rules
6. Record saved to database (but not committed)
7. Record reloaded from database
8. All Apex after triggers (EE / UE only)
9. Assignment rules
10. Auto-response rules
11. Workflow rules
12. Processes
13. Escalation rules
14. Parent Rollup Summary Formula value updated (if present)
15. Database commit
16. Post-commit logic (sending email)

Previous load of class failed: in Salesforce

line -1, column -1: Previous load of class failed: – In most scenarios, this error message is related to the “IsValid”  checkbox for the code which is being referred to in the message.

“IsValid” Checkbox is unchecked for an Apex Class or a Trigger when the metadata on which the code is dependent, has been changed and the code is not compiled after that. The code can be compiled by running the Run All Test, or clicking “Compile All Classes” link, or by Editing and saving that class, or by executing the code in the class. When the code is compiled, either an error message is displayed which can be rectified, or the code is saved successfully and the isValid checkbox is checked.

Invalid Class/Trigger will cause this issue in the org. You can query and check if any invalid classes or triggers in you org from ApexClass and ApexTrigger like below.

Select Id, IsValid, Name From ApexClass where isValid=false

Select Id, IsValid, Name From ApexTrigger where isValid=false

Apex classes are stored with an isValid flag that is set to true as long as dependent metadata has not changed since the class was last compiled. If any changes are made to object names or fields that are used in the class, including superficial changes such as edits to an object or field description, or if changes are made to a class that calls this class, the isValid flag is set to false. When a trigger or Web service call invokes the class, the code is recompiled and the user is notified if there are any errors. If there are no errors, the isValid flag is reset.

Triggers are stored with an isValid flag that is set to true as long as dependent metadata has not changed since the trigger was last compiled. If any changes are made to object names or fields that are used in the trigger, including superficial changes such as edits to an object or field description, the isValid flag is set to false until the Apex compiler reprocesses the code. Recompiling occurs when the trigger is next executed, or when a user re-saves the trigger in metadata.

Resolution –  To resolve this error recompile all classes and do the changes if any class failed to compiled as per the error message.

Steps To Compile All Classes .

Setup > Apex Classes  – Click on the “Complie all Classes” link as below.


Quote Salesforce

Quotes in Salesforce represent the proposed prices of your company’s products and services. You create a quote from an opportunity and its products. Each opportunity can have multiple associated quotes, and any one of them can be synced with the opportunity. When a quote and an opportunity are synced, any change to line items in the quote syncs with products on the opportunity, and vice versa.

Create and Manage Quote –

Create a set of quotes to show different combinations of products, discounts, and quantities so customers can compare prices. Then add products as needed to keep your quotes up to date.

  1. You can create Quote from quote related list on an opportunity.
  2. On Quote creation –
    1. A unique quote number is added.
    2. Products on the opportunity are copied to the quote as line items.
    3. The grand total is recalculated based on the taxes and shipping information that you entered.
  3. Considerations for Creating and Managing Quotes and Quote Line Items.

    1. Relevant price books, products, and list prices must be active in an opportunity before you can create a quote for the opportunity.
    2. If a quote is synced with an opportunity, and you add a line item to the quote, the new line item is copied to the opportunity as a product in the Products related list.
    3. If an opportunity is syncing with a quote, deleting the quote line item deletes the corresponding product from the opportunity’s Products related list.
    4. Custom field syncing isn’t supported. When a quote is created from the API, the quote line items aren’t created based on the opportunity line items. New opportunity line items are created based on the quote line items.
    5. If a product has default schedules, the schedules aren’t created when you add a line item to a quote. If the line item is copied from the opportunity, you can view the schedule from the opportunity. To change the schedule, edit the product record directly.
    6. If you have a quote line item with a discount, and the product has a default schedule on the product record or an individual schedule on the opportunity product record, you can’t edit the Discount field of the line item.
Sync Quotes and Opportunities

To Sync a quote with Opportunity, open quote details page and click Start Sync.


To stop syncing between a quote and an opportunity, open the synced quote, and then click Stop Sync.

Use of Quote Sync –

Quote syncing lets you link a quote to the opportunity that it was created from and synchronize all updates between the two records. An opportunity can have multiple quotes, but it can sync with only one quote at a time.

  • While a quote and an opportunity are synced, any addition or change to the list of products in one record syncs with the list of products in the other one.
  • Adding or removing a line item from a quote updates the synced opportunity’s Products related list.
  • Adding or removing a product from the opportunity updates the synced quote’s Quote Line Items related list.
  • Product sorting also syncs between the two records.
  • The quote and opportunity continue to sync each way until you stop syncing or delete one of the records.
  • You can sync quotes and opportunities that don’t have any products. When you add a product to either record, it’s automatically added to the record that it’s synced to.
Troubleshooting Quote Syncing
  • This quote can’t be synced because it has inactive or archived products.
    • If the product isn’t active, edit the product and select the Active checkbox.
    • If the product has been archived and the opportunity isn’t closed, delete the product.
  • This quote can’t be synced because it has an inactive or archived price book.
    • If the price book isn’t active, edit the price book and select the Active checkbox.
    • If the price book has been archived and the opportunity isn’t closed, delete the price book.
  • This quote can’t be synced because it has inactive or archived list prices.
    • If the list price isn’t active, edit the list price and select the Active checkbox for the list price.
    • If the list price has been archived and the opportunity isn’t closed, delete the list price.
  • This quote can’t be synced because it has an inactive currency.

Activate the currency from Setup by entering Manage Currencies in the Quick Find box, then selecting Manage Currencies.

  • This quote can’t be synced because one or more of the schedules for the opportunity products have changed since the quote was created.

Create a different quote and sync it, or open the product, delete the schedule, and then re-create the schedule.

  • This quote can’t be synced because another quote that’s being synced for this opportunity is locked due to a workflow approval process. Opportunities can only sync with one quote at a time.
    To unlock the other quote, finish the approval process or recall the request for approval.
  • The SyncedQuote field is read only within a trigger.

Determine whether your Salesforce org is using an Apex trigger that’s attempting to modify the SyncedQuote field. The SyncedQuote field is read only and can’t be modified with a trigger. An admin can modify the trigger.

Email Quote PDF

Provide quotes to your customers the easy way: via PDF. Create quote PDFs from a standard template or from your company’s design. Email quotes to your customers. Then delete quotes that you no longer need.

Create a Quote PDF from the Standard Template-
  1. On the quote detail page, generate a preview by clicking Create PDF.
  2. Save the PDF to the Quote PDFs related list by clicking Save to Quote. The PDF name is the quote name plus a version number, such as AcmeQuote_V1
Create a Quote PDF by Selecting a Template

Before using the Template to create PDF, create a Template in Quote, to make it available.

Then Create PDF will show as dropdown list, which allow to select the custom created template.

Email a Quote PDF in Salesforce Classic.

You can email the Quote PDF from Email Quote button of Quote details page, this will attached the latest version of PDF created.

You can send the individual Quote PDF from Quote PDFs related list of Quote.

Considerations for Deleting Quotes

Before you delete a quote, it’s a good idea to understand how deleting a quote affects quote-related PDFs and syncing with opportunities.

  • An admin, the quote owner, or someone above the quote owner in the organization role hierarchy, you can delete quotes.
  • When you delete a quote, All related PDFs, notes, and attachments are deleted. If the quote is synced with an opportunity, the record is removed from the opportunity’s Quotes related list.
  • If you undelete a previously synced quote, it’s no longer synced with the opportunity.

Retrieving sObject RecordTypeId in Apex

Usually we required to get the sObject record type id when working in Apex. There are different ways to get the record type Id in Salesforce.

We can query from RecordType object to get the record type type id of the object, like below syntax-

RecordType rt = [SELECT Id FROM RecordType WHERE SObjectType = ‘OBJECT NAME‘ AND DeveloperName = ‘RECORDTYPE NAME‘ LIMIT 1];

RecordType accRecTypeId = [Select id from RecordType where sObjectType = ‘Account’ and DeveloperName =’Person Account’ ];

This works fine, but it will use one query against our query governor limit which may create issue in different scenario.

Instead of SOQL query, its preferable to use dynamic apex to get RecordTypeId. Here is the way to do this without any query:

Id accRecordTypeId = Schema. SObjectType. Account. getRecordTypeInfosByName().                                                                                         get(‘Person Account’).getRecordTypeId();

Best way to access record type id using Schema which does not count in Salesforce SOQL governors limit. In below code, instead of hard coding the object name and record type label, dynamic approach which give flexibility to admin or developer.

 public class ObjectUtility {  
   public static Map<String,Schema.RecordTypeInfo> recordTypeInfoMap = new Map<String,Schema.RecordTypeInfo>();  
   public static Id getObjectRecordTypeId(String sObjectName, String recordTypeName) {  
     sObject obj;  
     Schema.SObjectType sObjectType = Schema.getGlobalDescribe().get(sObjectName);  
     if(sObjectType != null) {  
       obj = sObjectType.newsObject();  
       Schema.DescribeSObjectResult sObjDesc = obj.getSObjectType().getDescribe();  
       if(sObjDesc != null) {  
         recordTypeInfoMap = sObjDesc.getRecordTypeInfosByName();  
       if(!recordTypeInfoMap.containsKey(recordTypeName)) {  
         throw new RecordTypeException('Record type "'+ recordTypeName +'" does not exist.');  
       } else {  
         return recordTypeInfoMap.get(recordTypeName).getRecordTypeId();  
     return null;  
   public class RecordTypeException extends Exception{}  

Now you can retrieve SObject RecordType Id without any queries using the following code:

Now pass parameters to above method. Parameters are: Object name and Record Type label. Do not pass developer name.

ID RecordTypeId = ObjectUtility.getObjectRecordTypeId(‘Account’ , ‘PersonAccount’);

Salesforce Object Relationship

Relationships associate objects with other objects.

You can define different types of relationships by creating custom relationship fields on an object. The differences between relationship types include how they handle data deletion, record ownership, security, and required fields in page layouts.

Master-Detail (1:n) – A parent-child relationship in which the master object controls certain behaviors of the detail object:

  • When a record of the master object is deleted, its related detail records are also deleted.
  • The Owner field on the detail object is not available and is automatically set to the owner of its associated master record. Custom objects on the detail side of a master-detail relationship cannot have sharing rules, manual sharing, or queues, as these require the Owner field.
  • The detail record inherits the sharing and security settings of its master record.
  • The master-detail relationship field is required on the page layout of the detail record.
  • By default, records can’t be reparented in master-detail relationships. Administrators can, however, allow child records in master-detail relationships on custom objects to be reparented to different parent records by selecting the Allow reparenting option in the master-detail relationship definition.
  • You can define master-detail relationships between custom objects or between a custom object and a standard object.
  • However, the standard object cannot be on the detail side of a relationship with a custom object.
  • In addition, you cannot create a master-detail relationship in which the User or Lead objects are the master.
  • When you define a master-detail relationship, the custom object on which you are working is the detail side. Its data can appear as a custom related list on page layouts for the other object.

Many-to-many —You can use master-detail relationships to model many-to-many relationships between any two objects. A many-to-many relationship allows each record of one object to be linked to multiple records from another object and vice versa.

For example, you create a custom object called “Bug” that relates to the standard case object such that a bug could be related to multiple cases and a case could also be related to multiple bugs.

To create a many-to-many relationship, simply create a custom junction object with two master-detail relationship fields, each linking to the objects you want to relate.

Lookup (1:n) — This type of relationship links two objects together, but has no effect on deletion or security. Unlike master-detail fields, lookup fields are not automatically required.When you define a lookup relationship, data from one object can appear as a custom related list on page layouts for the other object.

Setting DML Option in Apex

You can specify DML options for insert and update operations by setting the desired options in the Database.DMLOptions object.

The Database.DMLOptions class has the following properties:

  • allowFieldTruncation Property [truncation behavior of fields]
  • assignmentRuleHeader Property [Assignment rule information]
  • dupicateRuleHeader [Duplicate rule information]
  • emailHeader Property [Whether automatic emails are sent]
  • localeOptions Property [The user locale for labels]
  • optAllOrNone Property [Whether the operation allows for partial success]

1. allowFieldTruncation Property

The allowFieldTruncation property specifies the truncation behavior of strings.  If you specify a value for a string and that value is too large, the value is truncated in API version below 15.

For API version 15.0 and later, if a value is specified that is too large, the operation fails and an error message is returned. The allowFieldTruncation property allows you to specify that the previous behavior, truncation, be used instead of the new behavior in Apex saved against API versions 15.0 and later.

If allowFieldTruncation  value is true, the property truncates String values that are too long.

Database.DMLOptions dml = new Database.DMLOptions();                                                         dml.allowFieldTruncation = true;

2. assignmentRuleHeader Property

The assignmentRuleHeader property specifies the assignment rule to be used when creating a case or lead.

Using the assignmentRuleHeader property, you can set these options:

  • assignmentRuleID: The ID of an assignment rule for the case or lead. The assignment rule can be active or inactive. The ID can be retrieved by querying the AssignmentRule sObject. If specified, do not specify useDefaultRule. If the value is not in the correct ID format (15-character or 18-character Salesforce ID), the call fails and an exception is returned.

Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.assignmentRuleId= '01QD0000000EqAn';

Lead l = new Lead(company='ABC', lastname='Smith');
insert l;

  • useDefaultRule: Indicates whether the default (active) assignment rule will be used for a case or lead. If specified, do not specify an assignmentRuleId.

Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule= true;

Lead l = new Lead(company='ABC', lastname='Smith');
insert l;

3. dupicateRuleHeader Property

The dupicateRuleHeader property determines whether a record that’s identified as a duplicate can be saved. Duplicate rules are part of the Duplicate Management feature.

Using the dupicateRuleHeader property, you can set these options.

  • allowSave: Indicates whether a record that’s identified as a duplicate can be saved.

Database.DMLOptions dml = new Database.DMLOptions();
dml.DuplicateRuleHeader.AllowSave = true;
Account duplicateAccount = new Account(Name='dupe');
Database.SaveResult sr = Database.insert(duplicateAccount, dml);
if (sr.isSuccess()) {
System.debug('### - Duplicate account has been inserted in Salesforce!');

4. optAllOrNone Property

The optAllOrNone property specifies whether the operation allows for partial success. If optAllOrNone is set to true, all changes are rolled back if any record causes errors. The default for this property is false and successfully processed records are committed while records with errors aren’t.

Trigger Best practices

#1: One Trigger Per Object
A single Apex Trigger is all you need for one particular object. If you develop multiple Triggers for a single object, you have no way of controlling the order of execution if those Triggers can run in the same contexts.

A single Trigger can handle all possible combinations of Trigger contexts which are:

  • before insert
  • after insert
  • before update
  • after update
  • before delete
  • after delete
  • after undelete

So as a best practice, create one Trigger per object and let it handle all of the contexts that you need. Here is an example of a Trigger that implements all possible contexts:

trigger OpportunityTrigger on Opportunity ( before insert, before update, before delete, 
                                       after insert, after update, after delete, after undelete) {
                  // trigger body

#2: Bulkify your Helper Methods
Make sure any code that runs a query or DML operation does it in a bulk manner and doesn’t execute within an iteration or a for loop. Executing queries or DML operations within an iteration adds risk that the governor limits will be exceeded. This is also true for any helper or utility methods an Apex request executes.
Governor limits are calculated at runtime. After the request is initiated (Trigger, Visualforce page, etc.), any Apex code executed in that transaction applies and shares the governor limits. So if a trigger uses some Apex methods written in a helper class, it’s important that those shared Apex methods are properly designed to handle bulk records. These methods should be written to be invoked with a set of records, especially if the method has a SOQL query or DML operation.
For example, if the Apex method performs a SOQL query, that method should receive a collection (Array, List, Set, etc.) of records so when it performs the query, it can perform the query for all records in the Apex transaction. Otherwise, if the Apex method is called individually for each record being processed, the Apex transaction will inefficiently run queries and possibly exceed the allowed number of queries allowed in that transaction. The same is true for DML statements in Apex methods.

#3: Logic-less Triggers
Make your Triggers logic-less,  means – the role of the Trigger is just to delegate the logic responsibilities to some other handler class. There are many reasons to do this. For one, testing a Trigger is difficult if all of the application logic is in the trigger itself.

If you write methods in your Triggers, those can’t be exposed for test purposes. You also can’t expose logic to be re-used anywhere else in your org.
Here is an example:
trigger OpportunityTrigger on Opportunity (after insert) {
Handler class:
public class OpportunityTriggerHandler {
  public static void handleAfterInsert(List opps) {
    // handler logic
#4: Avoid SOQL Queries or DML statements inside FOR Loops
A common mistake is that queries or DML statements are placed inside a for loop. There is a governor limit that enforces a maximum number of SOQL queries. There is another that enforces a maximum number of DML statements (insert, update, delete, undelete). When these operations are placed inside a for loop, database operations are invoked once per iteration of the loop making it very easy to reach these governor limits.
Instead, move any database operations outside of for loops. If you need to query, query once, retrieve all the necessary data in a single query, then iterate over the results. If you need to modify the data, batch up data into a list and invoke your DML once on that list of data.

#5: Using Collections, Streamlining Queries, and Efficient For Loops
It is important to use Apex Collections to efficiently query data and store the data in memory. A combination of using collections and streamlining SOQL queries can substantially help writing efficient Apex code and avoid governor limits.

#6: Querying Large Data Sets
The total number of records that can be returned by SOQL queries in a request is 50,000. If returning a large set of queries causes you to exceed your heap limit, then a SOQL query for loop must be used instead. It can process multiple batches of records through the use of internal calls to query and queryMore.

For example, if the results are too large, the syntax below causes a runtime exception:
//A runtime exception is thrown if this query returns enough records to exceed your heap limit.
Account[] accts = [SELECT id FROM account];

Instead, use a SOQL query for loop as in one of the following examples:

// Use this format for efficiency if you are executing DML statements
// within the for loop.  Be careful not to exceed the 150 DML statement limit.

Account[] accts = new Account[];

for (List acct : [SELECT id, name FROM account
                            WHERE name LIKE ‘Acme’]) {
    // Your logic here

update accts;

Let the platform chunk your large query results into batches of 200 records by using this syntax where the SOQL query is in the for loop definition, and then handle the individual datasets in the for loop logic.

#7: Use of the Limits Apex Methods to Avoid Hitting Governor Limits
Apex has a System class called Limits that lets you output debug messages for each governor limit. There are two versions of every method: the first returns the amount of the resource that has been used in the current context, while the second version contains the word limit and returns the total amount of the resource that is available for that context.

#8 : Use @future Appropriately

It is critical to write your Apex code to efficiently handle bulk or many records at a time. This is also true for asynchronous Apex methods (those annotated with the @future keyword).  Even though Apex written within an asynchronous method gets its own independent set of higher governor limits, it still has governor limits. Additionally, no more than ten @future methods can be invoked within a single Apex transaction.

#9 : Test Methods to Verify Large Datasets

Since Apex code executes in bulk, it is required to have test scenarios to verify that the Apex being tested is designed to handle large datasets and not just single records. An Apex trigger can be invoked either by a data operation from the user interface or by a data operation from the SOAP API. The API can send multiple records per batch, leading to the trigger being invoked with several records. Therefore, it is key to have test methods that verify that all Apex code is properly designed to handle larger datasets and that it does not exceed governor limits.

#10: Avoid Hardcoding IDs

When deploying Apex code between sandbox and production environments, or installing AppExchange packages, it is essential to avoid hardcoding IDs in the Apex code. By doing so, if the record IDs change between environments, the logic can dynamically identify the proper data to operate against and not fail.

Here in the below examples we are using Record Type Id, this will work fine in the environment, where code is written but if this code were deployed to  a separate org , there is no guarantee that the record type id will be the same.

for(Account a:{

//Error - hardcoded the record type id
//do some logic here.....
}else if(a.RecordTypeId=='0123000000095Km'){
//do some logic here for a different record type...

Better way to use record type is like below –

//Query for the Account record types
List<RecordType> rtypes = [Select Name, Id From RecordType
where sObjectType='Account' and isActive=true];

//Create a map between the Record Type Name and Id for easy retrieval
Map<String,String> accountRecordTypes = new Map<String,String>{};
for(RecordType rt: rtypes)

for(Account a:{

//Use the Map collection to dynamically retrieve the Record Type Id
//Avoid hardcoding Ids in the Apex code
//do some logic here.....
}else if(a.RecordTypeId==accountRecordTypes.get('High Tech')){
//do some logic here for a different record type...


Using Custom Label in Salesforce

Custom Labels are custom text values, up to 1,000 characters, that can be accessed from Apex Classes or Visualforce Pages. If Translation Workbench has been enabled for your organization, these labels can then be translated into any of the languages supports. This allows developers to create true multilingual apps by presenting information to users – for example, help text or error messages – in their native language. You can create up to 5,000 custom labels.

To access custom labels, from Setup, enter Custom Labels in the Quick Find box, then select Custom Labels.

Create Custom Labels

Create custom labels that can be referenced from Apex classes, Visualforce pages, or Lightning components to make an app multilingual.


Note – You can’t change the language of an existing custom label.
Using Custom Label in Apex and VFP –
  • In Apex use the System.Label.Label_name syntax.
  • In Visualforce and Lightning components, use the $Label global variable.

Translate Custom Labels

Translations for custom labels determine what text to display for the label’s value when a user’s default language is the translation language.


  1. In the Translations related list, click New to enter a new translation or Edit next to the language to change a translation.
  2. Select the Language you are translating into.
  3. Enter the Translation Text. This text overrides the value specified in the label’s Value field when a user’s default language is the translation language.

Using Custom Label in Apex and Visualforce Page –

Controller –

public class CustomLabelController {
public String labelValue{get;set;}
public CustomLabelController() {
labelValue = System.Label.Description;

Visualforce Page –

<apex:page controller="CustomLabelController">
<h1>Custom labels In VF Page :</h1>
<apex:outputLabel value="{!$Label.Description}"/>
<h2>Custom Label From Controller -:</h2>

Below are the Pros and Cons of using Custom Label in Salesforce


  • Code base is prepared for multi-lingual support.
  • Common error messages or help text can be reused
  • Ability to use a label in Apex Code and Visualforce Pages
  • Allows the text to be maintained by a system administrator rather than a developer
  • Data integrity is enforced if a delete attempt is made on the label and it is referenced by a Visualforce page or Apex Code
  • Readability of code and pages is improved by the use of custom label properties


  • instance must have the Translation Workbench enabled
  • Increased time to develop and coordinate with development team to ensure that custom labels are not used inappropriately
  • Limit of 1,000 characters and up to 5,000 custom labels. If developing for a large org you must take care to ensure the labels are manageable and utilized