Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## v2.6.0

### Feb 23, 2026
- Enhancement: Asset localization added

## v2.5.0

### Feb 12, 2026
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.contentstack.sdk</groupId>
<artifactId>java</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
<packaging>jar</packaging>
<name>contentstack-java</name>
<description>Java SDK for Contentstack Content Delivery API</description>
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/contentstack/sdk/Asset.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class Asset {
protected String fileSize = null;
protected String fileName = null;
protected String uploadUrl = null;
protected String language = null;
protected JSONObject json = null;
protected String[] tagsArray = null;
protected LinkedHashMap<String, Object> headers;
Expand Down Expand Up @@ -75,6 +76,7 @@ public Asset configure(JSONObject jsonObject) {
this.contentType = model.contentType;
this.fileSize = model.fileSize;
this.uploadUrl = model.uploadUrl;
this.language = model.language;
this.fileName = model.fileName;
this.json = model.json;
this.assetUid = model.uploadedUid;
Expand Down Expand Up @@ -558,6 +560,38 @@ public Asset assetFields(String... fields) {
return this;
}

/**
* Specifies the fields to be included in the asset response.
* <p>
* This method allows you to specify one or more field names, and only those fields
* will be included in the returned asset data. This is useful for reducing response size
* and fetching only the required asset properties.
*
* @param fields Variable number of field names to be included in the asset response.
* @return The {@link Asset} instance for chaining further calls.
*
* <b>Example:</b><br>
* <pre class="prettyprint">
* Asset asset = stack.asset("asset_uid");
* asset.assetFields("title", "filename");
* </pre>
*/

public Asset setLocale(String locale) {
urlQueries.put("locale",locale);
return this;
}


/**
* Returns the locale (language) associated with this asset.
*
* @return The asset's locale as a {@link String}, or {@code null} if not set.
*/
public String getLocale() {
return this.language;
}

/**
* Fetch.
*
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/contentstack/sdk/AssetLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,28 @@ public AssetLibrary includeMetadata() {
return this;
}

/**
* Sets the locale for asset queries.
* <p>
* This method allows you to specify a locale code, so asset results are returned
* for a particular language or region. If not explicitly set, the default locale
* configured in the stack will be used.
*
* @param locale The locale code to filter assets by (e.g., "en-us").
* @return The {@link AssetLibrary} instance for method chaining.
*
* <b>Example:</b>
* <pre class="prettyprint">
* Stack stack = Contentstack.stack("apiKey", "deliveryToken", "environment");
* AssetLibrary assetLibrary = stack.assetLibrary();
* assetLibrary.setLocale("en-us");
* </pre>
*/
public AssetLibrary setLocale(String locale) {
urlQueries.put("locale",locale);
return this;
}

/**
* Gets count.
*
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/contentstack/sdk/AssetModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class AssetModel {
String fileSize;
String fileName;
String uploadUrl;
String language;
String[] tags;
JSONObject json;
int count = 0;
Expand All @@ -45,6 +46,7 @@ public AssetModel(JSONObject response, boolean isArray) {
fileSize = (String) json.opt("file_size");
fileName = (String) json.opt("filename");
uploadUrl = (String) json.opt("url");
language = (String) json.opt("locale");
if (json.opt("tags") instanceof JSONArray) {
extractTags();
}
Expand Down
73 changes: 73 additions & 0 deletions src/test/java/com/contentstack/sdk/TestAsset.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,79 @@ void testConfigureWithMinimalJson() {
assertSame(asset, result);
}

@Test
void testConfigureWithLocaleSetsLanguage() {
JSONObject json = new JSONObject();
json.put("uid", "locale_asset_uid");
json.put("locale", "en-us");
json.put("filename", "localized.jpg");

asset.configure(json);
assertEquals("en-us", asset.getLocale());
assertEquals("en-us", asset.language);
}

@Test
void testConfigureWithoutLocaleLeavesLanguageNull() {
JSONObject json = new JSONObject();
json.put("uid", "no_locale_uid");
json.put("filename", "test.jpg");

asset.configure(json);
assertNull(asset.getLocale());
assertNull(asset.language);
}

// ========== LOCALE TESTS (setLocale / getLocale) ==========

@Test
void testSetLocale() {
Asset result = asset.setLocale("en-us");
assertSame(asset, result);
assertTrue(asset.urlQueries.has("locale"));
assertEquals("en-us", asset.urlQueries.get("locale"));
}

@Test
void testSetLocaleReturnsThisForChaining() {
Asset result = asset.setLocale("fr-fr").includeDimension().includeMetadata();
assertSame(asset, result);
assertEquals("fr-fr", asset.urlQueries.get("locale"));
assertTrue(asset.urlQueries.has("include_dimension"));
assertTrue(asset.urlQueries.has("include_metadata"));
}

@Test
void testSetLocaleOverwritesPrevious() {
asset.setLocale("en-us");
assertEquals("en-us", asset.urlQueries.get("locale"));
asset.setLocale("de-de");
assertEquals("de-de", asset.urlQueries.get("locale"));
}

@Test
void testGetLocaleBeforeConfigureReturnsNull() {
assertNull(asset.getLocale());
}

@Test
void testGetLocaleAfterConfigureWithLocale() {
JSONObject json = new JSONObject();
json.put("uid", "uid");
json.put("locale", "ja-jp");
asset.configure(json);
assertEquals("ja-jp", asset.getLocale());
}

@Test
void testGetLocaleAfterSetLocaleOnlySetsQueryNotLanguage() {
asset.setLocale("es-es");
// setLocale only puts in urlQueries; it does not set this.language
assertTrue(asset.urlQueries.has("locale"));
assertEquals("es-es", asset.urlQueries.get("locale"));
assertNull(asset.getLocale()); // getLocale returns this.language, not urlQueries
}

// ========== HEADER TESTS ==========

@Test
Expand Down
35 changes: 35 additions & 0 deletions src/test/java/com/contentstack/sdk/TestAssetLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,41 @@ void testIncludeMetadata() {
assertEquals(true, assetLibrary.urlQueries.get("include_metadata"));
}

// ========== LOCALE TESTS (setLocale for asset localisation) ==========

@Test
void testSetLocale() {
AssetLibrary result = assetLibrary.setLocale("en-us");
assertSame(assetLibrary, result);
assertTrue(assetLibrary.urlQueries.has("locale"));
assertEquals("en-us", assetLibrary.urlQueries.get("locale"));
}

@Test
void testSetLocaleReturnsThisForChaining() {
AssetLibrary result = assetLibrary.setLocale("fr-fr").includeCount().limit(10);
assertSame(assetLibrary, result);
assertEquals("fr-fr", assetLibrary.urlQueries.get("locale"));
assertTrue(assetLibrary.urlQueries.has("include_count"));
assertEquals(10, assetLibrary.urlQueries.get("limit"));
}

@Test
void testSetLocaleOverwritesPrevious() {
assetLibrary.setLocale("en-us");
assertEquals("en-us", assetLibrary.urlQueries.get("locale"));
assetLibrary.setLocale("de-de");
assertEquals("de-de", assetLibrary.urlQueries.get("locale"));
}

@Test
void testSetLocaleWithVariousLocaleCodes() {
assetLibrary.setLocale("ja-jp");
assertEquals("ja-jp", assetLibrary.urlQueries.get("locale"));
assetLibrary.setLocale("pt-br");
assertEquals("pt-br", assetLibrary.urlQueries.get("locale"));
}

// ========== ASSET FIELDS TESTS (CDA asset_fields[] parameter) ==========

@Test
Expand Down
64 changes: 64 additions & 0 deletions src/test/java/com/contentstack/sdk/TestAssetModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,70 @@ void testConstructorWithAllFields() {
assertEquals(2, model.tags.length);
}

// ========== LOCALE / LANGUAGE TESTS (asset localisation) ==========

@Test
void testConstructorWithLocaleIsArrayTrue() {
JSONObject response = new JSONObject();
response.put("uid", "localized_asset");
response.put("filename", "localized.jpg");
response.put("locale", "en-us");

AssetModel model = new AssetModel(response, true);

assertNotNull(model);
assertEquals("localized_asset", model.uploadedUid);
assertEquals("en-us", model.language);
}

@Test
void testConstructorWithoutLocaleLeavesLanguageNull() {
JSONObject response = new JSONObject();
response.put("uid", "no_locale_asset");
response.put("filename", "test.jpg");

AssetModel model = new AssetModel(response, true);

assertNotNull(model);
assertNull(model.language);
}

@Test
void testConstructorWithVariousLocaleCodes() {
JSONObject response = new JSONObject();
response.put("uid", "uid");
response.put("locale", "fr-fr");

AssetModel model = new AssetModel(response, true);
assertEquals("fr-fr", model.language);

JSONObject response2 = new JSONObject();
response2.put("uid", "uid2");
response2.put("locale", "ja-jp");
AssetModel model2 = new AssetModel(response2, true);
assertEquals("ja-jp", model2.language);
}

@Test
void testConstructorWithLocaleAndOtherFields() {
JSONObject response = new JSONObject();
response.put("uid", "full_localized");
response.put("content_type", "image/png");
response.put("file_size", "1024");
response.put("filename", "image.png");
response.put("url", "https://cdn.example.com/image.png");
response.put("locale", "de-de");

AssetModel model = new AssetModel(response, true);

assertEquals("full_localized", model.uploadedUid);
assertEquals("image/png", model.contentType);
assertEquals("1024", model.fileSize);
assertEquals("image.png", model.fileName);
assertEquals("https://cdn.example.com/image.png", model.uploadUrl);
assertEquals("de-de", model.language);
}

@Test
void testConstructorWithMinimalData() {
JSONObject response = new JSONObject();
Expand Down
Loading