Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions localtypings/pxtarget.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,7 @@ declare namespace ts.pxtc {
_expandedDef?: ParsedBlockDef;
_untranslatedBlock?: string; // The block definition before it was translated
_untranslatedJsDoc?: string // the jsDoc before it was translated
_untranslatedParamDefl?: pxt.Map<string>; // the parameter defaults before they were translated
_translatedLanguageCode?: string // the language this block has been translated into
_shadowOverrides?: pxt.Map<string>;
jsDoc?: string;
Expand Down
6 changes: 6 additions & 0 deletions pxtcompiler/emitter/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ namespace ts.pxtc {
if (param.labelLocalizationKey && param.label) {
locStrings[param.labelLocalizationKey] = param.label;
}

const defaultString = pxt.blocks.parameterDefaultToLocalizationString(param.defaultValue, param.type);
const defaultLocalizationKey = pxt.blocks.parameterDefaultLocalizationKey(si.qName, param.actualName);
if (defaultLocalizationKey && defaultString !== undefined) {
locStrings[defaultLocalizationKey] = defaultString;
}
}
if (comp.handlerArgs?.length) {
for (const arg of comp.handlerArgs) {
Expand Down
22 changes: 22 additions & 0 deletions pxtlib/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,28 @@ namespace pxt.blocks {
localizationKey: string;
}

export function parameterDefaultLocalizationKey(qName: string, actualName: string) {
return qName ? `${qName}|param|${actualName}|defl` : undefined;
}

export function parameterDefaultToLocalizationString(defaultValue: string, type?: string) {
if (!defaultValue) return undefined;
if (type === "string" && defaultValue.charAt(0) !== "\"") return defaultValue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this be

Suggested change
if (type === "string" && defaultValue.charAt(0) !== "\"") return defaultValue;
if (type === "string" && defaultValue.charAt(0) === "\"") return defaultValue;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

the is the 'bail out if the default value is not explicitly a string' check, e.g. if this is set to a blockid for a block that returns a string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, gotcha, thanks for the clarification!

if (defaultValue.charAt(0) !== "\"") return undefined;

try {
const value = JSON.parse(defaultValue);
return typeof value === "string" ? value : undefined;
}
catch (e) {
return undefined;
}
}

export function localizationStringToParameterDefault(value: string) {
return JSON.stringify(value);
}

// Information for blocks that compile to function calls but are defined by vanilla Blockly
// and not dynamically by BlocklyLoader
export const builtinFunctionInfo: pxt.Map<{ params: string[]; blockId: string; }> = {
Expand Down
43 changes: 35 additions & 8 deletions pxtlib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,10 @@ namespace ts.pxtc {

if (fn.attributes._untranslatedJsDoc) fn.attributes.jsDoc = fn.attributes._untranslatedJsDoc;
if (fn.attributes._untranslatedBlock) fn.attributes.jsDoc = fn.attributes._untranslatedBlock;
if (fn.attributes._untranslatedParamDefl) {
fn.attributes.paramDefl = U.clone(fn.attributes._untranslatedParamDefl);
syncParameterDefaults(fn);
}

const lookupLoc = (locSuff: string, attrKey: string) => {
return loc[fn.qName + locSuff] || fn.attributes.locs?.[attrKey]
Expand Down Expand Up @@ -758,15 +762,30 @@ namespace ts.pxtc {
}
const paramsWithLabels = comp.thisParameter ? [comp.thisParameter, ...comp.parameters] : comp.parameters;
for (const param of paramsWithLabels) {
if (!param.labelLocalizationKey) continue;

const locSuff = param.labelLocalizationKey.slice(fn.qName.length);
const paramLabel = lookupLoc(locSuff, langLower + locSuff);
if (paramLabel) {
setBlockTranslationCacheKey(param.labelLocalizationKey, paramLabel);
if (param.labelLocalizationKey) {
const locSuff = param.labelLocalizationKey.slice(fn.qName.length);
const paramLabel = lookupLoc(locSuff, langLower + locSuff);
if (paramLabel) {
setBlockTranslationCacheKey(param.labelLocalizationKey, paramLabel);
}
else {
clearBlockTranslationCacheKey(param.labelLocalizationKey);
}
}
else {
clearBlockTranslationCacheKey(param.labelLocalizationKey);

const defaultString = pxt.blocks.parameterDefaultToLocalizationString(param.defaultValue, param.type);
const defaultLocalizationKey = pxt.blocks.parameterDefaultLocalizationKey(fn.qName, param.actualName);
if (defaultLocalizationKey && defaultString !== undefined) {
const locSuff = defaultLocalizationKey.slice(fn.qName.length);
const paramDefault = lookupLoc(locSuff, langLower + locSuff);
if (paramDefault !== undefined) {
if (!fn.attributes._untranslatedParamDefl) {
fn.attributes._untranslatedParamDefl = U.clone(fn.attributes.paramDefl || {});
}
if (!fn.attributes.paramDefl) fn.attributes.paramDefl = {};
fn.attributes.paramDefl[param.actualName] = pxt.blocks.localizationStringToParameterDefault(paramDefault);
syncParameterDefaults(fn);
}
}
}
}
Expand Down Expand Up @@ -835,6 +854,14 @@ namespace ts.pxtc {
return cleanLocalizations(apis);
}

function syncParameterDefaults(fn: SymbolInfo) {
if (!fn.parameters) return;

for (const param of fn.parameters) {
param.default = fn.attributes.paramDefl && fn.attributes.paramDefl[param.name];
}
}

function cleanLocalizations(apis: ApisInfo) {
Util.values(apis.byQName)
.filter(fb => fb.attributes.block && /^{[^:]+:[^}]+}/.test(fb.attributes.block))
Expand Down
50 changes: 50 additions & 0 deletions tests/blocklycompiler-test/commentparsing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,55 @@ describe("comment attribute parser", () => {
chai.expect(strings["pins.DigitalPin.digitalRead|param|this|label"]).to.equal("pin");
});

it("should compile and extract parameter default strings", () => {
const docDefault = testSymbolInfo("display.showString", "show string $text", [testParameter("text", "string")], `
/**
* Show text on the screen.
* @param text the text to print on the screen, eg: "name"
*/
`);
const explicitDefault = testSymbolInfo("game.setGameOverMessage", "use message $message", [testParameter("message", "string")], `
/**
* Set the message that displays when the game is over.
* @param message the message, eg: "Try again"
*/
//% message.defl="winner"
`);
const numericDefault = testSymbolInfo("display.showNumber", "show number $value", [testParameter("value", "number")], `
/**
* Show a number on the screen.
* @param value the number to show, eg: 42
*/
`);

const docs = pxtc.genDocs("test", testApisInfo([docDefault, explicitDefault, numericDefault]), { locs: true });
const strings = JSON.parse(docs["test-strings.json"]);
chai.expect(strings["display.showString|param|text|defl"]).to.equal("name");
chai.expect(strings["game.setGameOverMessage|param|message|defl"]).to.equal("winner");
chai.expect(strings["display.showNumber|param|value|defl"]).to.equal(undefined);
});

it("should apply localized parameter default strings", async () => {
const explicitDefault = testSymbolInfo("game.setGameOverMessage", "use message $message", [testParameter("message", "string")], `
//% message.defl="GAME OVER!"
`);

pxt.Util.setUserLanguage("es");
try {
await pxtc.localizeApisAsync(testApisInfo([explicitDefault]), {
localizationStringsAsync: () => Promise.resolve({
"game.setGameOverMessage|param|message|defl": "FIN DEL JUEGO"
})
} as unknown as pxt.MainPackage);

chai.expect(explicitDefault.attributes.paramDefl["message"]).to.equal("\"FIN DEL JUEGO\"");
chai.expect(explicitDefault.parameters[0].default).to.equal("\"FIN DEL JUEGO\"");
}
finally {
pxt.Util.setUserLanguage("en");
}
});

it("should parse parameter snippets", () => {
const parsed = ts.pxtc.parseCommentString(`
/**
Expand Down Expand Up @@ -539,6 +588,7 @@ function testSymbolInfo(qName: string, block: string, parameters: pxtc.Parameter
//% block="${block}"
${attributes}
`);
parameters.forEach(parameter => parameter.default = attrs.paramDefl[parameter.name]);
const qNameParts = qName.split(".");
return {
attributes: attrs,
Expand Down
Loading