Skip to content

Commit 1aa450b

Browse files
committed
Add localization to toolbox categories
1 parent 71441af commit 1aa450b

16 files changed

+153
-83
lines changed

src/App.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { mutatorOpenListener } from './blocks/mrc_param_container'
4848
import { TOOLBOX_UPDATE_EVENT } from './blocks/mrc_mechanism_component_holder';
4949
import { antdThemeFromString } from './reactComponents/ThemeModal';
5050
import { useTranslation } from 'react-i18next';
51+
import { setup } from './blocks/mrc_event_handler';
5152

5253
/** Storage key for shown toolbox categories. */
5354
const SHOWN_TOOLBOX_CATEGORIES_KEY = 'shownPythonToolboxCategories';
@@ -87,7 +88,7 @@ const LAYOUT_BACKGROUND_COLOR = '#0F0';
8788
* project management, and user interface layout.
8889
*/
8990
const App: React.FC = (): React.JSX.Element => {
90-
const { t } = useTranslation();
91+
const { t, i18n } = useTranslation();
9192

9293
const [alertErrorMessage, setAlertErrorMessage] = React.useState('');
9394
const [storage, setStorage] = React.useState<commonStorage.Storage | null>(null);
@@ -263,7 +264,7 @@ const App: React.FC = (): React.JSX.Element => {
263264
if (blocksEditor.current && currentModule) {
264265
blocksEditor.current.updateToolbox(shownPythonToolboxCategories);
265266
}
266-
}, [currentModule, shownPythonToolboxCategories]);
267+
}, [currentModule, shownPythonToolboxCategories, i18n.language]);
267268

268269
// Add event listener for toolbox updates
269270
React.useEffect(() => {
@@ -294,6 +295,23 @@ const App: React.FC = (): React.JSX.Element => {
294295
}
295296
}, [currentModule]);
296297

298+
const setupWorkspace = (newWorkspace: Blockly.WorkspaceSvg) => {
299+
if (!blocklyComponent.current || !storage) {
300+
return;
301+
}
302+
// Recreate workspace when Blockly component is ready
303+
ChangeFramework.setup(newWorkspace);
304+
newWorkspace.addChangeListener(mutatorOpenListener);
305+
newWorkspace.addChangeListener(handleBlocksChanged);
306+
generatorContext.current = createGeneratorContext();
307+
308+
if (currentModule) {
309+
generatorContext.current.setModule(currentModule);
310+
}
311+
312+
blocksEditor.current = new editor.Editor(newWorkspace, generatorContext.current, storage);
313+
blocksEditor.current.updateToolbox(shownPythonToolboxCategories);
314+
};
297315

298316
// Initialize Blockly workspace and editor when component and storage are ready
299317
React.useEffect(() => {
@@ -303,17 +321,8 @@ const App: React.FC = (): React.JSX.Element => {
303321

304322
const blocklyWorkspace = blocklyComponent.current.getBlocklyWorkspace();
305323
if (blocklyWorkspace) {
306-
ChangeFramework.setup(blocklyWorkspace);
307-
blocklyWorkspace.addChangeListener(mutatorOpenListener);
308-
blocklyWorkspace.addChangeListener(handleBlocksChanged);
309-
}
310-
311-
generatorContext.current = createGeneratorContext();
312-
if (currentModule) {
313-
generatorContext.current.setModule(currentModule);
324+
setupWorkspace(blocklyWorkspace);
314325
}
315-
316-
blocksEditor.current = new editor.Editor(blocklyWorkspace, generatorContext.current, storage);
317326
}, [blocklyComponent, storage]);
318327

319328
// Generate code when module or regeneration trigger changes
@@ -395,6 +404,7 @@ const App: React.FC = (): React.JSX.Element => {
395404
<Content>
396405
<BlocklyComponent
397406
theme={theme}
407+
onWorkspaceRecreated={setupWorkspace}
398408
ref={blocklyComponent}
399409
/>
400410
</Content>

src/blocks/tokens.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,20 @@ export const customTokens = (t: (key: string) => string): typeof Blockly.Msg =>
2727
OPMODE_ENABLED_TOOLTIP: t('BLOCKLY.TOOLTIP.OPMODE_ENABLED_TOOLTIP'),
2828
OPMODE_NAME_TOOLTIP: t('BLOCKLY.TOOLTIP.OPMODE_NAME_TOOLTIP'),
2929
OPMODE_GROUP_TOOLTIP: t('BLOCKLY.TOOLTIP.OPMODE_GROUP_TOOLTIP'),
30+
MRC_CATEGORY_HARDWARE: t('BLOCKLY.CATEGORY.HARDWARE'),
31+
MRC_CATEGORY_ROBOT: t('BLOCKLY.CATEGORY.ROBOT'),
32+
MRC_CATEGORY_COMPONENTS: t('BLOCKLY.CATEGORY.COMPONENTS'),
33+
MRC_CATEGORY_MECHANISMS: t('BLOCKLY.CATEGORY.MECHANISMS'),
34+
MRC_CATEGORY_LOGIC: t('BLOCKLY.CATEGORY.LOGIC'),
35+
MRC_CATEGORY_LOOPS: t('BLOCKLY.CATEGORY.LOOPS'),
36+
MRC_CATEGORY_LISTS: t('BLOCKLY.CATEGORY.LISTS'),
37+
MRC_CATEGORY_MATH: t('BLOCKLY.CATEGORY.MATH'),
38+
MRC_CATEGORY_TEXT: t('BLOCKLY.CATEGORY.TEXT'),
39+
MRC_CATEGORY_MISC: t('BLOCKLY.CATEGORY.MISC'),
40+
MRC_CATEGORY_VARIABLES: t('BLOCKLY.CATEGORY.VARIABLES'),
41+
MRC_CATEGORY_METHODS: t('BLOCKLY.CATEGORY.METHODS'),
42+
MRC_CATEGORY_EVENTS: t('BLOCKLY.CATEGORY.EVENTS'),
43+
MRC_CATEGORY_ADD_MECHANISM: t('BLOCKLY.CATEGORY.ADD_MECHANISM'),
44+
MRC_CATEGORY_ADD_COMPONENT: t('BLOCKLY.CATEGORY.ADD_COMPONENT'),
3045
};
3146
};

src/i18n/locales/en/translation.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,23 @@
5757
"OPMODE_ENABLED": "Whether the OpMode is shown on Driver Station",
5858
"OPMODE_NAME": "The name shown on the Driver Station. If blank will use the class name.",
5959
"OPMODE_GROUP": "An optional group to group OpModes on Driver Station"
60+
},
61+
"CATEGORY":{
62+
"LISTS": "Lists",
63+
"HARDWARE": "Hardware",
64+
"COMPONENTS": "Components",
65+
"ROBOT": "Robot",
66+
"MECHANISMS": "Mechanisms",
67+
"LOGIC": "Logic",
68+
"LOOPS": "Loops",
69+
"MATH": "Math",
70+
"TEXT": "Text",
71+
"MISC": "Miscellaneous",
72+
"VARIABLES": "Variables",
73+
"METHODS": "Methods",
74+
"EVENTS": "Events",
75+
"ADD_MECHANISM": "+ Mechanism",
76+
"ADD_COMPONENT": "+ Component"
6077
}
6178
}
6279
}

src/i18n/locales/es/translation.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@
5757
"OPMODE_ENABLED": "Si el OpMode se muestra en la Estación del Conductor",
5858
"OPMODE_NAME": "El nombre mostrado en la Estación del Conductor. Si está en blanco usará el nombre de la clase.",
5959
"OPMODE_GROUP": "Un grupo opcional para agrupar OpModes en la Estación del Conductor"
60-
}
60+
},
61+
"CATEGORY":{
62+
"LISTS": "Listas",
63+
"HARDWARE": "Hardware",
64+
"COMPONENTS": "Componentes",
65+
"ROBOT": "Robot",
66+
"MECHANISMS": "Mecanismos",
67+
"LOGIC": "Lógica",
68+
"LOOPS": "Bucles",
69+
"MATH": "Matemáticas",
70+
"TEXT": "Texto",
71+
"MISC": "Varios",
72+
"VARIABLES": "Variables",
73+
"METHODS": "Métodos",
74+
"EVENTS": "Eventos",
75+
"ADD_MECHANISM": "+ Mecanismo",
76+
"ADD_COMPONENT": "+ Componente"
6177
}
6278
}

