Skip to content

Translation Metadata addition to GLaaS#189

Merged
janaki-r-bhagwath merged 5 commits intomainfrom
loc
Feb 17, 2026
Merged

Translation Metadata addition to GLaaS#189
janaki-r-bhagwath merged 5 commits intomainfrom
loc

Conversation

@janaki-r-bhagwath
Copy link
Contributor

  • HTML annotation for maxlength and keywords based on block-schema
    (its-storage-size, its-loc-note, its-loc-note-type)
  • Per-language keywords metadata (langMetadata) sent to GLaaS
  • Single <p> tag unwrapping for cleaner HTML structure

@aem-code-sync
Copy link

aem-code-sync bot commented Feb 12, 2026

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
In case there are problems, just click a checkbox below to rerun the respective action.

  • Re-run PSI checks
  • Re-sync branch
Commits

jenssingler
jenssingler previously approved these changes Feb 13, 2026
const { id, selector } = processSchemaKey(key);
const fields = [];
blockData.data.forEach((field) => {
const fieldName = field['field name'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea how rigid schemaData is, but is there any need to possibly support field_name too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is dev/super-author file. So, I don't think we need to support field_name. Originally I had fieldName in the sheet, but Chris M. felt it was too code-like :)

Copy link
Contributor

@chrischrischris chrischrischris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few optimizations to reduce querySelector usage. Might be overkill if the docs are small


const BLOCK_SCHEMA_PATH = '/.da/block-schema.json';

let BLOCK_SCHEMA_CACHE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: All caps usually used for constants

Comment on lines 131 to 144
function unwrapSoleParagraphs(doc) {
doc.querySelectorAll('div[class]').forEach((block) => {
const rows = block.querySelectorAll(':scope > div');
rows.forEach((row) => {
const columns = row.querySelectorAll(':scope > div');
columns.forEach((div) => {
if (div.children.length === 1 && div.children[0].tagName === 'P') {
const pTag = div.children[0];
div.replaceChildren(...pTag.childNodes);
}
});
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be optimized a bit

Suggested change
function unwrapSoleParagraphs(doc) {
doc.querySelectorAll('div[class]').forEach((block) => {
const rows = block.querySelectorAll(':scope > div');
rows.forEach((row) => {
const columns = row.querySelectorAll(':scope > div');
columns.forEach((div) => {
if (div.children.length === 1 && div.children[0].tagName === 'P') {
const pTag = div.children[0];
div.replaceChildren(...pTag.childNodes);
}
});
});
});
}
function unwrapSoleParagraphs(doc) {
doc.querySelectorAll('div[class] > div > div').forEach((div) => {
if (div.children.length === 1 && div.children[0].tagName === 'P') {
const pTag = div.children[0];
div.replaceChildren(...pTag.childNodes);
}
});
}

Comment on lines 177 to 202
Object.values(parsedSchema).forEach((block) => {
const { selector, fields } = block;
const blockElements = doc.querySelectorAll(selector);

blockElements.forEach((blockElement, blockIndex) => {
const blockId = Object.keys(parsedSchema).find(
(id) => parsedSchema[id].selector === selector,
);
const rows = blockElement.querySelectorAll(':scope > div');
rows.forEach((row) => {
const labelDiv = row.querySelector(':scope > div:nth-child(1)');
const contentDiv = row.querySelector(':scope > div:nth-child(2)');
if (!labelDiv || !contentDiv) return;
const field = fields.find((f) => isExactMatch(labelDiv, f.fieldName));
if (!field) return;
const { fieldName, fieldKey, charCount, keywordsInjection } = field;
if (charCount) {
contentDiv.setAttribute('its-storage-size', charCount);
}
const keywordsValue = String(keywordsInjection);
const locNoteValue = `block-name=${blockId}_${blockIndex + 1}_${fieldKey}|fieldName=${fieldName}|apply-keywords=${keywordsValue}`;
contentDiv.setAttribute('its-loc-note', locNoteValue);
contentDiv.setAttribute('its-loc-note-type', 'description');
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be optimized a bit

Suggested change
Object.values(parsedSchema).forEach((block) => {
const { selector, fields } = block;
const blockElements = doc.querySelectorAll(selector);
blockElements.forEach((blockElement, blockIndex) => {
const blockId = Object.keys(parsedSchema).find(
(id) => parsedSchema[id].selector === selector,
);
const rows = blockElement.querySelectorAll(':scope > div');
rows.forEach((row) => {
const labelDiv = row.querySelector(':scope > div:nth-child(1)');
const contentDiv = row.querySelector(':scope > div:nth-child(2)');
if (!labelDiv || !contentDiv) return;
const field = fields.find((f) => isExactMatch(labelDiv, f.fieldName));
if (!field) return;
const { fieldName, fieldKey, charCount, keywordsInjection } = field;
if (charCount) {
contentDiv.setAttribute('its-storage-size', charCount);
}
const keywordsValue = String(keywordsInjection);
const locNoteValue = `block-name=${blockId}_${blockIndex + 1}_${fieldKey}|fieldName=${fieldName}|apply-keywords=${keywordsValue}`;
contentDiv.setAttribute('its-loc-note', locNoteValue);
contentDiv.setAttribute('its-loc-note-type', 'description');
});
});
});
Object.entries(parsedSchema).forEach(([blockId, block]) => {
const { selector, fields } = block;
const blockElements = doc.querySelectorAll(selector);
blockElements.forEach((blockElement, blockIndex) => {
const rows = blockElement.querySelectorAll(':scope > div');
rows.forEach((row) => {
const labelDiv = row.children[0];
const contentDiv = row.children[1];
if (!labelDiv || !contentDiv || labelDiv.tagName !== 'DIV' || contentDiv.tagName !== 'DIV') {
return;
}
const field = fields.find((f) => isExactMatch(labelDiv, f.fieldName));
if (!field) return;
const { fieldName, fieldKey, charCount, keywordsInjection } = field;
if (charCount) {
contentDiv.setAttribute('its-storage-size', charCount);
}
const keywordsValue = String(keywordsInjection);
const locNoteValue = `block-name=${blockId}_${blockIndex + 1}_${fieldKey}|fieldName=${fieldName}|apply-keywords=${keywordsValue}`;
contentDiv.setAttribute('its-loc-note', locNoteValue);
contentDiv.setAttribute('its-loc-note-type', 'description');
});
});
});

Comment on lines 211 to 214
Object.keys(keywordsData).forEach((key) => {
if (key.startsWith(':')) return;
const blockData = keywordsData[key];
if (!blockData.data) return;
Copy link
Contributor

@chrischrischris chrischrischris Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use Object.entries to get both vals at once

Suggested change
Object.keys(keywordsData).forEach((key) => {
if (key.startsWith(':')) return;
const blockData = keywordsData[key];
if (!blockData.data) return;
Object.entries(keywordsData).forEach(([key, blockData]) => {
if (key.startsWith(':') || !blockData?.data) continue;

blockData.data.forEach((entry) => {
const languageName = entry.language;
if (!languageName) return;
const langCode = languageNameToCode(languageName, langs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the languageNameToCode and fieldNameToKey calls, if they're being called a lot for the same values you could memoize the lookups. Not sure how large keywordsData/blockData can get to

Comment on lines 265 to 273
if (hasKeywords) {
const keywordsData = await fetchKeywordsFile(org, site, url.suppliedPath);
if (keywordsData) {
const langMetadata = buildLanguageMetadata(keywordsData, langs);
if (langMetadata && Object.keys(langMetadata).length > 0) {
url.translationMetadata = langMetadata;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit early return

Suggested change
if (hasKeywords) {
const keywordsData = await fetchKeywordsFile(org, site, url.suppliedPath);
if (keywordsData) {
const langMetadata = buildLanguageMetadata(keywordsData, langs);
if (langMetadata && Object.keys(langMetadata).length > 0) {
url.translationMetadata = langMetadata;
}
}
}
if (!hasKeywords) return;
const keywordsData = await fetchKeywordsFile(org, site, url.suppliedPath);
if (!keywordsData) return;
const langMetadata = buildLanguageMetadata(keywordsData, langs);
if (langMetadata && Object.keys(langMetadata).length > 0) {
url.translationMetadata = langMetadata;
}

janaki-r-bhagwath and others added 5 commits February 17, 2026 00:26
- HTML annotation for maxlength and keywords based on block-schema
(its-storage-size, its-loc-note, its-loc-note-type)
- Per-language keywords metadata (langMetadata) sent to GLaaS
- Single <p> tag unwrapping for cleaner HTML structure
@janaki-r-bhagwath janaki-r-bhagwath merged commit 0cbbfa0 into main Feb 17, 2026
3 of 4 checks passed
@janaki-r-bhagwath janaki-r-bhagwath restored the loc branch February 17, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants