Skip to content
Merged
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
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