src/reactComponents/BlocklyComponent.tsx

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface BlocklyComponentType {
3939
/** Interface for props passed to the BlocklyComponent. */
4040
export interface BlocklyComponentProps {
4141
theme: string;
42+
onWorkspaceRecreated: (workspace: Blockly.WorkspaceSvg) => void;
4243
}
4344

4445
/** Grid spacing for the Blockly workspace. */
@@ -79,15 +80,15 @@ const WORKSPACE_STYLE: React.CSSProperties = {
7980
* cleanup, and resize handling.
8081
*/
8182
const BlocklyComponent = React.forwardRef<BlocklyComponentType | null, BlocklyComponentProps>(
82-
({ theme }, ref): React.JSX.Element => {
83+
(props, ref): React.JSX.Element => {
8384
const blocklyDiv = React.useRef<HTMLDivElement | null>(null);
8485
const workspaceRef = React.useRef<Blockly.WorkspaceSvg | null>(null);
8586

8687
const { t, i18n } = useTranslation();
8788

8889

8990
const getBlocklyTheme = (): Blockly.Theme => {
90-
const blocklyTheme = 'mrc_theme_' + theme.replace(/-/g, '_');
91+
const blocklyTheme = 'mrc_theme_' + props.theme.replace(/-/g, '_');
9192
// Find the theme by key
9293
const themeObj = themes.find(t => t.name === blocklyTheme);
9394
if (!themeObj) {
@@ -140,12 +141,6 @@ const BlocklyComponent = React.forwardRef<BlocklyComponentType | null, BlocklyCo
140141
return;
141142
}
142143

143-
// Save the current workspace state
144-
const workspaceXml = Blockly.Xml.workspaceToDom(workspaceRef.current);
145-
146-
// Dispose of the current workspace
147-
workspaceRef.current.dispose();
148-
149144
// Set new locale
150145
switch (i18n.language) {
151146
case 'es':
@@ -162,16 +157,26 @@ const BlocklyComponent = React.forwardRef<BlocklyComponentType | null, BlocklyCo
162157
// Apply custom tokens
163158
Blockly.setLocale(customTokens(t));
164159

165-
// Re-create workspace with new locale
166-
const workspaceConfig = createWorkspaceConfig();
167-
const newWorkspace = Blockly.inject(blocklyDiv.current!, workspaceConfig);
168-
workspaceRef.current = newWorkspace;
160+
// Save workspace state
161+
const workspaceXml = Blockly.Xml.workspaceToDom(workspaceRef.current);
169162

170-
// Restore the workspace state (this will create blocks with new locale)
171-
Blockly.Xml.domToWorkspace(workspaceXml, newWorkspace);
163+
// Clear the workspace
164+
workspaceRef.current.clear();
165+
166+
// Restore workspace with new locale (blocks will be recreated with new text)
167+
if (workspaceXml.hasChildNodes()) {
168+
Blockly.Xml.domToWorkspace(workspaceXml, workspaceRef.current);
169+
}
170+
171+
// Refresh the toolbox
172+
const toolbox = workspaceRef.current.getToolbox();
173+
if (toolbox) {
174+
// Force toolbox to rebuild with new locale
175+
toolbox.refreshSelection();
176+
}
172177

173-
// Re-apply any event listeners that were on the original workspace
174-
// You may need to call your setup functions here again
178+
// Trigger workspace refresh
179+
Blockly.svgResize(workspaceRef.current);
175180
};
176181

177182
/** Initializes the Blockly workspace. */
@@ -250,7 +255,7 @@ const BlocklyComponent = React.forwardRef<BlocklyComponentType | null, BlocklyCo
250255
const newTheme = getBlocklyTheme();
251256
workspaceRef.current.setTheme(newTheme);
252257
}
253-
}, [theme]);
258+
}, [props.theme]);
254259

255260
React.useEffect(() => {
256261
updateBlocklyLocale();

src/toolbox/event_category.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ import { RETURN_TYPE_NONE, FunctionKind } from '../blocks/mrc_call_python_functi
2929

3030
const CUSTOM_CATEGORY_EVENTS = 'EVENTS';
3131

32-
export const category = {
32+
export const getCategory = () => ({
3333
kind: 'category',
3434
categorystyle: MRC_CATEGORY_STYLE_METHODS,
35-
name: 'Events',
35+
name: Blockly.Msg['MRC_CATEGORY_EVENTS'],
3636
custom: CUSTOM_CATEGORY_EVENTS,
37-
};
37+
});
3838

3939
export class EventsCategory {
4040
private currentModule: commonStorage.Module | null = null;

src/toolbox/hardware_category.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function getHardwareCategory(currentModule: commonStorage.Module) {
3030
if (currentModule.moduleType === commonStorage.MODULE_TYPE_ROBOT) {
3131
return {
3232
kind: 'category',
33-
name: 'Hardware',
33+
name: Blockly.Msg['MRC_CATEGORY_HARDWARE'],
3434
contents: [
3535
getRobotMechanismsBlocks(currentModule),
3636
getComponentsBlocks(currentModule, false),
@@ -43,7 +43,7 @@ export function getHardwareCategory(currentModule: commonStorage.Module) {
4343
if (currentModule.moduleType === commonStorage.MODULE_TYPE_OPMODE) {
4444
return {
4545
kind: 'category',
46-
name: 'Robot',
46+
name: Blockly.Msg['MRC_CATEGORY_ROBOT'],
4747
contents: [
4848
getRobotMechanismsBlocks(currentModule),
4949
getRobotComponentsBlocks(currentModule),
@@ -67,7 +67,7 @@ function getRobotMechanismsBlocks(currentModule: commonStorage.Module) {
6767
if (currentModule.moduleType === commonStorage.MODULE_TYPE_ROBOT) {
6868
contents.push({
6969
kind: 'category',
70-
name: '+ Mechanism',
70+
name: Blockly.Msg['MRC_CATEGORY_ADD_MECHANISM'],
7171
contents: getAllPossibleMechanisms(),
7272
});
7373
}
@@ -203,7 +203,7 @@ function getRobotMechanismsBlocks(currentModule: commonStorage.Module) {
203203

204204
return {
205205
kind: 'category',
206-
name: 'Mechanisms',
206+
name: Blockly.Msg['MRC_CATEGORY_MECHANISMS'],
207207
contents,
208208
};
209209
}
@@ -233,7 +233,7 @@ function getRobotComponentsBlocks(currentModule: commonStorage.Module) {
233233

234234
return {
235235
kind: 'category',
236-
name: 'Components',
236+
name: Blockly.Msg['MRC_CATEGORY_COMPONENTS'],
237237
contents,
238238
};
239239
}
@@ -246,7 +246,7 @@ function getRobotMethodsBlocks(currentModule: commonStorage.Module) {
246246

247247
return {
248248
kind: 'category',
249-
name: 'Methods',
249+
name: Blockly.Msg['MRC_CATEGORY_METHODS'],
250250
contents,
251251
};
252252
}
@@ -260,7 +260,7 @@ function getComponentsBlocks(currentModule: commonStorage.Module, hideParams : b
260260
// Add the "+ Component" category
261261
contents.push({
262262
kind: 'category',
263-
name: '+ Component',
263+
name: Blockly.Msg['MRC_CATEGORY_ADD_COMPONENT'],
264264
contents: Component.getAllPossibleComponents(hideParams)
265265
});
266266

@@ -283,7 +283,7 @@ function getComponentsBlocks(currentModule: commonStorage.Module, hideParams : b
283283

284284
return {
285285
kind: 'category',
286-
name: 'Components',
286+
name: Blockly.Msg['MRC_CATEGORY_COMPONENTS'],
287287
contents,
288288
};
289289
}

src/toolbox/lists_category.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
export const category = {
1+
import * as Blockly from 'blockly/core';
2+
3+
export const getCategory = () => ({
24
kind: 'category',
3-
name: 'Lists',
5+
name: Blockly.Msg['MRC_CATEGORY_LISTS'],
46
categorystyle: 'list_category',
57
contents: [
68
{
@@ -151,4 +153,4 @@ export const category = {
151153
type: 'lists_reverse',
152154
},
153155
],
154-
}
156+
});

src/toolbox/logic_category.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
export const category =
1+
import * as Blockly from 'blockly/core';
2+
3+
export const getCategory = () => (
24
{
35
kind: 'category',
4-
name: 'Logic',
6+
name: Blockly.Msg['MRC_CATEGORY_LOGIC'],
57
categorystyle: 'logic_category',
68
contents: [
79
{
@@ -48,4 +50,4 @@ export const category =
4850
type: 'logic_ternary',
4951
},
5052
],
51-
}
53+
});

src/toolbox/loop_category.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
export const category =
2-
{
1+
import * as Blockly from 'blockly/core';
2+
3+
export const getCategory = () => ({
34
kind: 'category',
4-
name: 'Loops',
5+
name: Blockly.Msg['MRC_CATEGORY_LOOPS'],
56
categorystyle: 'loop_category',
67
contents: [
78
{
@@ -61,4 +62,4 @@ export const category =
6162
type: 'controls_flow_statements',
6263
},
6364
],
64-
}
65+
});

0 commit comments

Comments
 (0)