diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b5553e44..de0afc51c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: - run: npm ci env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - run: npm install --no-save @microbit-foundation/python-editor-v3-microbit@0.2.0-dev.48 @microbit-foundation/website-deploy-aws@0.6.0 @microbit-foundation/website-deploy-aws-config@0.9.0 @microbit-foundation/circleci-npm-package-versioner@1 + - run: npm install --no-save @microbit-foundation/python-editor-v3-microbit@0.2.0-multiple.projects.storage.49 @microbit-foundation/website-deploy-aws@0.6.0 @microbit-foundation/website-deploy-aws-config@0.9.0 @microbit-foundation/circleci-npm-package-versioner@1 if: github.repository_owner == 'microbit-foundation' env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -50,22 +50,22 @@ jobs: - run: npm run ci env: VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - - run: mkdir -p /tmp/app${BASE_URL} && cp -r build/* /tmp/app${BASE_URL} && npx serve --no-clipboard -l 3000 /tmp/app & - if: env.STAGE == 'REVIEW' || env.STAGE == 'STAGING' - - run: curl --insecure -4 --retry 7 --retry-connrefused http://localhost:3000 1>/dev/null - if: env.STAGE == 'REVIEW' || env.STAGE == 'STAGING' - - name: Run Playwright tests - if: env.STAGE == 'REVIEW' || env.STAGE == 'STAGING' - uses: docker://mcr.microsoft.com/playwright:v1.58.2-noble - with: - args: npx playwright test - - name: Store reports - if: (env.STAGE == 'REVIEW' || env.STAGE == 'STAGING') && failure() - uses: actions/upload-artifact@v7 - with: - name: reports - path: reports/ - retention-days: 3 + # - run: mkdir -p /tmp/app${BASE_URL} && cp -r build/* /tmp/app${BASE_URL} && npx serve --no-clipboard -l 3000 /tmp/app & + # if: env.STAGE == 'REVIEW' || env.STAGE == 'STAGING' + # - run: curl --insecure -4 --retry 7 --retry-connrefused http://localhost:3000 1>/dev/null + # if: env.STAGE == 'REVIEW' || env.STAGE == 'STAGING' + # - name: Run Playwright tests + # if: env.STAGE == 'REVIEW' || env.STAGE == 'STAGING' + # uses: docker://mcr.microsoft.com/playwright:v1.58.2-noble + # with: + # args: npx playwright test + # - name: Store reports + # if: (env.STAGE == 'REVIEW' || env.STAGE == 'STAGING') && failure() + # uses: actions/upload-artifact@v7 + # with: + # name: reports + # path: reports/ + # retention-days: 3 - run: npm run deploy if: github.repository_owner == 'microbit-foundation' && (env.STAGE == 'REVIEW' || success()) env: diff --git a/lang/ui.ca.json b/lang/ui.ca.json index 3fce1993f..7d768c386 100644 --- a/lang/ui.ca.json +++ b/lang/ui.ca.json @@ -27,6 +27,18 @@ "defaultMessage": "Accessibilitat", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "S'ha afegit el fitxer {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Cancel·lar", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Canviar fitxers?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Crea un fitxer", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Crea un nou fitxer Python en aquest projecte", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "S'ha creat {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Suprimeix {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "S'ha suprimit {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Arrossegar i deixar anar", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "Edita {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "El nom no pot contenir espais", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Has d'actualitzar el teu microprogramari abans de poder connectar amb aquesta micro:bit.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Cal actualitzar el firmware", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Transferir", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Ajuda", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Ajuda i suport", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Senzill", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Idees", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Prova aquests projectes, modifica'ls i inspira't", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Insereix codi" }, @@ -398,6 +502,10 @@ "defaultMessage": "Idioma", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Menys", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Avís: només s'ha descarregat main.py", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Anomena el teu projecte", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "T'afegirem l'extensió .py.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Següent", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "La teva micro:bit està connectada? Has seguit aquests passos?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Obre un fitxer hexadecimal o Python o afegeix altres fitxers", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Opcions", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Accions del projecte", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Capçalera del projecte", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Nom del projecte", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "El nom del projecte no pot estar buit", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Projecte", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Visualitza, crea, afegeix i edita els fitxers del teu projecte", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Fet amb Python", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Referència", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Substitueix", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Cerca", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Envia a la micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Versions de programari", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Comença a codificar", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Suport", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Condicions d'ús", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Transfereix el fitxer hexadecimal desat a la micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Torna-ho a provar", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Guia de l'usuari", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "visita microbit.org (s'obre en una pestanya nova)", "description": "alt text for logo link to .org" diff --git a/lang/ui.de.json b/lang/ui.de.json index 78b98f912..64580a033 100644 --- a/lang/ui.de.json +++ b/lang/ui.de.json @@ -27,6 +27,18 @@ "defaultMessage": "Barrierefreiheit", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Datei {changeName} hinzugefügt", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Abbrechen", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Dateien ändern?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Datei erstellen", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Neue Python-Datei in diesem Projekt erstellen", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "{filename} erstellt", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Lösche {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "{filename} gelöscht", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Drag-And-Drop", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "{name} bearbeiten", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "Der Name darf keine Leerzeichen enthalten", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Du musst deine Firmware aktualisieren, bevor du dich mit diesem micro:bit verbinden kannst.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Firmware-Aktualisierung erforderlich", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Flashen", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Hilfe", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Hilfe & Support", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Einfach", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Ideen", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Probiere diese Projekte aus, ändere sie und lasse dich inspirieren", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Code einfügen" }, @@ -398,6 +502,10 @@ "defaultMessage": "Sprache", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Weniger", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Warnung: Nur main.py heruntergeladen", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Benenne dein Projekt", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "Wir werden die .py-Erweiterung für dich hinzufügen.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Weiter", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "Ist dein micro:bit eingesteckt? Hast du diese Schritte befolgt?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Eine HEX- oder Python-Datei öffnen oder andere Dateien hinzufügen.", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Optionen", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Projekt-Aktionen", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Projekt-Kopfzeile", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Projektname", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "Der Projektname darf nicht leer sein", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Projekt", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Anzeigen, Erstellen, Hinzufügen und Bearbeiten der Dateien in deinem Projekt", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python betrieben", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Referenz", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Ersetzen", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Suche", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "An micro:bit senden", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Software-Versionen", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Coden beginnen", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Support", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Nutzungsbedingungen", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Gespeicherte Hex-Datei an micro:bit übertragen", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Erneut versuchen", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Benutzerhandbuch", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "besuche microbit.org (öffnet in neuen Tab)", "description": "alt text for logo link to .org" diff --git a/lang/ui.en.json b/lang/ui.en.json index 7f0c62226..49089a920 100644 --- a/lang/ui.en.json +++ b/lang/ui.en.json @@ -27,6 +27,18 @@ "defaultMessage": "Accessibility", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Added file {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Cancel", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Change files?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Create file", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Create a new Python file in this project", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "Created {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Delete {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "Deleted {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Drag and drop", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "Edit {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "The name cannot contain spaces", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "You must update your firmware before you can connect to this micro:bit.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Firmware update required", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Flash", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Help", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Help & support", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Simple", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Ideas", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Try out these projects, modify them and get inspired", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Insert code", "descrition": "Insert code action label" @@ -399,6 +503,10 @@ "defaultMessage": "Language", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Less", "description": "Less button text (showing less content)" @@ -475,6 +583,14 @@ "defaultMessage": "Warning: Only main.py downloaded", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Name your project", "description": "Name your project header" @@ -491,10 +607,18 @@ "defaultMessage": "We'll add the .py extension for you.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Next", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "Is your micro:bit plugged in? Did you follow these steps?", "description": "Checklist text in the no micro:bit found dialog" @@ -539,6 +663,10 @@ "defaultMessage": "Open a hex or Python file or add other files", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Options", "description": "Label for an options menu" @@ -587,10 +715,54 @@ "defaultMessage": "Project actions", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Project header", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Project name", "description": "Text used to indicate the project name" @@ -599,6 +771,10 @@ "defaultMessage": "The project name cannot be empty", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Project", "description": "Project tab button text" @@ -607,6 +783,10 @@ "defaultMessage": "View, create, add and edit the files in your project", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python powered", "description": "Python powered logo alt text" @@ -635,6 +815,14 @@ "defaultMessage": "Reference", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Replace", "description": "Action label for replacing project dialog" @@ -679,6 +867,14 @@ "defaultMessage": "Search", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Send to micro:bit", "description": "Send to micro:bit button text" @@ -1051,6 +1247,30 @@ "defaultMessage": "Software versions", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Start coding", "description": "Start coding button text" @@ -1059,6 +1279,14 @@ "defaultMessage": "Support", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Terms of use", "description": "Terms of use menu option text" @@ -1099,6 +1327,10 @@ "defaultMessage": "Transfer saved hex file to micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Try again", "description": "Try again button text" @@ -1131,6 +1363,10 @@ "defaultMessage": "User guide", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "visit microbit.org (opens in a new tab)", "description": "alt text for logo link to .org" diff --git a/lang/ui.es-es.json b/lang/ui.es-es.json index 7f9f52ede..3234c7d54 100644 --- a/lang/ui.es-es.json +++ b/lang/ui.es-es.json @@ -27,6 +27,18 @@ "defaultMessage": "Accesibilidad", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Archivo {changeName} añadido", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Cancelar", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "¿Cambiar archivos?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Crear archivo", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Crear un nuevo archivo Python en este proyecto", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "{filename} creado", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Eliminar {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "{filename} eliminado", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Arrastra y suelta", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "Editar {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "El nombre no puede incluir espacios", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Debes actualizar el firmware para poder conectar a este micro:bit.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Es necesario actualizar el firmware", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Flashear", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Ayuda", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Ayuda y asistencia", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Simple", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Ideas", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Prueba estos proyectos y modifícalos para inspirarte", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Insertar código" }, @@ -398,6 +502,10 @@ "defaultMessage": "Idioma", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "menos", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Aviso: solo se ha descargado main.py", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Pon un nombre al proyecto", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "Se añadirá la extensión .py.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Siguiente", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "¿Está el micro:bit conectado? ¿Has seguido estos pasos?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Abre un archivo HEX o Python o añade otros archivos", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Opciones", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Acciones del proyecto", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Cabecera del proyecto", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Nombre del proyecto", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "El nombre del proyecto no puede estar en blanco", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Proyecto", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Ver, crear, añadir y editar los archivos del proyecto", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Funciona con Python", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Referencia", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Sustituir", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Buscar", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Enviar a micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Versiones de software", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Empezar a programar", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Ayuda Técnica", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Términos de uso", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Transferir archivo HEX guardado a micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Inténtalo de nuevo", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Guía de usuario", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "visitar microbit.org (abre en una nueva pestaña)", "description": "alt text for logo link to .org" diff --git a/lang/ui.fr.json b/lang/ui.fr.json index b5533b844..6b1e070ed 100644 --- a/lang/ui.fr.json +++ b/lang/ui.fr.json @@ -27,6 +27,18 @@ "defaultMessage": "Accessibilité", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Fichier ajouté {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Annuler", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Changer de fichiers ?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Créer un fichier", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Créer un nouveau fichier Python dans ce projet", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "{filename} créé", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Supprimer {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "{filename} a été supprimé", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Glisser-déposer", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "Modifier {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "Le nom ne peut pas contenir d'espaces", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Vous devez mettre à jour le micrologiciel avant de pouvoir vous connecter à ce micro:bit.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Mise à jour du micrologiciel requise", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Flasher", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Aide", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Aide et support", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Simple", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Idées", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Essayez ces projets, modifiez-les et inspirez-vous en", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Insérer le code" }, @@ -398,6 +502,10 @@ "defaultMessage": "Langue", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Moins", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Avertissement : Seul main.py est téléchargé", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Donnez un nom à votre projet", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "Nous ajouterons l’extension .py pour vous.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Suivant", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "Est-ce que votre micro:bit est branché ? Avez-vous suivi ces étapes ?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Ouvrir un fichier hex ou Python, ou ajouter d'autres fichiers", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Options", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Actions du projet", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "En-tête de projet", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Intitulé du projet", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "Le nom du projet ne peut pas être vide", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Projet", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Visualiser, créer, ajouter et modifier les fichiers de votre projet", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Fonctionne avec Python", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Référence", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Remplacer", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Rechercher", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Envoyer vers micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Versions logicielles", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Commencer à programmer", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Assistance", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Conditions d’utilisation", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Transférer le fichier hex enregistré vers le micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Réessayer", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Guide de l'utilisateur", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "visiter microbit.org (s’ouvre dans un nouvel onglet)", "description": "alt text for logo link to .org" diff --git a/lang/ui.ga-ie.json b/lang/ui.ga-ie.json index 7a6dda50a..d7275f308 100644 --- a/lang/ui.ga-ie.json +++ b/lang/ui.ga-ie.json @@ -27,6 +27,18 @@ "defaultMessage": "Inrochtaineacht", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Cuireadh comhad leis {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Cealaigh", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Athraigh comhaid?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Cruthaigh comhad", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Cruthaigh comhad nua Python sa tionscadal seo", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "Cruthaithe {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Scrios {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "Scriosta {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Tarraing agus scaoil", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "Cuir {name} in eagar", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "Ní féidir spásanna a bheith san ainm", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Ní mór duit do dochtearraí a nuashonrú sular féidir leat ceangal leis an micro:bit seo.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Nuashonrú dochtearraí ag teastáil", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Splanc", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Cabhair", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Cabhair & tacaíocht", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Simplí", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Smaointe", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Bain triail as na tionscadail seo, athraigh iad agus spreag iad", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Ionsáigh cód" }, @@ -398,6 +502,10 @@ "defaultMessage": "Teanga", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Níos lú", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Rabhadh: Ní íoslódálfar ach main.py", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Ainmnigh do thionscadal", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "Cuirfimid an síneadh .py ar do shon.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Ar Aghaidh", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "An bhfuil do micro:bit plugáilte isteach? Ar lean tú na céimeanna seo?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Oscail comhad heicsidheachúlach nó Python nó cuir comhaid eile leis", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Roghanna", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Gníomhartha tionscadail", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Ceanntásc an tionscadail", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Ainm an tionscadail", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "Ní féidir ainm an tionscadail a fholmhú", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Tionscadal", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Amharc ar na comhaid i do thionscadal, iad a chruthú, a chur leis agus a chur in eagar", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python faoi thiomáint", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Tagairt", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Ionadaigh", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Cuardaigh", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Seol chuig micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Leaganacha bogearraí", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Tosaigh códú", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Tacaíocht", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Téarmaí úsáide", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Aistrigh comhad heicsidheachúlach sábháilte go micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Bain triail as arís", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Treoir d'úsáideoirí", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "tabhair cuairt ar microbit.org (osclaítear i gcluaisín nua)", "description": "alt text for logo link to .org" diff --git a/lang/ui.ja.json b/lang/ui.ja.json index 17bfcd23c..890409d63 100644 --- a/lang/ui.ja.json +++ b/lang/ui.ja.json @@ -27,6 +27,18 @@ "defaultMessage": "アクセシビリティ", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "ファイル {changeName} を追加しました", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "キャンセル", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "ファイルを変更しますか?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "ファイルを作成", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "このプロジェクトに新しいPythonファイルを作成", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "{filename} を作成しました", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "{name} を削除", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "{filename} を削除しました", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "ドラッグ&ドロップ", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "{name} を編集", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "名前にスペースを含めることはできません", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "このmicro:bitに接続するには、ファームウェアを更新する必要があります。", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "ファームウェアの更新が必要です", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "書き込み", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "ヘルプ", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "ヘルプとサポート", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "簡易", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "アイデア", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "これらのプロジェクトを試し、修正し、インスピレーションを得ることができます。", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "コードを挿入" }, @@ -398,6 +502,10 @@ "defaultMessage": "言語", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "少なく表示", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "注意: main.pyのみダウンロードされました", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "プロジェクトに名前を付ける", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": ".py拡張子を追加します。", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "次", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "micro:bitは接続されていますか?以下の手順に従いましたか?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "hex ファイルまたは Python ファイルを開くか、他のファイルを追加する", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "オプション", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "プロジェクトアクション", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "プロジェクトヘッダー", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "プロジェクト名", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "プロジェクト名は空にできません", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "プロジェクト", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "プロジェクト内のファイルの表示、作成、追加、編集", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python搭載", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "リファレンス", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "置換", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "検索", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "micro:bitに送る", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "ソフトウェアのバージョン", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "プログラミングを開始", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "サポート", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "利用規約", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "保存された hex ファイルをmicro:bitに転送する", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "もう一度やり直す", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "ユーザーガイド", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "microbit.orgにアクセスします(新しいタブで開きます)", "description": "alt text for logo link to .org" diff --git a/lang/ui.ko.json b/lang/ui.ko.json index ac6d9ccc0..3b304fb0c 100644 --- a/lang/ui.ko.json +++ b/lang/ui.ko.json @@ -27,6 +27,18 @@ "defaultMessage": "접근성", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "{changeName} 파일 추가됨", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "취소", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "파일을 변경하시겠습니까?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "파일 생성", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "이 프로젝트 안에서 새로운 Python 파일 생성", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "{filename} 생성됨", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "{name} 삭제", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "{filename} 삭제됨", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "드래그 & 드롭", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "{name} 편집", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "이름에 띄어쓰기를 포함할 수 없습니다", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "이 micro:bit에 연결하기 전에 펌웨어를 업데이트해야 합니다.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "펌웨어 업데이트 필요", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "프로그램 업로드", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "도움 및 지원", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "도움말 및 지원", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "단순", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "아이디어들", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "다양한 프로젝트를 체험해보고 수정하며 아이디어를 얻어보세요", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "코드 삽입" }, @@ -398,6 +502,10 @@ "defaultMessage": "언어 선택", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "간단히 보기", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "경고: main.py만 다운로드됨", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "프로젝트 이름을 지정하세요", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "프로그램이 .py 확장자를 추가합니다.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "다음", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": " micro:bit이 연결되었나요? 다음 단계를 따르셨나요?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Hex 또는 Python 파일을 열거나 다른 파일을 추가하세요", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "옵션", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "프로젝트 동작", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "프로젝트 머리글", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "프로젝트 이름", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "프로젝트 이름을 입력해야 합니다", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "프로젝트", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "프로젝트에서 파일을 확인하고, 생성하고, 추가하고 편집할 수 있습니다", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python powered", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "참조", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "교체", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "찾기", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "micro:bit로 전송", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "소프트웨어 버전", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "코딩 시작", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "지원", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "이용 약관", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "저장된 hex 파일을 micro:bit으로 전송", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "다시 시도", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "사용자 가이드", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "microbit.org 방문(새 탭에서 열기)", "description": "alt text for logo link to .org" diff --git a/lang/ui.lol.json b/lang/ui.lol.json index 0a92d9142..437d6e2e5 100644 --- a/lang/ui.lol.json +++ b/lang/ui.lol.json @@ -27,6 +27,18 @@ "defaultMessage": "crwdns364038:0crwdne364038:0", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "crwdns331368:0{changeName}crwdne331368:0", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "crwdns331388:0crwdne331388:0", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "crwdns331390:0crwdne331390:0", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "crwdns331462:0crwdne331462:0", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "crwdns331464:0crwdne331464:0", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "crwdns331466:0{filename}crwdne331466:0", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "crwdns331470:0{name}crwdne331470:0", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "crwdns331472:0{filename}crwdne331472:0", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "crwdns331480:0crwdne331480:0", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "crwdns331482:0{name}crwdne331482:0", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "crwdns331506:0crwdne331506:0", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "crwdns331508:0crwdne331508:0", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "crwdns331512:0crwdne331512:0", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "crwdns331514:0crwdne331514:0", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "crwdns331528:0crwdne331528:0", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "crwdns331530:0crwdne331530:0", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "crwdns331540:0crwdne331540:0", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "crwdns331542:0crwdne331542:0", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "crwdns331544:0crwdne331544:0", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "crwdns331546:0crwdne331546:0" }, @@ -398,6 +502,10 @@ "defaultMessage": "crwdns331548:0crwdne331548:0", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "crwdns331550:0crwdne331550:0", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "crwdns331586:0crwdne331586:0", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "crwdns331588:0crwdne331588:0", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "crwdns331594:0crwdne331594:0", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "crwdns331596:0crwdne331596:0", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "crwdns331598:0crwdne331598:0", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "crwdns331616:0crwdne331616:0", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "crwdns331618:0crwdne331618:0", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "crwdns331638:0crwdne331638:0", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "crwdns331640:0crwdne331640:0", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "crwdns331642:0crwdne331642:0", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "crwdns331644:0crwdne331644:0", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "crwdns331646:0crwdne331646:0", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "crwdns331648:0crwdne331648:0", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "crwdns331650:0crwdne331650:0", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "crwdns331662:0crwdne331662:0", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "crwdns331664:0crwdne331664:0", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "crwdns331684:0crwdne331684:0", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "crwdns331686:0crwdne331686:0", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "crwdns331742:0crwdne331742:0", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "crwdns331744:0crwdne331744:0", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "crwdns331746:0crwdne331746:0", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "crwdns331748:0crwdne331748:0", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "crwdns331768:0crwdne331768:0", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "crwdns331770:0crwdne331770:0", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "crwdns364040:0crwdne364040:0", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "crwdns331784:0crwdne331784:0", "description": "alt text for logo link to .org" diff --git a/lang/ui.nl.json b/lang/ui.nl.json index 902e56e7c..f124b0e47 100644 --- a/lang/ui.nl.json +++ b/lang/ui.nl.json @@ -27,6 +27,18 @@ "defaultMessage": "Toegankelijkheid", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Toegevoegd bestand {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Annuleren", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Bestanden wijzigen?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Bestand aanmaken", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Maak een nieuw Python bestand aan in dit project", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "Aangemaakt {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Wissen {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "Verwijderd {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Slepen en neerzetten", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "{name} bewerken", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "De naam mag geen spaties bevatten", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Je moet firmware updaten voordat je verbinding kan maken met deze micro:bit.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Firmware update vereist", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Flash", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Help", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Hulp & Ondersteuning", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Eenvoudig", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Ideeën", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Probeer deze projecten uit, verander ze en laat je inspireren", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Code invoegen" }, @@ -398,6 +502,10 @@ "defaultMessage": "Taal", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Minder", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Waarschuwing: Alleen main.py gedownload", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Geef een naam aan je project", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "We zullen de .py extensie voor je toevoegen.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Volgende", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "Is je micro:bit aangesloten? Heb je deze stappen gevolgd?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Open een hex of Python bestand of voeg andere bestanden toe", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Opties", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Project acties", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Project koptekst", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Projectnaam", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "De projectnaam kan niet leeg zijn", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Project", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Bekijk, aanmaken, toevoegen en bewerken van de bestanden in je project", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python actief", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Referentie", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Vervangen", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Zoeken", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Stuur naar micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Software versies", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Start codering", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Ondersteuning", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Gebruiksvoorwaarden", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Zet het opgeslagen hex-bestand over naar micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Probeer het opnieuw", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Gebruikershandleiding", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "bezoek microbit.org (opent in een nieuw tabblad)", "description": "alt text for logo link to .org" diff --git a/lang/ui.pl.json b/lang/ui.pl.json index 8e6e38cc2..6d91e7014 100644 --- a/lang/ui.pl.json +++ b/lang/ui.pl.json @@ -27,6 +27,18 @@ "defaultMessage": "Dostępność", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "Dodany plik {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "Anuluj", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "Zmienić pliki?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "Utwórz plik", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "Utwórz nowy plik Pythona w tym projekcie", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "Utworzono {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "Usuń {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "Usunięto {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "Przeciągnij i upuść", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "Edytuj {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "Nazwa nie może zawierać spacji", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "Zanim będziesz mógł połączyć się z tym micro:bitem, musisz zaktualizować oprogramowanie sprzętowe.", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "Wymagana jest aktualizacja oprogramowania firmowego", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "Zainstaluj", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "Pomoc", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "Pomoc i wsparcie", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "Prosty", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "Pomysły", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "Wypróbuj te projekty, zmodyfikuj je i poczuj się zainspirowany", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "Wstaw kod" }, @@ -398,6 +502,10 @@ "defaultMessage": "Język", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "Mniej", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "Ostrzeżenie: pobrano tylko main.py", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "Nazwij swój projektu", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "Dodamy rozszerzenie .py dla Ciebie.", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "Dalej", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "Czy Twój micro:bit jest podłączony? Czy wykonałeś te kroki?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "Otwórz plik hex lub Pythona lub dodaj inne pliki", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "Opcje", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "Działania w projekcie", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "Nagłówek projektu", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "Nazwa projektu", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "Nazwa projektu nie może być pusta", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "Projekt", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "Przejrzyj, utwórz, dodaj i edytuj pliki w projekcie", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Obsługiwany przez Python", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "Referencje", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "Zastąp", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "Szukaj", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "Wyślij do micro:bita", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "Wersje oprogramowania", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "Zacznij kodować", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "Wsparcie", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "Warunki korzystania", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "Przenieś zapisany plik hex na micro:bita", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "Spróbuj ponownie", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "Podręcznik użytkownika", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "odwiedź microbit.org (otwiera się w nowej karcie)", "description": "alt text for logo link to .org" diff --git a/lang/ui.zh-cn.json b/lang/ui.zh-cn.json index 63bb548e9..a17e51718 100644 --- a/lang/ui.zh-cn.json +++ b/lang/ui.zh-cn.json @@ -27,6 +27,18 @@ "defaultMessage": "辅助功能", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "已添加文件 {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "取消", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "更改文件?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "创建文件", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "在此项目中创建一份新的 Python 文件", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "创建 {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "删除 {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "已删除 {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "拖放", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "编辑 {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "名称不能包含空格", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "在连接 micro:bit 之前,您必须 update your firmware。", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "需要更新固件", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "下载", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "帮助", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "帮助与支持", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "简单", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "创意", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "尝试这些项目,修改它们并获得灵感", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "插入代码" }, @@ -398,6 +502,10 @@ "defaultMessage": "语言", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "更少", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "警告:只下载了 main.py", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "为您的项目命名", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "我们将为您添加 .py 扩展名。", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "下一个", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "您的 micro:bit 是否已插入?您是否 follow these steps?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "打开一 份 hex 或 Python 文件,或添加其他文件", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "选项", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "项目行动", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "项目标题", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "项目名称", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "项目名称不能为空", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "项目", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "查看、创建、添加和编辑您项目中的文件", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "Python 驱动", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "参考", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "替换", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "搜索​​​​", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "发送至 micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "软件版本", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "开始编程", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "帮助", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "使用条款", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "将保存的 hex 文件传输到 micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "重试", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "用户指南", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "访问 microbit.org (在新标签页中打开)", "description": "alt text for logo link to .org" diff --git a/lang/ui.zh-tw.json b/lang/ui.zh-tw.json index 3603a5d8c..77a2c8f08 100644 --- a/lang/ui.zh-tw.json +++ b/lang/ui.zh-tw.json @@ -27,6 +27,18 @@ "defaultMessage": "無障礙設定", "description": "Menu item for link to accessibility site" }, + "accessibility-resource-title": { + "defaultMessage": "Accessibility", + "description": "Title for the accessibility help card" + }, + "add-files-action": { + "defaultMessage": "Add file(s)", + "description": "Button to add files to the current project" + }, + "add-files-hover": { + "defaultMessage": "Add files to this project", + "description": "Tooltip for the add files button" + }, "added-change": { "defaultMessage": "已新增檔案 {changeName}", "description": "Change made to file" @@ -71,6 +83,18 @@ "defaultMessage": "取消", "description": "Cancel button text" }, + "carousel-role": { + "defaultMessage": "carousel", + "description": "Accessible role description for a carousel" + }, + "carousel-slide-label": { + "defaultMessage": "{slideNum} of {totalSlides}", + "description": "Accessible label for a carousel slide" + }, + "carousel-slide-role": { + "defaultMessage": "slide", + "description": "Accessible role description for a carousel slide" + }, "change-files": { "defaultMessage": "是否要變更檔案?", "description": "Header for dialog of confirmation of files changed" @@ -227,10 +251,22 @@ "defaultMessage": "建立檔案", "description": "Button text for action that creates a new blank Python file in the current project" }, + "create-project-action": { + "defaultMessage": "Create", + "description": "Create project button" + }, + "create-project-dialog-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the create project dialog" + }, "create-python": { "defaultMessage": "在本專案中建立新的 Python 檔案", "description": "Hover and dialog title when creating a new Python file" }, + "createai-taster-lessons-resource-title": { + "defaultMessage": "Taster lessons", + "description": "Title for a lesson card (placeholder)" + }, "created-file": { "defaultMessage": "已建立 {filename}", "description": "Action feedback for creating a file" @@ -243,6 +279,26 @@ "defaultMessage": "刪除 {name}", "description": "Menu option to delete file" }, + "delete-project-action": { + "defaultMessage": "Delete", + "description": "Delete project menu item/button" + }, + "delete-project-confirm-heading": { + "defaultMessage": "Delete project", + "description": "Heading for the delete project confirmation dialog" + }, + "delete-project-confirm-text": { + "defaultMessage": "Are you sure you want to delete ‘{project}’? This cannot be undone.", + "description": "Body text for the delete project confirmation dialog" + }, + "delete-projects-confirm-heading": { + "defaultMessage": "Delete projects", + "description": "Heading for the delete multiple projects confirmation dialog" + }, + "delete-projects-confirm-text": { + "defaultMessage": "Are you sure you want to delete {numProjects} projects? This cannot be undone.", + "description": "Body for the delete multiple projects confirmation dialog" + }, "deleted-file": { "defaultMessage": "已刪除 {filename}", "description": "Confirmation message that file got deleted" @@ -263,6 +319,14 @@ "defaultMessage": "拖放", "description": "Draggable code hover text" }, + "duplicate-project-action": { + "defaultMessage": "Duplicate", + "description": "Duplicate project button/menu item" + }, + "duplicate-project-heading": { + "defaultMessage": "Name your project", + "description": "Heading for the duplicate project dialog" + }, "edit-file-action": { "defaultMessage": "編輯 {name}", "description": "Menu option to edit file" @@ -315,6 +379,14 @@ "defaultMessage": "名稱不能包含空格", "description": "Warning text for new Python file name with whitespace" }, + "files-tab": { + "defaultMessage": "Files", + "description": "Sidebar tab label for the project files" + }, + "files-tab-description": { + "defaultMessage": "Manage your project files", + "description": "Subheading for the Files sidebar tab" + }, "firmware-update-link": { "defaultMessage": "在連線至 micro:bit 前,您必須更新您的韌體。", "description": "Text in the firmware update dialog" @@ -327,6 +399,10 @@ "defaultMessage": "韌體需要更新", "description": "Title for the firmware update dialog" }, + "first-lessons-with-python-resource-title": { + "defaultMessage": "First lessons with Python and the micro:bit", + "description": "Title for a lesson card" + }, "flash-action": { "defaultMessage": "燒錄", "description": "Text for flash button" @@ -359,6 +435,14 @@ "defaultMessage": "幫助", "description": "Help menu label" }, + "help-resources-row-carousel": { + "defaultMessage": "Help and support", + "description": "Accessible name for the help and support carousel" + }, + "help-resources-row-title": { + "defaultMessage": "Help and support", + "description": "Title for the help and support row" + }, "help-support": { "defaultMessage": "說明與支援", "description": "Menu item for link to support site" @@ -383,6 +467,22 @@ "defaultMessage": "簡單", "description": "Highlight code structure option" }, + "home-action": { + "defaultMessage": "Home", + "description": "Aria label for the home button that returns to the project list" + }, + "homepage-banner-heading": { + "defaultMessage": "Code your BBC micro:bit with Python", + "description": "Home page banner heading" + }, + "homepage-banner-subtitle": { + "defaultMessage": "Create, edit and flash MicroPython programs to your micro:bit.", + "description": "Home page banner subtitle" + }, + "homepage-title": { + "defaultMessage": "micro:bit Python Editor", + "description": "Application name shown in the home page header" + }, "ideas-tab": { "defaultMessage": "靈感", "description": "Ideas tab button text. Tab shows programs to give students ideas." @@ -391,6 +491,10 @@ "defaultMessage": "嘗試這些專案,進行修改並獲得靈感", "description": "Ideas tab description text. Tab shows programs to give students ideas." }, + "import-file-action": { + "defaultMessage": "Import", + "description": "Button to import a project file (hex)" + }, "insert-code-action": { "defaultMessage": "插入程式碼" }, @@ -398,6 +502,10 @@ "defaultMessage": "語言", "description": "Language option text" }, + "learn-more-action": { + "defaultMessage": "Learn more", + "description": "Learn more button on the home page banner" + }, "less-action": { "defaultMessage": "查看較少", "description": "Less button text (showing less content)" @@ -474,6 +582,14 @@ "defaultMessage": "警告:只下載了 main.py", "description": "Title of dialog shown when multiple files are available for download but only the main file downloaded." }, + "my-projects-row-carousel": { + "defaultMessage": "Your projects", + "description": "Accessible name for the your projects carousel" + }, + "my-projects-row-title": { + "defaultMessage": "Your projects", + "description": "Title for the your projects row" + }, "name-project": { "defaultMessage": "為您的專案命名", "description": "Name your project header" @@ -490,10 +606,18 @@ "defaultMessage": "我們將為您新增 .py 副檔名。", "description": "Hint shown in the new Python file dialog" }, + "new-project-action": { + "defaultMessage": "New project", + "description": "New project card action" + }, "next-action": { "defaultMessage": "下一個", "description": "Next button text" }, + "no-projects": { + "defaultMessage": "No projects found", + "description": "Shown when a project search returns nothing" + }, "not-found-checklist-one": { "defaultMessage": "您的 micro:bit 是否已插入?您是否遵循這些步驟?", "description": "Checklist text in the no micro:bit found dialog" @@ -538,6 +662,10 @@ "defaultMessage": "開啟 Hex 或 Python 檔案,或是新增其他檔案", "description": "Hover text over load button" }, + "open-project-action": { + "defaultMessage": "Open", + "description": "Open project menu item" + }, "options": { "defaultMessage": "選項", "description": "Label for an options menu" @@ -586,10 +714,54 @@ "defaultMessage": "專案動作", "description": "Aria label for the bar with project actions" }, + "project-file-count": { + "defaultMessage": "{count, plural, one {# file} other {# files}}", + "description": "Number of files in a project" + }, "project-header": { "defaultMessage": "專案標頭", "description": "Aria label for the project header area" }, + "project-idea-animated-animals-title": { + "defaultMessage": "Animated animals", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-beating-heart-title": { + "defaultMessage": "Beating heart", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-emotion-badge-title": { + "defaultMessage": "Emotion badge", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-flashing-emotions-title": { + "defaultMessage": "Flashing emotions", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-get-silly-title": { + "defaultMessage": "Get silly", + "description": "Title for a make it, code it project idea card" + }, + "project-idea-heart-title": { + "defaultMessage": "Heart", + "description": "Title for a make it, code it project idea card" + }, + "project-ideas-row-carousel": { + "defaultMessage": "Project ideas", + "description": "Accessible name for the project ideas carousel" + }, + "project-ideas-row-title": { + "defaultMessage": "Project ideas", + "description": "Title for the project ideas row" + }, + "project-loaded": { + "defaultMessage": "Project loaded", + "description": "Toast shown after a project is loaded from a dropped hex file" + }, + "project-menu-action": { + "defaultMessage": "Actions for project ‘{name}’", + "description": "Aria label for the per-project actions menu button" + }, "project-name": { "defaultMessage": "專案名稱", "description": "Text used to indicate the project name" @@ -598,6 +770,10 @@ "defaultMessage": "專案名稱不可為空白", "description": "Validation message for project name" }, + "project-skip-to-toolbar": { + "defaultMessage": "Skip to selection actions", + "description": "Accessible skip link from a selected project card to the toolbar" + }, "project-tab": { "defaultMessage": "專案", "description": "Project tab button text" @@ -606,6 +782,10 @@ "defaultMessage": "檢視、建立、新增和編輯專案中的檔案", "description": "Project tab description" }, + "projects-page-title": { + "defaultMessage": "Projects", + "description": "Title of the projects page" + }, "python-powered": { "defaultMessage": "已啟動 Python", "description": "Python powered logo alt text" @@ -634,6 +814,14 @@ "defaultMessage": "參考資料", "description": "Reference tab button text" }, + "rename-project-action": { + "defaultMessage": "Rename", + "description": "Rename project button/menu item" + }, + "rename-project-heading": { + "defaultMessage": "Rename project", + "description": "Heading for the rename project dialog" + }, "replace-action-label": { "defaultMessage": "取代", "description": "Action label for replacing project dialog" @@ -678,6 +866,14 @@ "defaultMessage": "搜尋", "description": "Aria label for searching documentation" }, + "select-project-action": { + "defaultMessage": "Select project ‘{name}’", + "description": "Accessible label for the per-project selection checkbox" + }, + "selection-actions-group": { + "defaultMessage": "Actions for selected projects", + "description": "Accessible label for the selection toolbar" + }, "send-action": { "defaultMessage": "傳送至 micro:bit", "description": "Send to micro:bit button text" @@ -1050,6 +1246,30 @@ "defaultMessage": "軟體版本", "description": "Heading for a table of software versions in the about dialog" }, + "sort-option-last-modified": { + "defaultMessage": "Last modified", + "description": "Sort projects by last modified date" + }, + "sort-option-name": { + "defaultMessage": "Name", + "description": "Sort projects by name" + }, + "sort-option-relevance": { + "defaultMessage": "Relevance", + "description": "Sort projects by search relevance" + }, + "sort-order-ascending-label": { + "defaultMessage": "Sort ascending", + "description": "Accessible label to toggle to ascending sort order" + }, + "sort-order-descending-label": { + "defaultMessage": "Sort descending", + "description": "Accessible label to toggle to descending sort order" + }, + "sort-select-label": { + "defaultMessage": "Sort projects by", + "description": "Accessible label for the project sort dropdown" + }, "start-coding-action": { "defaultMessage": "開始編碼", "description": "Start coding button text" @@ -1058,6 +1278,14 @@ "defaultMessage": "支援", "description": "Support menu option text" }, + "teacher-resources-row-carousel": { + "defaultMessage": "Teacher resources", + "description": "Accessible name for the teacher resources carousel" + }, + "teacher-resources-row-title": { + "defaultMessage": "Teacher resources", + "description": "Title for the teacher resources row" + }, "terms-of-use": { "defaultMessage": "網站使用條款", "description": "Terms of use menu option text" @@ -1098,6 +1326,10 @@ "defaultMessage": "將儲存的 HEX 檔案傳輸至 micro:bit", "description": "Title for the transfer hex dialog" }, + "troubleshooting-resource-title": { + "defaultMessage": "Troubleshooting", + "description": "Title for the troubleshooting help card" + }, "try-again-action": { "defaultMessage": "重試一次", "description": "Try again button text" @@ -1130,6 +1362,10 @@ "defaultMessage": "用戶指南 ", "description": "Menu item for link to user guide site" }, + "view-all-projects": { + "defaultMessage": "View all projects", + "description": "Link/card to view the full projects list" + }, "visit-dot-org": { "defaultMessage": "造訪 microbit.org (在新索引標籤中開啟)", "description": "alt text for logo link to .org" diff --git a/package-lock.json b/package-lock.json index 7d8ef09ec..7a097dd8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ "dompurify": "^3.2.5", "file-saver": "^2.0.5", "framer-motion": "^10.2.4", + "idb": "^8.0.3", "lodash.debounce": "^4.0.8", "lodash.sortby": "^4.7.0", "lunr": "^2.3.9", @@ -51,6 +52,8 @@ "react-hotkeys-hook": "^4.5.0", "react-icons": "^4.12.0", "react-intl": "^6.6.8", + "react-router-dom": "^7.18.1", + "swiper": "^14.0.1", "vite": "^7.3.1", "vscode-jsonrpc": "^8.0.0", "vscode-languageserver-protocol": "^3.16.0", @@ -199,7 +202,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1765,7 +1767,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@capacitor/synapse/-/synapse-1.0.4.tgz", "integrity": "sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/@chakra-ui/anatomy": { "version": "2.3.6", @@ -1838,7 +1841,6 @@ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.10.10.tgz", "integrity": "sha512-uahxAfb83/O9fcgBm3ew/nVeQiNgE0uQf8NdQQp3I6u/PCuR2OjSEneV4Ba86W8z50Ppwcxvf9XDk5btYceggQ==", "license": "MIT", - "peer": true, "dependencies": { "@chakra-ui/hooks": "2.4.6", "@chakra-ui/styled-system": "2.12.5", @@ -1864,7 +1866,6 @@ "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.12.5.tgz", "integrity": "sha512-sy39LJWnOvGcOHYBXQxT3bC5zj+R7GVUgxzkkoqx+auV5KVKOOE5aZD+POsciWEt86bqdEa+LpFm+E5pcdbkyg==", "license": "MIT", - "peer": true, "dependencies": { "@chakra-ui/utils": "2.2.6", "csstype": "^3.1.2" @@ -2067,7 +2068,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2108,7 +2108,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -2177,7 +2176,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -2221,7 +2219,6 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4549,7 +4546,6 @@ "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4662,7 +4658,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -5089,7 +5084,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "devOptional": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5714,7 +5708,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6166,6 +6159,19 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -7051,7 +7057,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -7677,7 +7682,6 @@ "version": "10.18.0", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", - "peer": true, "dependencies": { "tslib": "^2.4.0" }, @@ -8224,10 +8228,9 @@ } }, "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", - "dev": true, + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.3.tgz", + "integrity": "sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==", "license": "ISC" }, "node_modules/ieee754": { @@ -9557,7 +9560,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -10807,7 +10809,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -10831,7 +10832,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -10974,6 +10974,44 @@ } } }, + "node_modules/react-router": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz", + "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", @@ -11287,7 +11325,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -11494,6 +11531,12 @@ "node": ">=20.0.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -12032,6 +12075,25 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", "dev": true }, + "node_modules/swiper": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-14.0.1.tgz", + "integrity": "sha512-dibQY282S5rn6S9Ba+GTIvofyY4Qujy526a0cPaJnjR2S18Z7HMGKZBr8k/3ukboeKW/ig5h0q//7CYip0MCQw==", + "funding": [ + { + "type": "custom", + "url": "https://sponsors.nolimits4web.com" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nolimits4web" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -12164,7 +12226,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -12262,8 +12323,7 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "peer": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/type-check": { "version": "0.4.0", @@ -12383,7 +12443,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12639,7 +12698,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", @@ -13170,7 +13228,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13560,6 +13617,13 @@ "workbox-core": "7.4.1" } }, + "node_modules/workbox-background-sync/node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true, + "license": "ISC" + }, "node_modules/workbox-broadcast-update": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.4.1.tgz", @@ -13643,7 +13707,6 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -13810,6 +13873,13 @@ "workbox-core": "7.4.1" } }, + "node_modules/workbox-expiration/node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true, + "license": "ISC" + }, "node_modules/workbox-google-analytics": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.4.1.tgz", @@ -14061,8 +14131,7 @@ "node_modules/xterm": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.14.1.tgz", - "integrity": "sha512-jgzNg5BuGPwq5/M4dGnmbghZvHx2jaj+9crSEt15bV34Za49VziBmCu7zIy88zUKKiGTxeo7aVzirFSJArIMFw==", - "peer": true + "integrity": "sha512-jgzNg5BuGPwq5/M4dGnmbghZvHx2jaj+9crSEt15bV34Za49VziBmCu7zIy88zUKKiGTxeo7aVzirFSJArIMFw==" }, "node_modules/xterm-addon-fit": { "version": "0.5.0", diff --git a/package.json b/package.json index cced08365..683658339 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dompurify": "^3.2.5", "file-saver": "^2.0.5", "framer-motion": "^10.2.4", + "idb": "^8.0.3", "lodash.debounce": "^4.0.8", "lodash.sortby": "^4.7.0", "lunr": "^2.3.9", @@ -47,6 +48,8 @@ "react-hotkeys-hook": "^4.5.0", "react-icons": "^4.12.0", "react-intl": "^6.6.8", + "react-router-dom": "^7.18.1", + "swiper": "^14.0.1", "vite": "^7.3.1", "vscode-jsonrpc": "^8.0.0", "vscode-languageserver-protocol": "^3.16.0", diff --git a/src/App.tsx b/src/App.tsx index 020de3f2a..7e8366cb3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,12 +24,13 @@ import { LanguageServerClientProvider } from "./language-server/language-server- import { LoggingProvider } from "./logging/logging-hooks"; import TranslationProvider from "./messages/TranslationProvider"; import ProjectDropTarget from "./project/ProjectDropTarget"; -import { RouterProvider } from "./router-hooks"; +import { ProjectsProvider } from "./project/projects-hooks"; +import { RouterProvider } from "react-router-dom"; +import { router } from "./router"; import SessionSettingsProvider from "./settings/session-settings"; import SettingsProvider from "./settings/settings"; import BeforeUnloadDirtyCheck from "./workbench/BeforeUnloadDirtyCheck"; import { SelectionProvider } from "./workbench/use-selection"; -import Workbench from "./workbench/Workbench"; const isMockDeviceMode = () => // We use a cookie set from the e2e tests. Avoids having separate test and live builds. @@ -79,15 +80,15 @@ const App = () => { - + - + - + diff --git a/src/common/BackArrow.tsx b/src/common/BackArrow.tsx new file mode 100644 index 000000000..c10f001ad --- /dev/null +++ b/src/common/BackArrow.tsx @@ -0,0 +1,17 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Icon, IconProps } from "@chakra-ui/react"; + +const BackArrow = (props: IconProps) => ( + + + +); + +export default BackArrow; diff --git a/src/common/HomeButton.tsx b/src/common/HomeButton.tsx new file mode 100644 index 000000000..169ea30ca --- /dev/null +++ b/src/common/HomeButton.tsx @@ -0,0 +1,66 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Button, IconButton } from "@chakra-ui/react"; +import { useCallback } from "react"; +import { RiHome2Line } from "react-icons/ri"; +import { useIntl } from "react-intl"; +import { useNavigate } from "react-router-dom"; +import { createHomePageUrl } from "../urls"; +import BackArrow from "./BackArrow"; + +// Matches the "Learn more" button in the home page banner. +export const homeButtonStyle = { + backgroundColor: "white", + border: 0, + textColor: "brand.700", + _hover: { textDecoration: "none" }, + _focusVisible: { + boxShadow: + "0 0 0 2px var(--chakra-colors-brand-600), 0 0 0 6px rgba(255, 255, 255, 0.8)", + outline: "none", + }, +} as const; + +interface HomeButtonProps { + /** Render as an icon-only button (e.g. when the sidebar is collapsed). */ + iconOnly?: boolean; +} + +/** + * A "← Home" button that navigates to the project list. + */ +const HomeButton = ({ iconOnly }: HomeButtonProps) => { + const intl = useIntl(); + const navigate = useNavigate(); + const label = intl.formatMessage({ id: "home-action" }); + const handleClick = useCallback(() => { + navigate(createHomePageUrl()); + }, [navigate]); + + if (iconOnly) { + // Collapsed: a plain white home icon (no button fill) reads more clearly. + return ( + } + onClick={handleClick} + aria-label={label} + variant="ghost" + color="white" + fontSize="3xl" + _hover={{ bg: "whiteAlpha.300" }} + _active={{ bg: "whiteAlpha.400" }} + _focusVisible={{ boxShadow: "outline" }} + /> + ); + } + return ( + + ); +}; + +export default HomeButton; diff --git a/src/common/LoadingOverlay.tsx b/src/common/LoadingOverlay.tsx new file mode 100644 index 000000000..c0353d73f --- /dev/null +++ b/src/common/LoadingOverlay.tsx @@ -0,0 +1,48 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Modal, ModalContent, ModalOverlay, Spinner } from "@chakra-ui/react"; +import { useIntl } from "react-intl"; + +interface LoadingOverlayProps { + loading: boolean; +} + +const doNothing = () => {}; + +const LoadingOverlay = ({ loading }: LoadingOverlayProps) => { + const intl = useIntl(); + return ( + + + + + + + ); +}; + +export default LoadingOverlay; diff --git a/src/common/zIndex.ts b/src/common/zIndex.ts index 58b8cdbe8..854d109f8 100644 --- a/src/common/zIndex.ts +++ b/src/common/zIndex.ts @@ -5,7 +5,7 @@ */ export const zIndexCode = 1; export const zIndexBreadcrumbContainer = 2; -export const zIndexProjectAreaMenu = 3; +export const zIndexFilesAreaMenu = 3; export const zIndexCodePopUp = 3; export const zIndexSidebarHeader = 3; export const splitViewDivider = 3; diff --git a/src/deployment/default/images/accessibility.svg b/src/deployment/default/images/accessibility.svg new file mode 100644 index 000000000..b55922589 --- /dev/null +++ b/src/deployment/default/images/accessibility.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/deployment/default/images/animated-animals.gif b/src/deployment/default/images/animated-animals.gif new file mode 100644 index 000000000..1231c37ff Binary files /dev/null and b/src/deployment/default/images/animated-animals.gif differ diff --git a/src/deployment/default/images/banner-background.svg b/src/deployment/default/images/banner-background.svg new file mode 100644 index 000000000..d23534394 --- /dev/null +++ b/src/deployment/default/images/banner-background.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/deployment/default/images/beating-heart.gif b/src/deployment/default/images/beating-heart.gif new file mode 100644 index 000000000..1231c37ff Binary files /dev/null and b/src/deployment/default/images/beating-heart.gif differ diff --git a/src/deployment/default/images/emotion-badge.png b/src/deployment/default/images/emotion-badge.png new file mode 100644 index 000000000..0de505b6a Binary files /dev/null and b/src/deployment/default/images/emotion-badge.png differ diff --git a/src/deployment/default/images/first-lessons-python.svg b/src/deployment/default/images/first-lessons-python.svg new file mode 100644 index 000000000..b55922589 --- /dev/null +++ b/src/deployment/default/images/first-lessons-python.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/deployment/default/images/flashing-emotions.gif b/src/deployment/default/images/flashing-emotions.gif new file mode 100644 index 000000000..1231c37ff Binary files /dev/null and b/src/deployment/default/images/flashing-emotions.gif differ diff --git a/src/deployment/default/images/get-silly.png b/src/deployment/default/images/get-silly.png new file mode 100644 index 000000000..0de505b6a Binary files /dev/null and b/src/deployment/default/images/get-silly.png differ diff --git a/src/deployment/default/images/heart.png b/src/deployment/default/images/heart.png new file mode 100644 index 000000000..0de505b6a Binary files /dev/null and b/src/deployment/default/images/heart.png differ diff --git a/src/deployment/default/images/troubleshooting.svg b/src/deployment/default/images/troubleshooting.svg new file mode 100644 index 000000000..b55922589 --- /dev/null +++ b/src/deployment/default/images/troubleshooting.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/deployment/default/images/user-guide.svg b/src/deployment/default/images/user-guide.svg new file mode 100644 index 000000000..b55922589 --- /dev/null +++ b/src/deployment/default/images/user-guide.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/fs/db.ts b/src/fs/db.ts new file mode 100644 index 000000000..2113302ac --- /dev/null +++ b/src/fs/db.ts @@ -0,0 +1,75 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { DBSchema, IDBPDatabase, openDB } from "idb"; + +/** + * A project as persisted in IndexedDB. + * + * This is the multi-project "library". The currently open project is loaded + * from here into the in-memory {@link FileSystem} working copy and saved back + * as the user edits. + */ +export interface ProjectRecord { + /** Stable project identifier. */ + id: string; + /** User-visible project name. */ + name: string; + /** File content keyed by filename, base64 encoded (as in PythonProject). */ + files: Record; + /** Last modified time, milliseconds since the epoch. */ + timestamp: number; +} + +const DB_NAME = "python-editor"; +const DB_VERSION = 1; +const PROJECTS_STORE = "projects"; + +interface Schema extends DBSchema { + [PROJECTS_STORE]: { + key: string; + value: ProjectRecord; + }; +} + +/** + * Data access layer for the multi-project store. + */ +export class ProjectsDatabase { + private dbPromise: Promise> | undefined; + + private db(): Promise> { + if (!this.dbPromise) { + this.dbPromise = openDB(DB_NAME, DB_VERSION, { + upgrade(db) { + db.createObjectStore(PROJECTS_STORE, { keyPath: "id" }); + }, + }); + } + return this.dbPromise; + } + + async getAll(): Promise { + const db = await this.db(); + return db.getAll(PROJECTS_STORE); + } + + async get(id: string): Promise { + const db = await this.db(); + return db.get(PROJECTS_STORE, id); + } + + async put(record: ProjectRecord): Promise { + const db = await this.db(); + await db.put(PROJECTS_STORE, record); + } + + async delete(id: string): Promise { + const db = await this.db(); + await db.delete(PROJECTS_STORE, id); + } +} + +export const projectsDb = new ProjectsDatabase(); diff --git a/src/fs/project-sync.ts b/src/fs/project-sync.ts new file mode 100644 index 000000000..d19d0e0aa --- /dev/null +++ b/src/fs/project-sync.ts @@ -0,0 +1,50 @@ +/** + * Cross-tab project synchronisation via BroadcastChannel. + * + * Keeps the project library and the open project in sync between tabs and + * windows in the same browser. + * + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +export enum ProjectSyncMessageType { + /** A project's contents or name changed (or a project was created). */ + ReloadProject = "reload-project", + /** A project was deleted. */ + DeleteProject = "delete-project", +} + +export interface ProjectSyncMessage { + type: ProjectSyncMessageType; + projectIds: string[]; +} + +const channel = + typeof BroadcastChannel !== "undefined" + ? new BroadcastChannel("python-editor-projects") + : undefined; + +export const postProjectSync = (message: ProjectSyncMessage): void => { + channel?.postMessage(message); +}; + +/** + * Subscribe to project sync messages from other tabs. + * + * Note: BroadcastChannel does not deliver a message to the tab that sent it, + * so listeners only see changes made elsewhere. + * + * @returns An unsubscribe function. + */ +export const addProjectSyncListener = ( + listener: (message: ProjectSyncMessage) => void +): (() => void) => { + if (!channel) { + return () => {}; + } + const handler = (event: MessageEvent) => + listener(event.data); + channel.addEventListener("message", handler); + return () => channel.removeEventListener("message", handler); +}; diff --git a/src/homepage/ActionCard.tsx b/src/homepage/ActionCard.tsx new file mode 100644 index 000000000..3abbf980b --- /dev/null +++ b/src/homepage/ActionCard.tsx @@ -0,0 +1,69 @@ +/** + * (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors + * Modifications (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + Button, + Card, + CardBody, + Icon, + LinkBox, + LinkOverlay, + VStack, +} from "@chakra-ui/react"; +import { IconType } from "react-icons/lib"; +import { FormattedMessage } from "react-intl"; +import { shortScreenHeightBreakpoint } from "./responsive"; + +interface ActionCardProps { + onClick: () => void; + icon: IconType; + textId: string; +} + +/** + * A large, coloured call-to-action card used in the projects carousel + * (e.g. "New project", "View all projects"). + */ +const ActionCard = ({ onClick, icon, textId }: ActionCardProps) => { + return ( + + + + + + + + + + + + + ); +}; + +export default ActionCard; diff --git a/src/homepage/DefaultPageLayout.tsx b/src/homepage/DefaultPageLayout.tsx new file mode 100644 index 000000000..2b17bd2a3 --- /dev/null +++ b/src/homepage/DefaultPageLayout.tsx @@ -0,0 +1,112 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Box, Flex, Heading, HStack, VStack } from "@chakra-ui/react"; +import { ReactNode } from "react"; +import { Link as RouterLink } from "react-router-dom"; +import HomeButton from "../common/HomeButton"; +import { useDeployment } from "../deployment"; +import SettingsMenu from "../settings/SettingsMenu"; +import { createHomePageUrl } from "../urls"; +import HelpMenu from "../workbench/HelpMenu"; + +interface DefaultPageLayoutProps { + children: ReactNode; + /** Show a "← Home" button on the left instead of the app branding. */ + showBackToHome?: boolean; +} + +const Branding = () => { + const brand = useDeployment(); + return ( + + {brand.squareLogo && ( + <> + + + {brand.squareLogo} + + + {brand.horizontalLogo} + + + + + )} + {/* Product name; deliberately not translated. */} + + Python Editor + + + ); +}; + +/** + * Simple full-height page layout with a top toolbar and a scrollable body. + * + * A pared-down version of the CreateAI layout suitable for this prototype. + */ +const DefaultPageLayout = ({ + children, + showBackToHome = false, +}: DefaultPageLayoutProps) => { + return ( + + + {showBackToHome ? : } + + + + + + + {children} + + + ); +}; + +export default DefaultPageLayout; diff --git a/src/homepage/HelpCards.tsx b/src/homepage/HelpCards.tsx new file mode 100644 index 000000000..8c0e29e37 --- /dev/null +++ b/src/homepage/HelpCards.tsx @@ -0,0 +1,55 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { IntlShape } from "react-intl"; +import { + accessibilityUrl, + supportSiteFolderUrl, + userGuideUrl, +} from "./external-links"; +import helpImage1 from "theme-package/images/user-guide.svg"; +import helpImage2 from "theme-package/images/troubleshooting.svg"; +import helpImage3 from "theme-package/images/accessibility.svg"; +import ResourceCard from "./ResourceCard"; + +interface HelpConfig { + titleId: string; + url: string; + imgSrc: string; + imagePadding?: number; + aspectRatio?: number; +} + +const helpConfig: HelpConfig[] = [ + { + titleId: "user-guide", + url: userGuideUrl(), + imgSrc: helpImage1, + }, + { + titleId: "troubleshooting-resource-title", + url: supportSiteFolderUrl(), + imgSrc: helpImage2, + }, + { + titleId: "accessibility-resource-title", + url: accessibilityUrl(), + imgSrc: helpImage3, + }, +]; + +export const createHelpCards = (intl: IntlShape) => + helpConfig.map((help) => ( + + )); diff --git a/src/homepage/HomePage.tsx b/src/homepage/HomePage.tsx new file mode 100644 index 000000000..1d44ef8b8 --- /dev/null +++ b/src/homepage/HomePage.tsx @@ -0,0 +1,84 @@ +/** + * (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors + * Modifications (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Heading } from "@chakra-ui/react"; +import { FormattedMessage, useIntl } from "react-intl"; +import { useProjects } from "../project/projects-hooks"; +import { useSettings } from "../settings/settings"; +import CarouselRow from "./carousel/CarouselRow"; +import DefaultPageLayout from "./DefaultPageLayout"; +import { createHelpCards } from "./HelpCards"; +import HomepageBanner from "./HomepageBanner"; +import ImportProjectButton from "./ImportProjectButton"; +import { createLessonCards } from "./LessonCards"; +import NewProjectCard from "./NewProjectCard"; +import ProjectCard from "./ProjectCard"; +import { createProjectIdeaCards } from "./ProjectIdeaCards"; +import { useProjectCardActions } from "./use-project-card-actions"; +import ViewAllProjectsCard from "./ViewAllProjectsCard"; +import ViewAllProjectsLink from "./ViewAllProjectsLink"; + +const numCardsDisplayed = 10; + +const HomePage = () => { + const intl = useIntl(); + const [{ languageId }] = useSettings(); + const { projects } = useProjects(); + const { handleCreate, handleOpen, handleRenameDuplicate, handleDelete } = + useProjectCardActions("home"); + + const projectCards: JSX.Element[] = [ + , + ...projects.slice(0, numCardsDisplayed).map((project) => ( + + )), + ...(projects.length > numCardsDisplayed + ? [] + : []), + ]; + + return ( + + + , + , + ]} + titleElement={ + + + + } + /> + + + + + ); +}; + +export default HomePage; diff --git a/src/homepage/HomepageBanner.tsx b/src/homepage/HomepageBanner.tsx new file mode 100644 index 000000000..ff2745c8b --- /dev/null +++ b/src/homepage/HomepageBanner.tsx @@ -0,0 +1,83 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Button, Heading, HStack, Link, Text, VStack } from "@chakra-ui/react"; +import { FormattedMessage } from "react-intl"; +import { useSettings } from "../settings/settings"; +import { learnMoreUrl } from "./external-links"; +import bannerBackground from "theme-package/images/banner-background.svg"; + +// For landscape mobile screen sizes. +const shortScreenHeightBreakpoint = "@media (max-height: 700px)"; + +const HomepageBanner = () => { + const [settings] = useSettings(); + return ( + + + + + + + + + + + + + + + + ); +}; + +export default HomepageBanner; diff --git a/src/homepage/ImportProjectButton.tsx b/src/homepage/ImportProjectButton.tsx new file mode 100644 index 000000000..17131e61f --- /dev/null +++ b/src/homepage/ImportProjectButton.tsx @@ -0,0 +1,65 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Button, IconButton, Input } from "@chakra-ui/react"; +import { useCallback, useRef } from "react"; +import { RiUpload2Line } from "react-icons/ri"; +import { FormattedMessage, useIntl } from "react-intl"; +import { useImportProject } from "../project/use-import-project"; + +/** + * Opens a file picker to import a hex as a new project. Shown as a labelled + * button on wider screens and an icon-only button on narrow ones. + */ +const ImportProjectButton = () => { + const intl = useIntl(); + const importProject = useImportProject(); + const inputRef = useRef(null); + + const handleClick = useCallback(() => { + inputRef.current?.click(); + }, []); + + const handleChange = useCallback( + (e: React.ChangeEvent) => { + const files = e.target.files ? Array.from(e.target.files) : []; + // Allow re-selecting the same file. + e.target.value = ""; + if (files.length > 0) { + void importProject(files, "file-upload"); + } + }, + [importProject] + ); + + return ( + <> + + } + onClick={handleClick} + aria-label={intl.formatMessage({ id: "import-file-action" })} + variant="ghost" + display={{ base: "inline-flex", sm: "none" }} + /> + + + ); +}; + +export default ImportProjectButton; diff --git a/src/homepage/LessonCards.tsx b/src/homepage/LessonCards.tsx new file mode 100644 index 000000000..403346932 --- /dev/null +++ b/src/homepage/LessonCards.tsx @@ -0,0 +1,38 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + * + * NOTE: placeholder content/URLs carried over from CreateAI for the prototype. + */ +import { IntlShape } from "react-intl"; +import { lessonUrl } from "./external-links"; +import lessonImage1 from "theme-package/images/first-lessons-python.svg"; +import ResourceCard from "./ResourceCard"; + +interface LessonConfig { + titleId: string; + url: string; + imgSrc: string; +} + +const lessons: LessonConfig[] = [ + { + titleId: "first-lessons-with-python-resource-title", + url: "first-lessons-with-python-and-the-microbit", + imgSrc: lessonImage1, + }, +]; + +export const createLessonCards = (intl: IntlShape) => + lessons.map((lesson) => ( + + )); diff --git a/src/homepage/NewProjectCard.tsx b/src/homepage/NewProjectCard.tsx new file mode 100644 index 000000000..14c073f43 --- /dev/null +++ b/src/homepage/NewProjectCard.tsx @@ -0,0 +1,17 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { RiAddLine } from "react-icons/ri"; +import ActionCard from "./ActionCard"; + +interface NewProjectCardProps { + onClick: () => void; +} + +const NewProjectCard = ({ onClick }: NewProjectCardProps) => ( + +); + +export default NewProjectCard; diff --git a/src/homepage/ProjectCard.tsx b/src/homepage/ProjectCard.tsx new file mode 100644 index 000000000..2e162805f --- /dev/null +++ b/src/homepage/ProjectCard.tsx @@ -0,0 +1,123 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + Button, + Card, + CardBody, + Icon, + LinkBox, + LinkOverlay, + Stack, + Text, +} from "@chakra-ui/react"; +import { useCallback } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; +import PythonLogo from "../common/PythonLogo"; +import { ProjectRecord } from "../fs/db"; +import { timeAgo } from "./datetime"; +import ProjectCardActions from "./ProjectCardActions"; +import { ProjectNameDialogReason } from "./project-name"; +import { shortScreenHeightBreakpoint } from "./responsive"; + +interface ProjectCardProps { + projectData: ProjectRecord; + isSelected?: boolean; + onSelected?: (id: string) => void; + onSkipToToolbar?: () => void; + onDeleteProject: (id: string) => void; + onOpenProject: (id: string) => void; + onRenameDuplicateProject: ( + reason: ProjectNameDialogReason, + id: string + ) => void; +} + +const ProjectCard = ({ + projectData, + isSelected, + onSelected, + onSkipToToolbar, + onDeleteProject, + onOpenProject, + onRenameDuplicateProject, +}: ProjectCardProps) => { + const intl = useIntl(); + const { id, name, files, timestamp } = projectData; + const fileCount = Object.keys(files).length; + const hasCheckbox = !!onSelected; + + const handleOpen = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + onOpenProject(id); + }, + [id, onOpenProject] + ); + + return ( + + + + + + + + {name} + + + + + + {timeAgo(intl, timestamp)} + + + + + + ); +}; + +export default ProjectCard; diff --git a/src/homepage/ProjectCardActions.tsx b/src/homepage/ProjectCardActions.tsx new file mode 100644 index 000000000..73bfae238 --- /dev/null +++ b/src/homepage/ProjectCardActions.tsx @@ -0,0 +1,152 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + Button, + Checkbox, + HStack, + IconButton, + Menu, + MenuButton, + MenuItem, + MenuList, + Portal, + VisuallyHidden, +} from "@chakra-ui/react"; +import { useCallback } from "react"; +import { MdMoreVert } from "react-icons/md"; +import { + RiDeleteBin2Line, + RiEdit2Line, + RiFileCopyLine, + RiFolderOpenLine, +} from "react-icons/ri"; +import { FormattedMessage, useIntl } from "react-intl"; +import { ProjectNameDialogReason } from "./project-name"; + +interface ProjectCardActionsProps { + id: string; + name: string; + isSelected?: boolean; + onSelected?: (id: string) => void; + onDeleteProject: (id: string) => void; + onOpenProject: (id: string) => void; + onRenameDuplicateProject: ( + reason: ProjectNameDialogReason, + id: string + ) => void; + onSkipToToolbar?: () => void; +} + +const ProjectCardActions = ({ + id, + name, + isSelected, + onSelected, + onDeleteProject, + onRenameDuplicateProject, + onOpenProject, + onSkipToToolbar, +}: ProjectCardActionsProps) => { + const intl = useIntl(); + const handleRenameProject = useCallback( + () => onRenameDuplicateProject("rename", id), + [onRenameDuplicateProject, id] + ); + const handleDuplicateProject = useCallback( + () => onRenameDuplicateProject("duplicate", id), + [onRenameDuplicateProject, id] + ); + + return ( + + {onSelected && ( + onSelected(id)} + color="brand.600" + zIndex={1} + borderColor="gray.600" + _hover={{ backgroundColor: "blackAlpha.50" }} + borderBottomRightRadius="md" + h="60px" + > + + + + + )} + {onSkipToToolbar && ( + + )} + + } + ml="auto" + /> + + + } + onClick={() => onOpenProject(id)} + > + + + } onClick={handleRenameProject}> + + + } onClick={handleDuplicateProject}> + + + } + onClick={() => onDeleteProject(id)} + > + + + + + + + ); +}; + +export default ProjectCardActions; diff --git a/src/homepage/ProjectIdeaCards.tsx b/src/homepage/ProjectIdeaCards.tsx new file mode 100644 index 000000000..a65cf3f39 --- /dev/null +++ b/src/homepage/ProjectIdeaCards.tsx @@ -0,0 +1,64 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { IntlShape } from "react-intl"; +import { projectUrl } from "./external-links"; +import animatedAnimals from "theme-package/images/animated-animals.gif"; +import beatingHeart from "theme-package/images/beating-heart.gif"; +import emotionBadge from "theme-package/images/emotion-badge.png"; +import flashingEmotions from "theme-package/images/flashing-emotions.gif"; +import getSilly from "theme-package/images/get-silly.png"; +import heart from "theme-package/images/heart.png"; +import ResourceCard from "./ResourceCard"; + +interface ProjectIdeaConfig { + titleId: string; + // "make it, code it" project slug. + slug: string; + imgSrc: string; +} + +const projectIdeas: ProjectIdeaConfig[] = [ + { + titleId: "project-idea-heart-title", + slug: "heart", + imgSrc: heart, + }, + { + titleId: "project-idea-beating-heart-title", + slug: "beating-heart", + imgSrc: beatingHeart, + }, + { + titleId: "project-idea-animated-animals-title", + slug: "animated-animals", + imgSrc: animatedAnimals, + }, + { + titleId: "project-idea-emotion-badge-title", + slug: "emotion-badge", + imgSrc: emotionBadge, + }, + { + titleId: "project-idea-get-silly-title", + slug: "get-silly", + imgSrc: getSilly, + }, + { + titleId: "project-idea-flashing-emotions-title", + slug: "flashing-emotions", + imgSrc: flashingEmotions, + }, +]; + +export const createProjectIdeaCards = (intl: IntlShape, languageId: string) => + projectIdeas.map((project) => ( + + )); diff --git a/src/homepage/ProjectNameBody.tsx b/src/homepage/ProjectNameBody.tsx new file mode 100644 index 000000000..fa764ebe8 --- /dev/null +++ b/src/homepage/ProjectNameBody.tsx @@ -0,0 +1,31 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { FormControl, FormLabel, Input } from "@chakra-ui/react"; +import { FormattedMessage, useIntl } from "react-intl"; +import { InputDialogBody } from "../common/InputDialog"; + +const ProjectNameBody = ({ + value, + setValue, + validationResult, +}: InputDialogBody) => { + const intl = useIntl(); + return ( + + + + + setValue(e.target.value)} + aria-label={intl.formatMessage({ id: "name-text" })} + /> + + ); +}; + +export default ProjectNameBody; diff --git a/src/homepage/ProjectsPage.tsx b/src/homepage/ProjectsPage.tsx new file mode 100644 index 000000000..6908a27b8 --- /dev/null +++ b/src/homepage/ProjectsPage.tsx @@ -0,0 +1,268 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + Box, + Container, + Flex, + Heading, + HStack, + SimpleGrid, + Slide, + Stack, + Text, + useBreakpointValue, + VStack, +} from "@chakra-ui/react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { FormattedMessage } from "react-intl"; +import { ProjectRecord } from "../fs/db"; +import { useProjects } from "../project/projects-hooks"; +import DefaultPageLayout from "./DefaultPageLayout"; +import ProjectCard from "./ProjectCard"; +import { ProjectNameDialogReason } from "./project-name"; +import ProjectsToolbar from "./ProjectsToolbar"; +import Search from "./Search"; +import SortInput from "./SortInput"; +import { useProjectCardActions } from "./use-project-card-actions"; + +type OrderByField = "timestamp" | "name"; + +const searchScore = (project: ProjectRecord, terms: string[]): number => { + const name = project.name.toLowerCase(); + const fileNames = Object.keys(project.files).map((f) => f.toLowerCase()); + // Every term must match the name or one of the file names. + const allMatch = terms.every( + (t) => name.includes(t) || fileNames.some((f) => f.includes(t)) + ); + if (!allMatch) { + return 0; + } + let score = 0; + for (const term of terms) { + if (name === term) { + score += 100; + } else if (name.startsWith(term)) { + score += 50; + } else if (name.includes(term)) { + score += 30; + } + if (fileNames.some((f) => f.includes(term))) { + score += 5; + } + } + return score; +}; + +const ProjectsPage = () => { + const { projects } = useProjects(); + const { handleOpen, handleRenameDuplicate, handleDelete, handleDeleteMany } = + useProjectCardActions("projects"); + + const [selectedProjectIds, setSelectedProjectIds] = useState([]); + const [query, setQuery] = useState(""); + const [orderByField, setOrderByField] = useState("timestamp"); + const [orderByDirection, setOrderByDirection] = useState<"asc" | "desc">( + "desc" + ); + const mobileIconOnly = useBreakpointValue({ base: true, md: false }); + + // Drop selections for projects that no longer exist. + useEffect(() => { + const ids = new Set(projects.map((p) => p.id)); + setSelectedProjectIds((prev) => prev.filter((id) => ids.has(id))); + }, [projects]); + + const handleQueryChange = useCallback( + (e: React.ChangeEvent) => { + if (e.target.value.trim()) { + setSelectedProjectIds([]); + } + setQuery(e.target.value); + }, + [] + ); + + const handleOrderByFieldChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value as OrderByField; + setOrderByField(value); + setOrderByDirection(value === "name" ? "asc" : "desc"); + }, + [] + ); + const toggleOrderByDirection = useCallback(() => { + setOrderByDirection((prev) => (prev === "asc" ? "desc" : "asc")); + }, []); + + const updateSelectedProjects = useCallback((id: string) => { + setSelectedProjectIds((prev) => + prev.includes(id) ? prev.filter((v) => v !== id) : [...prev, id] + ); + }, []); + const clearSelection = useCallback(() => setSelectedProjectIds([]), []); + + const processedProjects = useMemo(() => { + const trimmed = query.toLowerCase().trim(); + if (trimmed) { + const terms = trimmed.split(/\s+/); + return projects + .map((p) => ({ project: p, score: searchScore(p, terms) })) + .filter((x) => x.score > 0) + .sort((a, b) => b.score - a.score) + .map((x) => x.project); + } + const sorted = [...projects].sort((a, b) => { + if (orderByField === "name") { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + } + return a.timestamp - b.timestamp; + }); + return orderByDirection === "desc" ? sorted.reverse() : sorted; + }, [projects, query, orderByField, orderByDirection]); + + const hasSelection = selectedProjectIds.length > 0; + const lastSelectionRef = useRef(selectedProjectIds); + if (hasSelection) { + lastSelectionRef.current = selectedProjectIds; + } + + const desktopToolbarRef = useRef(null); + const mobileToolbarRef = useRef(null); + const handleSkipToToolbar = useCallback(() => { + const toolbar = desktopToolbarRef.current?.offsetParent + ? desktopToolbarRef.current + : mobileToolbarRef.current; + toolbar?.querySelector("button")?.focus(); + }, []); + + const handleToolbarRenameDuplicate = useCallback( + (reason: ProjectNameDialogReason) => { + if (selectedProjectIds.length === 1) { + void handleRenameDuplicate(reason, selectedProjectIds[0]); + } + }, + [handleRenameDuplicate, selectedProjectIds] + ); + const handleToolbarDelete = useCallback(() => { + void handleDeleteMany(selectedProjectIds); + }, [handleDeleteMany, selectedProjectIds]); + + return ( + <> + + + + + + + + setQuery("")} + maxW="30ch" + my="1px" + /> + {hasSelection && ( + + + + )} + + + + {processedProjects.map((project) => ( + + + + ))} + + {query.trim() && processedProjects.length === 0 && ( + + + + + + )} + + + + + + + + + + ); +}; + +export default ProjectsPage; diff --git a/src/homepage/ProjectsToolbar.tsx b/src/homepage/ProjectsToolbar.tsx new file mode 100644 index 000000000..4b5ac9eec --- /dev/null +++ b/src/homepage/ProjectsToolbar.tsx @@ -0,0 +1,117 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + Button, + ButtonGroup, + ButtonGroupProps, + IconButton, +} from "@chakra-ui/react"; +import { + RiCloseLine, + RiDeleteBin2Line, + RiEdit2Line, + RiFileCopyLine, +} from "react-icons/ri"; +import { FormattedMessage, useIntl } from "react-intl"; +import { ProjectNameDialogReason } from "./project-name"; + +interface ProjectsToolbarProps extends ButtonGroupProps { + selectedProjectIds: string[]; + onRenameDuplicateProject: (reason: ProjectNameDialogReason) => void; + onDeleteProject: () => void; + onClearSelection: () => void; + iconOnly?: boolean; +} + +const ProjectsToolbar = ({ + selectedProjectIds, + onRenameDuplicateProject, + onDeleteProject, + onClearSelection, + iconOnly, + ...rest +}: ProjectsToolbarProps) => { + const count = selectedProjectIds.length; + const isSingle = count === 1; + const intl = useIntl(); + const iconFontSize = iconOnly ? "xl" : undefined; + + return ( + button": { borderRadius: 0 }, + "& > button + button": { + borderLeftWidth: "1px", + borderColor: "gray.200", + }, + }} + {...rest} + > + {isSingle && + (iconOnly ? ( + onRenameDuplicateProject("rename")} + icon={} + fontSize={iconFontSize} + aria-label={intl.formatMessage({ id: "rename-project-action" })} + /> + ) : ( + + ))} + {isSingle && + (iconOnly ? ( + onRenameDuplicateProject("duplicate")} + icon={} + fontSize={iconFontSize} + aria-label={intl.formatMessage({ id: "duplicate-project-action" })} + /> + ) : ( + + ))} + {iconOnly ? ( + } + fontSize={iconFontSize} + aria-label={intl.formatMessage({ id: "delete-project-action" })} + /> + ) : ( + + )} + {iconOnly ? ( + } + fontSize={iconFontSize} + aria-label={intl.formatMessage({ id: "clear" })} + /> + ) : ( + + )} + + ); +}; + +export default ProjectsToolbar; diff --git a/src/homepage/ResourceCard.tsx b/src/homepage/ResourceCard.tsx new file mode 100644 index 000000000..573afda6c --- /dev/null +++ b/src/homepage/ResourceCard.tsx @@ -0,0 +1,68 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + AspectRatio, + Box, + Heading, + HStack, + Image, + Link, + LinkBox, + LinkOverlay, + VStack, +} from "@chakra-ui/react"; +import { ReactNode } from "react"; + +interface ResourceCardProps { + aspectRatio?: number; + imagePadding?: number; + url: string; + imgSrc: string; + title: ReactNode; +} + +const ResourceCard = ({ + aspectRatio = 4 / 3, + imagePadding, + imgSrc, + url, + title, +}: ResourceCardProps) => { + return ( + + + + + + + + + + + {title} + + + + + + ); +}; + +export default ResourceCard; diff --git a/src/homepage/Search.tsx b/src/homepage/Search.tsx new file mode 100644 index 000000000..15ee39b2e --- /dev/null +++ b/src/homepage/Search.tsx @@ -0,0 +1,72 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + BoxProps, + IconButton, + Input, + InputGroup, + InputLeftElement, + InputRightElement, +} from "@chakra-ui/react"; +import { useCallback, useRef } from "react"; +import { RiCloseLine, RiSearch2Line } from "react-icons/ri"; +import { useIntl } from "react-intl"; + +interface SearchProps extends BoxProps { + query: string; + onChange: React.ChangeEventHandler; + onClear: () => void; +} + +const Search = ({ + query, + onChange: onQueryChange, + onClear, + ...rest +}: SearchProps) => { + const intl = useIntl(); + const ref = useRef(null); + + const handleClear = useCallback(() => { + onClear(); + ref.current?.focus(); + }, [onClear]); + + return ( + + + + + + {query && ( + + } + onClick={handleClear} + /> + + )} + + ); +}; + +export default Search; diff --git a/src/homepage/SortInput.tsx b/src/homepage/SortInput.tsx new file mode 100644 index 000000000..e42b53a52 --- /dev/null +++ b/src/homepage/SortInput.tsx @@ -0,0 +1,93 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { + ButtonGroup, + ButtonGroupProps, + IconButton, + Select, +} from "@chakra-ui/react"; +import { RiArrowDownLine, RiArrowUpLine } from "react-icons/ri"; +import { useIntl } from "react-intl"; + +interface SortInputProps extends ButtonGroupProps { + value: string; + onSelectChange: React.ChangeEventHandler; + order: "desc" | "asc"; + toggleOrder: () => void; + hasSearchQuery: boolean; +} + +const SortInput = ({ + value, + onSelectChange, + order, + toggleOrder, + hasSearchQuery, + ...rest +}: SortInputProps) => { + const intl = useIntl(); + return ( + + + + ) : ( + + ) + } + /> + + ); +}; + +export default SortInput; diff --git a/src/homepage/ViewAllProjectsCard.tsx b/src/homepage/ViewAllProjectsCard.tsx new file mode 100644 index 000000000..945bd2755 --- /dev/null +++ b/src/homepage/ViewAllProjectsCard.tsx @@ -0,0 +1,26 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { useCallback } from "react"; +import { RiFolderOpenLine } from "react-icons/ri"; +import { useNavigate } from "react-router-dom"; +import { createProjectsPageUrl } from "../urls"; +import ActionCard from "./ActionCard"; + +const ViewAllProjectsCard = () => { + const navigate = useNavigate(); + const handleClick = useCallback(() => { + navigate(createProjectsPageUrl()); + }, [navigate]); + return ( + + ); +}; + +export default ViewAllProjectsCard; diff --git a/src/homepage/ViewAllProjectsLink.tsx b/src/homepage/ViewAllProjectsLink.tsx new file mode 100644 index 000000000..5aad4f246 --- /dev/null +++ b/src/homepage/ViewAllProjectsLink.tsx @@ -0,0 +1,28 @@ +/** + * (c) 2024-2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Link } from "@chakra-ui/react"; +import { FormattedMessage } from "react-intl"; +import { Link as RouterLink } from "react-router-dom"; +import { createProjectsPageUrl } from "../urls"; + +const ViewAllProjectsLink = () => { + return ( + + + + ); +}; + +export default ViewAllProjectsLink; diff --git a/src/homepage/carousel/CarouselButton.tsx b/src/homepage/carousel/CarouselButton.tsx new file mode 100644 index 000000000..8cd71e4d5 --- /dev/null +++ b/src/homepage/carousel/CarouselButton.tsx @@ -0,0 +1,66 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Box, BoxProps } from "@chakra-ui/react"; +import React from "react"; +import ChevronLeftIcon from "../icons/ChevronLeftIcon"; +import ChevronRightIcon from "../icons/ChevronRightIcon"; + +interface CarouselButtonProps extends BoxProps { + direction: "left" | "right"; + onClick?: () => void; + stroke?: string; +} + +const CarouselButton = React.forwardRef(function CarouselButton( + { direction, onClick, stroke, ...rest }: CarouselButtonProps, + ref: React.ForwardedRef +) { + return ( + + {direction === "left" ? ( + + ) : ( + + )} + + ); +}); + +export default CarouselButton; diff --git a/src/homepage/carousel/CarouselRow.tsx b/src/homepage/carousel/CarouselRow.tsx new file mode 100644 index 000000000..717e64fb7 --- /dev/null +++ b/src/homepage/carousel/CarouselRow.tsx @@ -0,0 +1,53 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Box, Heading, HStack } from "@chakra-ui/react"; +import { ReactNode } from "react"; +import { FormattedMessage } from "react-intl"; +import { shortScreenHeightBreakpoint } from "../responsive"; +import NewPageCarousel from "./NewPageCarousel"; + +interface CarouselRowProps { + carouselItems: JSX.Element[]; + containerMessageId: string; + titleElement?: ReactNode; + titleId?: string; + actions?: JSX.Element[]; +} + +const CarouselRow = ({ + carouselItems, + containerMessageId, + titleElement, + titleId, + actions, +}: CarouselRowProps) => { + return ( + + + {typeof titleId === "string" ? ( + + + + ) : ( + titleElement + )} + {actions} + + + + ); +}; + +export default CarouselRow; diff --git a/src/homepage/carousel/NewPageCarousel.tsx b/src/homepage/carousel/NewPageCarousel.tsx new file mode 100644 index 000000000..8d7dc06dc --- /dev/null +++ b/src/homepage/carousel/NewPageCarousel.tsx @@ -0,0 +1,121 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { useCallback, useMemo } from "react"; +import { SwiperClass } from "swiper/react"; +import SwiperCarousel from "./SwiperCarousel"; + +const fast = 1000; +const cardWidth = 260; +const mobileSpaceBetween = 15; + +interface NewPageCarouselProps { + carouselItems: JSX.Element[]; + containerMessageId: string; +} + +const NewPageCarousel = ({ + carouselItems, + containerMessageId, +}: NewPageCarouselProps) => { + const getSlidesPerGroup = useCallback((spacing: number) => { + // --carousel-px is 20px at md+ where this calculation is used. + return Math.max( + 1, + Math.floor((window.innerWidth - 40) / (cardWidth + spacing)) + ); + }, []); + + const breakpoints = useMemo(() => { + if (typeof window === "undefined") { + return; + } + + return { + // When window width is >= 0px. + 0: { + spaceBetween: mobileSpaceBetween, + slidesPerGroup: 1, + }, + // When window width is >= 768px. + 768: { + spaceBetween: 20, + slidesPerGroup: getSlidesPerGroup(20), + }, + // When window width is >= 992px. + 992: { + spaceBetween: 25, + slidesPerGroup: getSlidesPerGroup(25), + }, + // When window width is >= 1200px. + 1200: { + spaceBetween: 30, + slidesPerGroup: getSlidesPerGroup(30), + }, + // When window width is >= 1400px. + 1400: { + spaceBetween: 30, + slidesPerGroup: getSlidesPerGroup(30), + }, + }; + }, [getSlidesPerGroup]); + + const getBreakpoint = useCallback(() => { + if (typeof window !== "undefined" && breakpoints) { + if (window.innerWidth < 768) { + return breakpoints[0]; + } else if (window.innerWidth < 992) { + return breakpoints[768]; + } else if (window.innerWidth < 1200) { + return breakpoints[992]; + } else if (window.innerWidth < 1400) { + return breakpoints[1200]; + } else { + return breakpoints[1400]; + } + } + }, [breakpoints]); + + const recalculateBreakpoints = useCallback( + (swiper: SwiperClass) => { + if (typeof window === "undefined") { + return; + } + const breakpoint = getBreakpoint(); + if (breakpoint) { + let slidesPerGroup = breakpoint.slidesPerGroup; + if (breakpoint.spaceBetween !== mobileSpaceBetween) { + slidesPerGroup = getSlidesPerGroup(breakpoint.spaceBetween); + } + swiper.params.slidesPerGroup = slidesPerGroup; + } + }, + [getBreakpoint, getSlidesPerGroup] + ); + + return ( + div": { height: "100%", width: "260px" }, + "& .swiper": { + padding: "var(--carousel-pt) var(--carousel-px) var(--carousel-pb)", + }, + }} + /> + ); +}; +export default NewPageCarousel; diff --git a/src/homepage/carousel/SwiperCarousel.tsx b/src/homepage/carousel/SwiperCarousel.tsx new file mode 100644 index 000000000..90fbd7515 --- /dev/null +++ b/src/homepage/carousel/SwiperCarousel.tsx @@ -0,0 +1,125 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Box, SystemStyleObject } from "@chakra-ui/react"; +import React, { useCallback, useState } from "react"; +import { useIntl } from "react-intl"; +import { Swiper as SwiperClass } from "swiper"; +import "swiper/css"; +import "swiper/css/a11y"; +import "swiper/css/autoplay"; +import "swiper/css/navigation"; +import "swiper/css/pagination"; +import { A11y, Autoplay, Navigation, Pagination } from "swiper/modules"; +import { Swiper, SwiperProps, SwiperSlide } from "swiper/react"; +import SwiperCarouselButtons from "./SwiperCarouselButtons"; + +interface SwiperCarouselProps extends SwiperProps { + carouselItems: JSX.Element[]; + containerMessageId: string; + padding?: string | number; + slideClassName?: string; + swiperWrapperClassName?: string; + sx?: SystemStyleObject; +} + +const swiperModules = [A11y, Autoplay, Navigation, Pagination]; + +const SwiperCarousel = ({ + carouselItems, + containerMessageId, + padding, + navigation, + slideClassName, + swiperWrapperClassName, + sx: sxProp, + ...props +}: SwiperCarouselProps) => { + const intl = useIntl(); + const isRtl = false; + const [swiper, setSwiper] = useState(); + const handleSlideFocus = useCallback( + (e: React.FocusEvent) => { + if (swiper) { + swiper.slides.forEach((slide, i) => { + if (slide.contains(e.target)) { + swiper.activeIndex = i; + swiper.updateSlidesClasses(); + swiper.slideTo(i); + } + }); + } + }, + [swiper] + ); + + const handleSwiper = useCallback((swiper: SwiperClass) => { + setSwiper(swiper); + swiper.update(); + }, []); + + return ( + + + {navigation && } + {carouselItems.map((item) => ( + + {item} + + ))} + + + ); +}; + +export default SwiperCarousel; diff --git a/src/homepage/carousel/SwiperCarouselButtons.tsx b/src/homepage/carousel/SwiperCarouselButtons.tsx new file mode 100644 index 000000000..68c81dcd6 --- /dev/null +++ b/src/homepage/carousel/SwiperCarouselButtons.tsx @@ -0,0 +1,80 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Box } from "@chakra-ui/react"; +import { useEffect, useRef } from "react"; +import { useSwiper } from "swiper/react"; +import CarouselButton from "./CarouselButton"; + +const buttonStyles = { + position: "absolute" as const, + zIndex: 5, + top: "var(--carousel-pt)", + // Draw over the box shadow below the carousel cards + bottom: "calc(var(--carousel-pb) - 8px)", +}; + +const SwiperCarouselButtons = () => { + const isRtl = false; + const swiper = useSwiper(); + const nextButtonRef = useRef(null); + const prevButtonRef = useRef(null); + useEffect(() => { + if (!swiper.destroyed && prevButtonRef.current && nextButtonRef.current) { + swiper.navigation.prevEl = prevButtonRef.current; + swiper.navigation.nextEl = nextButtonRef.current; + swiper.navigation.update(); + (swiper.navigation.nextEl as HTMLElement).tabIndex = -1; + (swiper.navigation.prevEl as HTMLElement).tabIndex = -1; + } + }, [swiper.destroyed, swiper.navigation]); + + // Override tab index on buttons. These are useless for keyboard users. + // Just tab through the slide items/cards instead. + useEffect(() => { + const listener = () => { + (swiper.navigation.nextEl as HTMLElement).tabIndex = -1; + (swiper.navigation.prevEl as HTMLElement).tabIndex = -1; + }; + if (!swiper.destroyed) { + swiper.on("activeIndexChange", listener); + } + return () => { + if (!swiper.destroyed) { + swiper.off("activeIndexChange", listener); + } + }; + }, [swiper]); + + return ( + + + ); +}; + +export default SwiperCarouselButtons; diff --git a/src/homepage/datetime.ts b/src/homepage/datetime.ts new file mode 100644 index 000000000..e2b6e4ccf --- /dev/null +++ b/src/homepage/datetime.ts @@ -0,0 +1,33 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { IntlShape } from "react-intl"; + +const units: [Intl.RelativeTimeFormatUnit, number][] = [ + ["year", 1000 * 60 * 60 * 24 * 365], + ["month", 1000 * 60 * 60 * 24 * 30], + ["week", 1000 * 60 * 60 * 24 * 7], + ["day", 1000 * 60 * 60 * 24], + ["hour", 1000 * 60 * 60], + ["minute", 1000 * 60], +]; + +/** + * Formats a timestamp as a localized relative time, e.g. "3 days ago". + */ +export const timeAgo = (intl: IntlShape, timestamp: number): string => { + const diff = timestamp - Date.now(); + const abs = Math.abs(diff); + for (const [unit, ms] of units) { + if (abs >= ms) { + return intl.formatRelativeTime(Math.round(diff / ms), unit, { + numeric: "auto", + }); + } + } + return intl.formatRelativeTime(Math.round(diff / 1000), "second", { + numeric: "auto", + }); +}; diff --git a/src/homepage/external-links.ts b/src/homepage/external-links.ts new file mode 100644 index 000000000..37eca466c --- /dev/null +++ b/src/homepage/external-links.ts @@ -0,0 +1,39 @@ +/** + * External (theme-scoped) URLs used by the home page. + * + * For this prototype these live here rather than in the deployment/theme + * package. Several are placeholders carried over from CreateAI and can be + * updated to Python-specific destinations later. + * + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { stage } from "../environment"; + +const microbitOrgBaseUrl = + stage === "PRODUCTION" + ? "https://microbit.org/" + : "https://stage.microbit.org/"; + +const langPath = (languageId: string) => + languageId === "en" ? "" : `${languageId.toLowerCase()}/`; + +export const projectUrl = (slug: string, language: string) => + `${microbitOrgBaseUrl}${langPath( + language + )}projects/make-it-code-it/${encodeURIComponent(slug)}/`; + +export const lessonUrl = (slug: string) => + `${microbitOrgBaseUrl}teach/lessons/${encodeURIComponent(slug)}/`; + +export const userGuideUrl = () => + `${microbitOrgBaseUrl}get-started/user-guide/python-editor/`; + +export const supportSiteFolderUrl = () => + "https://support.microbit.org/support/solutions/folders/19000170475"; + +export const accessibilityUrl = () => `${microbitOrgBaseUrl}accessibility/`; + +export const learnMoreUrl = (languageId: string) => + `${microbitOrgBaseUrl}${langPath(languageId)}code/`; diff --git a/src/homepage/icons/ChevronLeftIcon.tsx b/src/homepage/icons/ChevronLeftIcon.tsx new file mode 100644 index 000000000..77c4b97a1 --- /dev/null +++ b/src/homepage/icons/ChevronLeftIcon.tsx @@ -0,0 +1,29 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +type Props = { + className?: string; + fill?: string; + stroke?: string; + strokeWidth?: number; +}; + +const ChevronLeftIcon = ({ + className = "", + fill = "none", + stroke = "#556C84", + strokeWidth = 1.5, +}: Props) => ( + + + +); + +export default ChevronLeftIcon; diff --git a/src/homepage/icons/ChevronRightIcon.tsx b/src/homepage/icons/ChevronRightIcon.tsx new file mode 100644 index 000000000..22b1dea12 --- /dev/null +++ b/src/homepage/icons/ChevronRightIcon.tsx @@ -0,0 +1,29 @@ +/** + * (c) 2024, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +type Props = { + className?: string; + fill?: string; + stroke?: string; + strokeWidth?: number; +}; + +const ChevronRightIcon = ({ + className = "", + fill = "none", + stroke = "#556C84", + strokeWidth = 1.5, +}: Props) => ( + + + +); + +export default ChevronRightIcon; diff --git a/src/homepage/project-name.ts b/src/homepage/project-name.ts new file mode 100644 index 000000000..5c5e3d48d --- /dev/null +++ b/src/homepage/project-name.ts @@ -0,0 +1,6 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +export type ProjectNameDialogReason = "rename" | "duplicate"; diff --git a/src/homepage/responsive.ts b/src/homepage/responsive.ts new file mode 100644 index 000000000..90912c9dd --- /dev/null +++ b/src/homepage/responsive.ts @@ -0,0 +1,8 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ + +// For landscape mobile screen sizes. +export const shortScreenHeightBreakpoint = "@media (max-height: 700px)"; diff --git a/src/homepage/use-project-card-actions.ts b/src/homepage/use-project-card-actions.ts new file mode 100644 index 000000000..243617cf6 --- /dev/null +++ b/src/homepage/use-project-card-actions.ts @@ -0,0 +1,174 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { createElement, useCallback } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; +import { useNavigate } from "react-router-dom"; +import { ConfirmDialog } from "../common/ConfirmDialog"; +import { + InputDialog, + InputDialogBody, + InputValidationResult, +} from "../common/InputDialog"; +import { useDialogs } from "../common/use-dialogs"; +import { useLogging } from "../logging/logging-hooks"; +import { useProjects } from "../project/projects-hooks"; +import { createEditorUrl } from "../urls"; +import ProjectNameBody from "./ProjectNameBody"; +import { ProjectNameDialogReason } from "./project-name"; + +export type ProjectActionSurface = "home" | "projects"; + +const validateName = (value: string): InputValidationResult => + value.trim().length > 0 ? { ok: true } : { ok: false }; + +/** + * Shared project card action handlers (create / open / rename / duplicate / + * delete) used by the home page and projects page. + */ +export const useProjectCardActions = (surface: ProjectActionSurface) => { + const intl = useIntl(); + const navigate = useNavigate(); + const dialogs = useDialogs(); + const logging = useLogging(); + const { + projects, + createProject, + openProject, + renameProject, + duplicateProject, + deleteProject, + } = useProjects(); + + const chooseName = useCallback( + (initialValue: string, headerId: string, actionId: string) => + dialogs.show((callback) => + createElement(InputDialog, { + header: createElement(FormattedMessage, { id: headerId }), + Body: ProjectNameBody as React.FC>, + initialValue, + actionLabel: intl.formatMessage({ id: actionId }), + validate: validateName, + callback, + }) + ), + [dialogs, intl] + ); + + const handleCreate = useCallback(async () => { + const name = await chooseName( + intl.formatMessage({ id: "untitled-project" }), + "create-project-dialog-heading", + "create-project-action" + ); + if (name) { + logging.event({ type: "project-create", detail: { surface } }); + await createProject(name.trim()); + navigate(createEditorUrl()); + } + }, [chooseName, createProject, intl, logging, navigate, surface]); + + const handleOpen = useCallback( + async (id: string) => { + logging.event({ type: "project-open", detail: { surface } }); + await openProject(id); + navigate(createEditorUrl()); + }, + [logging, navigate, openProject, surface] + ); + + const handleRenameDuplicate = useCallback( + async (reason: ProjectNameDialogReason, id: string) => { + const project = projects.find((p) => p.id === id); + if (!project) { + return; + } + const name = await chooseName( + project.name, + reason === "rename" + ? "rename-project-heading" + : "duplicate-project-heading", + reason === "rename" + ? "rename-project-action" + : "duplicate-project-action" + ); + if (name) { + if (reason === "rename") { + logging.event({ type: "project-rename", detail: { surface } }); + await renameProject(id, name.trim()); + } else { + logging.event({ type: "project-duplicate", detail: { surface } }); + await duplicateProject(id, name.trim()); + } + } + }, + [chooseName, duplicateProject, logging, projects, renameProject, surface] + ); + + const handleDelete = useCallback( + async (id: string) => { + const project = projects.find((p) => p.id === id); + if (!project) { + return; + } + const confirmed = await dialogs.show((callback) => + createElement(ConfirmDialog, { + header: intl.formatMessage({ id: "delete-project-confirm-heading" }), + body: createElement(FormattedMessage, { + id: "delete-project-confirm-text", + values: { project: project.name }, + }), + actionLabel: intl.formatMessage({ id: "delete-project-action" }), + callback, + }) + ); + if (confirmed) { + logging.event({ type: "project-delete", detail: { surface } }); + await deleteProject(id); + } + }, + [deleteProject, dialogs, intl, logging, projects, surface] + ); + + const handleDeleteMany = useCallback( + async (ids: string[]) => { + if (ids.length === 0) { + return; + } + if (ids.length === 1) { + return handleDelete(ids[0]); + } + const confirmed = await dialogs.show((callback) => + createElement(ConfirmDialog, { + header: intl.formatMessage({ id: "delete-projects-confirm-heading" }), + body: createElement(FormattedMessage, { + id: "delete-projects-confirm-text", + values: { numProjects: ids.length }, + }), + actionLabel: intl.formatMessage({ id: "delete-project-action" }), + callback, + }) + ); + if (confirmed) { + logging.event({ + type: "project-delete", + detail: { surface, count: ids.length }, + }); + for (const id of ids) { + await deleteProject(id); + } + } + }, + [deleteProject, dialogs, handleDelete, intl, logging, surface] + ); + + return { + handleCreate, + handleOpen, + handleRenameDuplicate, + handleDelete, + handleDeleteMany, + }; +}; diff --git a/src/messages/ui.ca.json b/src/messages/ui.ca.json index 00f5f41ac..a1b8c871a 100644 --- a/src/messages/ui.ca.json +++ b/src/messages/ui.ca.json @@ -65,6 +65,24 @@ "value": "Accessibilitat" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -165,6 +183,32 @@ "value": "Cancel·lar" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -459,12 +503,30 @@ "value": "Crea un fitxer" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Crea un nou fitxer Python en aquest projecte" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -491,6 +553,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -525,6 +633,18 @@ "value": "Arrossegar i deixar anar" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -615,6 +735,18 @@ "value": "El nom no pot contenir espais" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -633,6 +765,12 @@ "value": "Cal actualitzar el firmware" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -691,6 +829,18 @@ "value": "Ajuda" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -727,6 +877,30 @@ "value": "Senzill" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -739,6 +913,12 @@ "value": "Prova aquests projectes, modifica'ls i inspira't" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -751,6 +931,12 @@ "value": "Idioma" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1017,6 +1203,18 @@ "value": "Avís: només s'ha descarregat main.py" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1055,12 +1253,24 @@ "value": "." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Següent" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1169,6 +1379,12 @@ "value": "Obre un fitxer hexadecimal o Python o afegeix altres fitxers" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1313,12 +1529,112 @@ "value": "Accions del projecte" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Capçalera del projecte" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1331,6 +1647,12 @@ "value": "El nom del projecte no pot estar buit" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1343,6 +1665,12 @@ "value": "Visualitza, crea, afegeix i edita els fitxers del teu projecte" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1385,6 +1713,18 @@ "value": "Referència" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1489,6 +1829,26 @@ "value": "Cerca" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2205,6 +2565,42 @@ "value": "Versions de programari" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2217,6 +2613,18 @@ "value": "Suport" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2337,6 +2745,12 @@ "value": "Transfereix el fitxer hexadecimal desat a la micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2417,6 +2831,12 @@ "value": "Guia de l'usuari" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.de.json b/src/messages/ui.de.json index a9ebc990e..e5e77b6df 100644 --- a/src/messages/ui.de.json +++ b/src/messages/ui.de.json @@ -65,6 +65,24 @@ "value": "Barrierefreiheit" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -173,6 +191,32 @@ "value": "Abbrechen" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -495,12 +539,30 @@ "value": "Datei erstellen" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Neue Python-Datei in diesem Projekt erstellen" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 1, @@ -527,6 +589,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 1, @@ -561,6 +669,18 @@ "value": "Drag-And-Drop" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 1, @@ -651,6 +771,18 @@ "value": "Der Name darf keine Leerzeichen enthalten" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -683,6 +815,12 @@ "value": "Firmware-Aktualisierung erforderlich" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -741,6 +879,18 @@ "value": "Hilfe" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -777,6 +927,30 @@ "value": "Einfach" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -789,6 +963,12 @@ "value": "Probiere diese Projekte aus, ändere sie und lasse dich inspirieren" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -801,6 +981,12 @@ "value": "Sprache" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1071,6 +1257,18 @@ "value": "Warnung: Nur main.py heruntergeladen" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1109,12 +1307,24 @@ "value": "-Erweiterung für dich hinzufügen." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Weiter" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1247,6 +1457,12 @@ "value": "Eine HEX- oder Python-Datei öffnen oder andere Dateien hinzufügen." } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1387,12 +1603,112 @@ "value": "Projekt-Aktionen" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Projekt-Kopfzeile" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1405,6 +1721,12 @@ "value": "Der Projektname darf nicht leer sein" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1417,6 +1739,12 @@ "value": "Anzeigen, Erstellen, Hinzufügen und Bearbeiten der Dateien in deinem Projekt" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1459,6 +1787,18 @@ "value": "Referenz" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1563,6 +1903,26 @@ "value": "Suche" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2295,6 +2655,42 @@ "value": "Software-Versionen" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2307,6 +2703,18 @@ "value": "Support" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2427,6 +2835,12 @@ "value": "Gespeicherte Hex-Datei an micro:bit übertragen" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2499,6 +2913,12 @@ "value": "Benutzerhandbuch" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.en.json b/src/messages/ui.en.json index 9698703d9..67e489f45 100644 --- a/src/messages/ui.en.json +++ b/src/messages/ui.en.json @@ -65,6 +65,24 @@ "value": "Accessibility" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -165,6 +183,32 @@ "value": "Cancel" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -455,12 +499,30 @@ "value": "Create file" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Create a new Python file in this project" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -487,6 +549,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -521,6 +629,18 @@ "value": "Drag and drop" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -611,6 +731,18 @@ "value": "The name cannot contain spaces" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -643,6 +775,12 @@ "value": "Firmware update required" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -701,6 +839,18 @@ "value": "Help" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -737,6 +887,30 @@ "value": "Simple" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -749,6 +923,12 @@ "value": "Try out these projects, modify them and get inspired" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -761,6 +941,12 @@ "value": "Language" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1027,6 +1213,18 @@ "value": "Warning: Only main.py downloaded" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1065,12 +1263,24 @@ "value": " extension for you." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Next" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1203,6 +1413,12 @@ "value": "Open a hex or Python file or add other files" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1347,12 +1563,112 @@ "value": "Project actions" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Project header" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1365,6 +1681,12 @@ "value": "The project name cannot be empty" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1377,6 +1699,12 @@ "value": "View, create, add and edit the files in your project" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1419,6 +1747,18 @@ "value": "Reference" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1523,6 +1863,26 @@ "value": "Search" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2243,6 +2603,42 @@ "value": "Software versions" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2255,6 +2651,18 @@ "value": "Support" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2379,6 +2787,12 @@ "value": "Transfer saved hex file to micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2451,6 +2865,12 @@ "value": "User guide" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.es-es.json b/src/messages/ui.es-es.json index a4c22242e..e9d8dc300 100644 --- a/src/messages/ui.es-es.json +++ b/src/messages/ui.es-es.json @@ -65,6 +65,24 @@ "value": "Accesibilidad" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -169,6 +187,32 @@ "value": "Cancelar" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -459,12 +503,30 @@ "value": "Crear archivo" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Crear un nuevo archivo Python en este proyecto" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 1, @@ -491,6 +553,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 1, @@ -525,6 +633,18 @@ "value": "Arrastra y suelta" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -615,6 +735,18 @@ "value": "El nombre no puede incluir espacios" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -647,6 +779,12 @@ "value": "Es necesario actualizar el firmware" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -705,6 +843,18 @@ "value": "Ayuda" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -741,6 +891,30 @@ "value": "Simple" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -753,6 +927,12 @@ "value": "Prueba estos proyectos y modifícalos para inspirarte" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -765,6 +945,12 @@ "value": "Idioma" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1027,6 +1213,18 @@ "value": "Aviso: solo se ha descargado main.py" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1065,12 +1263,24 @@ "value": "." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Siguiente" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1203,6 +1413,12 @@ "value": "Abre un archivo HEX o Python o añade otros archivos" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1347,12 +1563,112 @@ "value": "Acciones del proyecto" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Cabecera del proyecto" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1365,6 +1681,12 @@ "value": "El nombre del proyecto no puede estar en blanco" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1377,6 +1699,12 @@ "value": "Ver, crear, añadir y editar los archivos del proyecto" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1419,6 +1747,18 @@ "value": "Referencia" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1523,6 +1863,26 @@ "value": "Buscar" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2235,6 +2595,42 @@ "value": "Versiones de software" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2247,6 +2643,18 @@ "value": "Ayuda Técnica" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2367,6 +2775,12 @@ "value": "Transferir archivo HEX guardado a micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2443,6 +2857,12 @@ "value": "Guía de usuario" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.fr.json b/src/messages/ui.fr.json index 78d2d1004..60ffe05e8 100644 --- a/src/messages/ui.fr.json +++ b/src/messages/ui.fr.json @@ -65,6 +65,24 @@ "value": "Accessibilité" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -165,6 +183,32 @@ "value": "Annuler" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -455,12 +499,30 @@ "value": "Créer un fichier" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Créer un nouveau fichier Python dans ce projet" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 1, @@ -487,6 +549,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 1, @@ -521,6 +629,18 @@ "value": "Glisser-déposer" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -611,6 +731,18 @@ "value": "Le nom ne peut pas contenir d'espaces" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -643,6 +775,12 @@ "value": "Mise à jour du micrologiciel requise" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -701,6 +839,18 @@ "value": "Aide" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -737,6 +887,30 @@ "value": "Simple" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -749,6 +923,12 @@ "value": "Essayez ces projets, modifiez-les et inspirez-vous en" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -761,6 +941,12 @@ "value": "Langue" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1027,6 +1213,18 @@ "value": "Avertissement : Seul main.py est téléchargé" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1065,12 +1263,24 @@ "value": " pour vous." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Suivant" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1203,6 +1413,12 @@ "value": "Ouvrir un fichier hex ou Python, ou ajouter d'autres fichiers" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1347,12 +1563,112 @@ "value": "Actions du projet" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "En-tête de projet" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1365,6 +1681,12 @@ "value": "Le nom du projet ne peut pas être vide" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1377,6 +1699,12 @@ "value": "Visualiser, créer, ajouter et modifier les fichiers de votre projet" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1419,6 +1747,18 @@ "value": "Référence" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1523,6 +1863,26 @@ "value": "Rechercher" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2235,6 +2595,42 @@ "value": "Versions logicielles" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2247,6 +2643,18 @@ "value": "Assistance" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2367,6 +2775,12 @@ "value": "Transférer le fichier hex enregistré vers le micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2443,6 +2857,12 @@ "value": "Guide de l'utilisateur" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.ga-ie.json b/src/messages/ui.ga-ie.json index d62dd8b27..12df6791a 100644 --- a/src/messages/ui.ga-ie.json +++ b/src/messages/ui.ga-ie.json @@ -65,6 +65,24 @@ "value": "Inrochtaineacht" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -193,6 +211,32 @@ "value": "Cealaigh" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -499,12 +543,30 @@ "value": "Cruthaigh comhad" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Cruthaigh comhad nua Python sa tionscadal seo" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -531,6 +593,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -565,6 +673,18 @@ "value": "Tarraing agus scaoil" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -659,6 +779,18 @@ "value": "Ní féidir spásanna a bheith san ainm" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -691,6 +823,12 @@ "value": "Nuashonrú dochtearraí ag teastáil" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -749,6 +887,18 @@ "value": "Cabhair" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -785,6 +935,30 @@ "value": "Simplí" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -797,6 +971,12 @@ "value": "Bain triail as na tionscadail seo, athraigh iad agus spreag iad" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -809,6 +989,12 @@ "value": "Teanga" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1103,6 +1289,18 @@ "value": "Rabhadh: Ní íoslódálfar ach main.py" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1141,12 +1339,24 @@ "value": " ar do shon." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Ar Aghaidh" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1279,6 +1489,12 @@ "value": "Oscail comhad heicsidheachúlach nó Python nó cuir comhaid eile leis" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1423,12 +1639,112 @@ "value": "Gníomhartha tionscadail" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Ceanntásc an tionscadail" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1441,6 +1757,12 @@ "value": "Ní féidir ainm an tionscadail a fholmhú" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1453,6 +1775,12 @@ "value": "Amharc ar na comhaid i do thionscadal, iad a chruthú, a chur leis agus a chur in eagar" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1495,6 +1823,18 @@ "value": "Tagairt" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1636,6 +1976,26 @@ "value": "Cuardaigh" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2385,6 +2745,42 @@ "value": "Leaganacha bogearraí" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2397,6 +2793,18 @@ "value": "Tacaíocht" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2517,6 +2925,12 @@ "value": "Aistrigh comhad heicsidheachúlach sábháilte go micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2589,6 +3003,12 @@ "value": "Treoir d'úsáideoirí" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.ja.json b/src/messages/ui.ja.json index f2ca82117..b7cb118f8 100644 --- a/src/messages/ui.ja.json +++ b/src/messages/ui.ja.json @@ -65,6 +65,24 @@ "value": "アクセシビリティ" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -165,6 +183,32 @@ "value": "キャンセル" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -479,12 +523,30 @@ "value": "ファイルを作成" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "このプロジェクトに新しいPythonファイルを作成" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 1, @@ -511,6 +573,52 @@ "value": " を削除" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 1, @@ -545,6 +653,18 @@ "value": "ドラッグ&ドロップ" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 1, @@ -635,6 +755,18 @@ "value": "名前にスペースを含めることはできません" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -667,6 +799,12 @@ "value": "ファームウェアの更新が必要です" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -729,6 +867,18 @@ "value": "ヘルプ" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -765,6 +915,30 @@ "value": "簡易" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -777,6 +951,12 @@ "value": "これらのプロジェクトを試し、修正し、インスピレーションを得ることができます。" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -789,6 +969,12 @@ "value": "言語" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1043,6 +1229,18 @@ "value": "注意: main.pyのみダウンロードされました" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1077,12 +1275,24 @@ "value": "拡張子を追加します。" } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "次" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "children": [ @@ -1211,6 +1421,12 @@ "value": "hex ファイルまたは Python ファイルを開くか、他のファイルを追加する" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1351,12 +1567,112 @@ "value": "プロジェクトアクション" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "プロジェクトヘッダー" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1369,6 +1685,12 @@ "value": "プロジェクト名は空にできません" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1381,6 +1703,12 @@ "value": "プロジェクト内のファイルの表示、作成、追加、編集" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1423,6 +1751,18 @@ "value": "リファレンス" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1516,6 +1856,26 @@ "value": "検索" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2229,6 +2589,42 @@ "value": "ソフトウェアのバージョン" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2241,6 +2637,18 @@ "value": "サポート" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2361,6 +2769,12 @@ "value": "保存された hex ファイルをmicro:bitに転送する" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2445,6 +2859,12 @@ "value": "ユーザーガイド" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.ko.json b/src/messages/ui.ko.json index 399b3e316..accb4e20a 100644 --- a/src/messages/ui.ko.json +++ b/src/messages/ui.ko.json @@ -65,6 +65,24 @@ "value": "접근성" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 1, @@ -169,6 +187,32 @@ "value": "취소" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -463,12 +507,30 @@ "value": "파일 생성" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "이 프로젝트 안에서 새로운 Python 파일 생성" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 1, @@ -495,6 +557,52 @@ "value": " 삭제" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 1, @@ -529,6 +637,18 @@ "value": "드래그 & 드롭" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 1, @@ -619,6 +739,18 @@ "value": "이름에 띄어쓰기를 포함할 수 없습니다" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -651,6 +783,12 @@ "value": "펌웨어 업데이트 필요" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -713,6 +851,18 @@ "value": "도움 및 지원" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -749,6 +899,30 @@ "value": "단순" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -761,6 +935,12 @@ "value": "다양한 프로젝트를 체험해보고 수정하며 아이디어를 얻어보세요" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -773,6 +953,12 @@ "value": "언어 선택" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1039,6 +1225,18 @@ "value": "경고: main.py만 다운로드됨" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1077,12 +1275,24 @@ "value": " 확장자를 추가합니다." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "다음" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1215,6 +1425,12 @@ "value": "Hex 또는 Python 파일을 열거나 다른 파일을 추가하세요" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1355,12 +1571,112 @@ "value": "프로젝트 동작" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "프로젝트 머리글" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1373,6 +1689,12 @@ "value": "프로젝트 이름을 입력해야 합니다" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1385,6 +1707,12 @@ "value": "프로젝트에서 파일을 확인하고, 생성하고, 추가하고 편집할 수 있습니다" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1427,6 +1755,18 @@ "value": "참조" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1531,6 +1871,26 @@ "value": "찾기" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2247,6 +2607,42 @@ "value": "소프트웨어 버전" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2259,6 +2655,18 @@ "value": "지원" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2367,6 +2775,12 @@ "value": "저장된 hex 파일을 micro:bit으로 전송" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2439,6 +2853,12 @@ "value": "사용자 가이드" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.lol.json b/src/messages/ui.lol.json index 2f64ccfc4..4517d6c6a 100644 --- a/src/messages/ui.lol.json +++ b/src/messages/ui.lol.json @@ -41,6 +41,24 @@ "value": "crwdns364038:0crwdne364038:0" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -123,6 +141,32 @@ "value": "crwdns331388:0crwdne331388:0" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -445,12 +489,30 @@ "value": "crwdns331462:0crwdne331462:0" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "crwdns331464:0crwdne331464:0" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -485,6 +547,52 @@ "value": "crwdne331470:0" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -523,6 +631,18 @@ "value": "crwdns331480:0crwdne331480:0" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -625,6 +745,18 @@ "value": "crwdns331506:0crwdne331506:0" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -643,6 +775,12 @@ "value": "crwdns331512:0crwdne331512:0" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -691,6 +829,18 @@ "value": "crwdns331528:0crwdne331528:0" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -727,6 +877,30 @@ "value": "crwdns331540:0crwdne331540:0" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -739,6 +913,12 @@ "value": "crwdns331544:0crwdne331544:0" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -751,6 +931,12 @@ "value": "crwdns331548:0crwdne331548:0" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -897,6 +1083,18 @@ "value": "crwdns331586:0crwdne331586:0" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -921,12 +1119,24 @@ "value": "crwdns331594:0crwdne331594:0" } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "crwdns331596:0crwdne331596:0" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -993,6 +1203,12 @@ "value": "crwdns331616:0crwdne331616:0" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1081,12 +1297,112 @@ "value": "crwdns331638:0crwdne331638:0" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "crwdns331640:0crwdne331640:0" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1099,6 +1415,12 @@ "value": "crwdns331644:0crwdne331644:0" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1111,6 +1433,12 @@ "value": "crwdns331648:0crwdne331648:0" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1153,6 +1481,18 @@ "value": "crwdns331662:0crwdne331662:0" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1235,6 +1575,26 @@ "value": "crwdns331684:0crwdne331684:0" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -1873,6 +2233,42 @@ "value": "crwdns331742:0crwdne331742:0" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -1885,6 +2281,18 @@ "value": "crwdns331746:0crwdne331746:0" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -1953,6 +2361,12 @@ "value": "crwdns331768:0crwdne331768:0" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2009,6 +2423,12 @@ "value": "crwdns364040:0crwdne364040:0" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.nl.json b/src/messages/ui.nl.json index 6364ad896..5c13fff70 100644 --- a/src/messages/ui.nl.json +++ b/src/messages/ui.nl.json @@ -65,6 +65,24 @@ "value": "Toegankelijkheid" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -165,6 +183,32 @@ "value": "Annuleren" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -467,12 +511,30 @@ "value": "Bestand aanmaken" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Maak een nieuw Python bestand aan in dit project" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -499,6 +561,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -533,6 +641,18 @@ "value": "Slepen en neerzetten" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 1, @@ -623,6 +743,18 @@ "value": "De naam mag geen spaties bevatten" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -655,6 +787,12 @@ "value": "Firmware update vereist" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -713,6 +851,18 @@ "value": "Help" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -749,6 +899,30 @@ "value": "Eenvoudig" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -761,6 +935,12 @@ "value": "Probeer deze projecten uit, verander ze en laat je inspireren" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -773,6 +953,12 @@ "value": "Taal" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1047,6 +1233,18 @@ "value": "Waarschuwing: Alleen main.py gedownload" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1085,12 +1283,24 @@ "value": " extensie voor je toevoegen." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Volgende" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1223,6 +1433,12 @@ "value": "Open een hex of Python bestand of voeg andere bestanden toe" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1367,12 +1583,112 @@ "value": "Project acties" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Project koptekst" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1385,6 +1701,12 @@ "value": "De projectnaam kan niet leeg zijn" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1397,6 +1719,12 @@ "value": "Bekijk, aanmaken, toevoegen en bewerken van de bestanden in je project" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1439,6 +1767,18 @@ "value": "Referentie" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1546,6 +1886,26 @@ "value": "Zoeken" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2274,6 +2634,42 @@ "value": "Software versies" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2286,6 +2682,18 @@ "value": "Ondersteuning" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2410,6 +2818,12 @@ "value": "Zet het opgeslagen hex-bestand over naar micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2486,6 +2900,12 @@ "value": "Gebruikershandleiding" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.pl.json b/src/messages/ui.pl.json index a72d7986d..59550456d 100644 --- a/src/messages/ui.pl.json +++ b/src/messages/ui.pl.json @@ -65,6 +65,24 @@ "value": "Dostępność" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -181,6 +199,32 @@ "value": "Anuluj" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -471,12 +515,30 @@ "value": "Utwórz plik" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "Utwórz nowy plik Pythona w tym projekcie" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -503,6 +565,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -537,6 +645,18 @@ "value": "Przeciągnij i upuść" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -627,6 +747,18 @@ "value": "Nazwa nie może zawierać spacji" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -659,6 +791,12 @@ "value": "Wymagana jest aktualizacja oprogramowania firmowego" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -717,6 +855,18 @@ "value": "Pomoc" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -753,6 +903,30 @@ "value": "Prosty" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -765,6 +939,12 @@ "value": "Wypróbuj te projekty, zmodyfikuj je i poczuj się zainspirowany" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -777,6 +957,12 @@ "value": "Język" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1083,6 +1269,18 @@ "value": "Ostrzeżenie: pobrano tylko main.py" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1121,12 +1319,24 @@ "value": " dla Ciebie." } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "Dalej" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1259,6 +1469,12 @@ "value": "Otwórz plik hex lub Pythona lub dodaj inne pliki" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1403,12 +1619,112 @@ "value": "Działania w projekcie" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "Nagłówek projektu" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1421,6 +1737,12 @@ "value": "Nazwa projektu nie może być pusta" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1433,6 +1755,12 @@ "value": "Przejrzyj, utwórz, dodaj i edytuj pliki w projekcie" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1475,6 +1803,18 @@ "value": "Referencje" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1601,6 +1941,26 @@ "value": "Szukaj" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2335,6 +2695,42 @@ "value": "Wersje oprogramowania" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2347,6 +2743,18 @@ "value": "Wsparcie" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2467,6 +2875,12 @@ "value": "Przenieś zapisany plik hex na micro:bita" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2539,6 +2953,12 @@ "value": "Podręcznik użytkownika" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.zh-cn.json b/src/messages/ui.zh-cn.json index d2b9fbd07..0ff968b81 100644 --- a/src/messages/ui.zh-cn.json +++ b/src/messages/ui.zh-cn.json @@ -65,6 +65,24 @@ "value": "辅助功能" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -157,6 +175,32 @@ "value": "取消" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -455,12 +499,30 @@ "value": "创建文件" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "在此项目中创建一份新的 Python 文件" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -487,6 +549,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -521,6 +629,18 @@ "value": "拖放" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -611,6 +731,18 @@ "value": "名称不能包含空格" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -643,6 +775,12 @@ "value": "需要更新固件" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -701,6 +839,18 @@ "value": "帮助" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -737,6 +887,30 @@ "value": "简单" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -749,6 +923,12 @@ "value": "尝试这些项目,修改它们并获得灵感" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -761,6 +941,12 @@ "value": "语言" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1031,6 +1217,18 @@ "value": "警告:只下载了 main.py" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1069,12 +1267,24 @@ "value": " 扩展名。" } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "下一个" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1207,6 +1417,12 @@ "value": "打开一 份 hex 或 Python 文件,或添加其他文件" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1351,12 +1567,112 @@ "value": "项目行动" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "项目标题" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1369,6 +1685,12 @@ "value": "项目名称不能为空" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1381,6 +1703,12 @@ "value": "查看、创建、添加和编辑您项目中的文件" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1423,6 +1751,18 @@ "value": "参考" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1516,6 +1856,26 @@ "value": "搜索​​​​" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2244,6 +2604,42 @@ "value": "软件版本" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2256,6 +2652,18 @@ "value": "帮助" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2380,6 +2788,12 @@ "value": "将保存的 hex 文件传输到 micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2452,6 +2866,12 @@ "value": "用户指南" } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/messages/ui.zh-tw.json b/src/messages/ui.zh-tw.json index 16a70e617..99dba7493 100644 --- a/src/messages/ui.zh-tw.json +++ b/src/messages/ui.zh-tw.json @@ -61,6 +61,24 @@ "value": "無障礙設定" } ], + "accessibility-resource-title": [ + { + "type": 0, + "value": "Accessibility" + } + ], + "add-files-action": [ + { + "type": 0, + "value": "Add file(s)" + } + ], + "add-files-hover": [ + { + "type": 0, + "value": "Add files to this project" + } + ], "added-change": [ { "type": 0, @@ -153,6 +171,32 @@ "value": "取消" } ], + "carousel-role": [ + { + "type": 0, + "value": "carousel" + } + ], + "carousel-slide-label": [ + { + "type": 1, + "value": "slideNum" + }, + { + "type": 0, + "value": " of " + }, + { + "type": 1, + "value": "totalSlides" + } + ], + "carousel-slide-role": [ + { + "type": 0, + "value": "slide" + } + ], "change-files": [ { "type": 0, @@ -451,12 +495,30 @@ "value": "建立檔案" } ], + "create-project-action": [ + { + "type": 0, + "value": "Create" + } + ], + "create-project-dialog-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "create-python": [ { "type": 0, "value": "在本專案中建立新的 Python 檔案" } ], + "createai-taster-lessons-resource-title": [ + { + "type": 0, + "value": "Taster lessons" + } + ], "created-file": [ { "type": 0, @@ -483,6 +545,52 @@ "value": "name" } ], + "delete-project-action": [ + { + "type": 0, + "value": "Delete" + } + ], + "delete-project-confirm-heading": [ + { + "type": 0, + "value": "Delete project" + } + ], + "delete-project-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete ‘" + }, + { + "type": 1, + "value": "project" + }, + { + "type": 0, + "value": "’? This cannot be undone." + } + ], + "delete-projects-confirm-heading": [ + { + "type": 0, + "value": "Delete projects" + } + ], + "delete-projects-confirm-text": [ + { + "type": 0, + "value": "Are you sure you want to delete " + }, + { + "type": 1, + "value": "numProjects" + }, + { + "type": 0, + "value": " projects? This cannot be undone." + } + ], "deleted-file": [ { "type": 0, @@ -517,6 +625,18 @@ "value": "拖放" } ], + "duplicate-project-action": [ + { + "type": 0, + "value": "Duplicate" + } + ], + "duplicate-project-heading": [ + { + "type": 0, + "value": "Name your project" + } + ], "edit-file-action": [ { "type": 0, @@ -607,6 +727,18 @@ "value": "名稱不能包含空格" } ], + "files-tab": [ + { + "type": 0, + "value": "Files" + } + ], + "files-tab-description": [ + { + "type": 0, + "value": "Manage your project files" + } + ], "firmware-update-link": [ { "type": 0, @@ -639,6 +771,12 @@ "value": "韌體需要更新" } ], + "first-lessons-with-python-resource-title": [ + { + "type": 0, + "value": "First lessons with Python and the micro:bit" + } + ], "flash-action": [ { "type": 0, @@ -697,6 +835,18 @@ "value": "幫助" } ], + "help-resources-row-carousel": [ + { + "type": 0, + "value": "Help and support" + } + ], + "help-resources-row-title": [ + { + "type": 0, + "value": "Help and support" + } + ], "help-support": [ { "type": 0, @@ -733,6 +883,30 @@ "value": "簡單" } ], + "home-action": [ + { + "type": 0, + "value": "Home" + } + ], + "homepage-banner-heading": [ + { + "type": 0, + "value": "Code your BBC micro:bit with Python" + } + ], + "homepage-banner-subtitle": [ + { + "type": 0, + "value": "Create, edit and flash MicroPython programs to your micro:bit." + } + ], + "homepage-title": [ + { + "type": 0, + "value": "micro:bit Python Editor" + } + ], "ideas-tab": [ { "type": 0, @@ -745,6 +919,12 @@ "value": "嘗試這些專案,進行修改並獲得靈感" } ], + "import-file-action": [ + { + "type": 0, + "value": "Import" + } + ], "insert-code-action": [ { "type": 0, @@ -757,6 +937,12 @@ "value": "語言" } ], + "learn-more-action": [ + { + "type": 0, + "value": "Learn more" + } + ], "less-action": [ { "type": 0, @@ -1027,6 +1213,18 @@ "value": "警告:只下載了 main.py" } ], + "my-projects-row-carousel": [ + { + "type": 0, + "value": "Your projects" + } + ], + "my-projects-row-title": [ + { + "type": 0, + "value": "Your projects" + } + ], "name-project": [ { "type": 0, @@ -1065,12 +1263,24 @@ "value": " 副檔名。" } ], + "new-project-action": [ + { + "type": 0, + "value": "New project" + } + ], "next-action": [ { "type": 0, "value": "下一個" } ], + "no-projects": [ + { + "type": 0, + "value": "No projects found" + } + ], "not-found-checklist-one": [ { "type": 0, @@ -1203,6 +1413,12 @@ "value": "開啟 Hex 或 Python 檔案,或是新增其他檔案" } ], + "open-project-action": [ + { + "type": 0, + "value": "Open" + } + ], "options": [ { "type": 0, @@ -1347,12 +1563,112 @@ "value": "專案動作" } ], + "project-file-count": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " file" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], "project-header": [ { "type": 0, "value": "專案標頭" } ], + "project-idea-animated-animals-title": [ + { + "type": 0, + "value": "Animated animals" + } + ], + "project-idea-beating-heart-title": [ + { + "type": 0, + "value": "Beating heart" + } + ], + "project-idea-emotion-badge-title": [ + { + "type": 0, + "value": "Emotion badge" + } + ], + "project-idea-flashing-emotions-title": [ + { + "type": 0, + "value": "Flashing emotions" + } + ], + "project-idea-get-silly-title": [ + { + "type": 0, + "value": "Get silly" + } + ], + "project-idea-heart-title": [ + { + "type": 0, + "value": "Heart" + } + ], + "project-ideas-row-carousel": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-ideas-row-title": [ + { + "type": 0, + "value": "Project ideas" + } + ], + "project-loaded": [ + { + "type": 0, + "value": "Project loaded" + } + ], + "project-menu-action": [ + { + "type": 0, + "value": "Actions for project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], "project-name": [ { "type": 0, @@ -1365,6 +1681,12 @@ "value": "專案名稱不可為空白" } ], + "project-skip-to-toolbar": [ + { + "type": 0, + "value": "Skip to selection actions" + } + ], "project-tab": [ { "type": 0, @@ -1377,6 +1699,12 @@ "value": "檢視、建立、新增和編輯專案中的檔案" } ], + "projects-page-title": [ + { + "type": 0, + "value": "Projects" + } + ], "python-powered": [ { "type": 0, @@ -1419,6 +1747,18 @@ "value": "參考資料" } ], + "rename-project-action": [ + { + "type": 0, + "value": "Rename" + } + ], + "rename-project-heading": [ + { + "type": 0, + "value": "Rename project" + } + ], "replace-action-label": [ { "type": 0, @@ -1523,6 +1863,26 @@ "value": "搜尋" } ], + "select-project-action": [ + { + "type": 0, + "value": "Select project ‘" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "’" + } + ], + "selection-actions-group": [ + { + "type": 0, + "value": "Actions for selected projects" + } + ], "send-action": [ { "type": 0, @@ -2243,6 +2603,42 @@ "value": "軟體版本" } ], + "sort-option-last-modified": [ + { + "type": 0, + "value": "Last modified" + } + ], + "sort-option-name": [ + { + "type": 0, + "value": "Name" + } + ], + "sort-option-relevance": [ + { + "type": 0, + "value": "Relevance" + } + ], + "sort-order-ascending-label": [ + { + "type": 0, + "value": "Sort ascending" + } + ], + "sort-order-descending-label": [ + { + "type": 0, + "value": "Sort descending" + } + ], + "sort-select-label": [ + { + "type": 0, + "value": "Sort projects by" + } + ], "start-coding-action": [ { "type": 0, @@ -2255,6 +2651,18 @@ "value": "支援" } ], + "teacher-resources-row-carousel": [ + { + "type": 0, + "value": "Teacher resources" + } + ], + "teacher-resources-row-title": [ + { + "type": 0, + "value": "Teacher resources" + } + ], "terms-of-use": [ { "type": 0, @@ -2379,6 +2787,12 @@ "value": "將儲存的 HEX 檔案傳輸至 micro:bit" } ], + "troubleshooting-resource-title": [ + { + "type": 0, + "value": "Troubleshooting" + } + ], "try-again-action": [ { "type": 0, @@ -2451,6 +2865,12 @@ "value": "用戶指南 " } ], + "view-all-projects": [ + { + "type": 0, + "value": "View all projects" + } + ], "visit-dot-org": [ { "type": 0, diff --git a/src/project/OpenButton.tsx b/src/project/AddFilesButton.tsx similarity index 62% rename from src/project/OpenButton.tsx rename to src/project/AddFilesButton.tsx index 16a4e54dd..f68e31328 100644 --- a/src/project/OpenButton.tsx +++ b/src/project/AddFilesButton.tsx @@ -3,35 +3,35 @@ * * SPDX-License-Identifier: MIT */ -import { RiFolderOpenLine } from "react-icons/ri"; +import { RiFileAddLine } from "react-icons/ri"; import { useIntl } from "react-intl"; import { CollapsibleButtonComposableProps } from "../common/CollapsibleButton"; import FileInputButton from "../common/FileInputButton"; import { useProjectActions } from "./project-hooks"; -interface OpenButtonProps extends CollapsibleButtonComposableProps {} +interface AddFilesButtonProps extends CollapsibleButtonComposableProps {} /** - * Open HEX button, with an associated input field. + * Add file(s) button, with an associated input field. */ -const OpenButton = ({ children, ...props }: OpenButtonProps) => { +const AddFilesButton = ({ children, ...props }: AddFilesButtonProps) => { const actions = useProjectActions(); const intl = useIntl(); return ( } + icon={} tooltip={intl.formatMessage({ - id: "open-hover", + id: "add-files-hover", })} /> ); }; -export default OpenButton; +export default AddFilesButton; diff --git a/src/project/FileRow.tsx b/src/project/FileRow.tsx index 6c1c2a413..f6dfc3639 100644 --- a/src/project/FileRow.tsx +++ b/src/project/FileRow.tsx @@ -17,7 +17,7 @@ import { import { MdMoreVert } from "react-icons/md"; import { RiDeleteBin2Line, RiDownload2Line, RiEdit2Line } from "react-icons/ri"; import { FormattedMessage, useIntl } from "react-intl"; -import { zIndexProjectAreaMenu } from "../common/zIndex"; +import { zIndexFilesAreaMenu } from "../common/zIndex"; import { FileVersion, MAIN_FILE } from "../fs/fs"; import { useProjectActions } from "./project-hooks"; import { isEditableFile } from "./project-utils"; @@ -62,7 +62,7 @@ const FileRow = ({ projectName, value, onEdit, ...props }: FileRowProps) => { color="grey.800" /> - + } isDisabled={!isEditableFile(name)} diff --git a/src/project/FilesArea.tsx b/src/project/FilesArea.tsx new file mode 100644 index 000000000..559da7128 --- /dev/null +++ b/src/project/FilesArea.tsx @@ -0,0 +1,81 @@ +/** + * (c) 2021, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Flex, List, ListItem } from "@chakra-ui/react"; +import { useIntl } from "react-intl"; +import AreaHeading from "../common/AreaHeading"; +import HeadedScrollablePanel from "../common/HeadedScrollablePanel"; +import FileRow from "./FileRow"; +import { useProject } from "./project-hooks"; +import { isEditableFile } from "./project-utils"; +import FilesAreaNav from "./FilesAreaNav"; + +interface FilesAreaProps { + selectedFile: string | undefined; + onSelectedFileChanged: (name: string) => void; +} + +/** + * The main files area, offering access to individual files. + */ +const FilesArea = ({ selectedFile, onSelectedFileChanged }: FilesAreaProps) => { + const { files, name: projectName } = useProject(); + const intl = useIntl(); + return ( + + + } + > + + {files.map((f) => { + const selected = selectedFile === f.name; + const select = () => { + if (isEditableFile(f.name)) { + onSelectedFileChanged(f.name); + } + }; + return ( + + { + // Clicks on buttons in the row shouldn't select the row. + if (e.target === e.currentTarget) { + select(); + } + }} + height={12} + value={f} + projectName={projectName} + onEdit={select} + /> + + ); + })} + + + + + ); +}; + +export default FilesArea; diff --git a/src/project/FilesAreaNav.tsx b/src/project/FilesAreaNav.tsx new file mode 100644 index 000000000..9acfb45c7 --- /dev/null +++ b/src/project/FilesAreaNav.tsx @@ -0,0 +1,21 @@ +/** + * (c) 2021, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { BoxProps, Flex, VStack } from "@chakra-ui/react"; +import AddFilesButton from "./AddFilesButton"; +import NewButton from "./NewButton"; + +const FilesAreaNav = (props: BoxProps) => { + return ( + + + + + + + ); +}; + +export default FilesAreaNav; diff --git a/src/project/ProjectActionBar.tsx b/src/project/ProjectActionBar.tsx index b8ea31873..5432f3173 100644 --- a/src/project/ProjectActionBar.tsx +++ b/src/project/ProjectActionBar.tsx @@ -6,7 +6,6 @@ import { BoxProps, HStack, useMediaQuery } from "@chakra-ui/react"; import SendButton from "./SendButton"; import SaveMenuButton from "./SaveMenuButton"; -import OpenButton from "./OpenButton"; import { widthXl } from "../common/media-queries"; import React, { ForwardedRef } from "react"; @@ -31,8 +30,6 @@ const ProjectActionBar = React.forwardRef( - {/* Min-width to avoid collapsing when out of space. Needs some work on responsiveness of the action bar. */} - ); diff --git a/src/project/ProjectArea.tsx b/src/project/ProjectArea.tsx deleted file mode 100644 index 14049a80c..000000000 --- a/src/project/ProjectArea.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/** - * (c) 2021, Micro:bit Educational Foundation and contributors - * - * SPDX-License-Identifier: MIT - */ -import { Box, List, ListItem, Text, VStack } from "@chakra-ui/react"; -import { FormattedMessage } from "react-intl"; -import FileRow from "./FileRow"; -import { useProject } from "./project-hooks"; -import { isEditableFile } from "./project-utils"; -import ProjectAreaNav from "./ProjectAreaNav"; -import ProjectNameEditable from "./ProjectNameEditable"; - -interface ProjectAreaProps { - selectedFile: string | undefined; - onSelectedFileChanged: (name: string) => void; -} - -/** - * The main files area, offering access to individual files. - */ -const ProjectArea = ({ - selectedFile, - onSelectedFileChanged, -}: ProjectAreaProps) => { - const { files, name: projectName } = useProject(); - return ( - - - - - - - - - - {files.map((f) => { - const selected = selectedFile === f.name; - const select = () => { - if (isEditableFile(f.name)) { - onSelectedFileChanged(f.name); - } - }; - return ( - - { - // Clicks on buttons in the row shouldn't select the row. - if (e.target === e.currentTarget) { - select(); - } - }} - height={12} - value={f} - projectName={projectName} - onEdit={select} - /> - - ); - })} - - - - ); -}; - -export default ProjectArea; diff --git a/src/project/ProjectAreaNav.tsx b/src/project/ProjectAreaNav.tsx deleted file mode 100644 index b7eaac00d..000000000 --- a/src/project/ProjectAreaNav.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/** - * (c) 2021, Micro:bit Educational Foundation and contributors - * - * SPDX-License-Identifier: MIT - */ -import { Box, BoxProps, Flex, VStack } from "@chakra-ui/react"; -import NewButton from "./NewButton"; -import OpenButton from "./OpenButton"; -import ResetButton from "./ResetButton"; - -const ProjectAreaNav = (props: BoxProps) => { - return ( - - - - - - - - - - ); -}; - -export default ProjectAreaNav; diff --git a/src/project/ProjectDropTarget.tsx b/src/project/ProjectDropTarget.tsx index df16d868b..7ec227658 100644 --- a/src/project/ProjectDropTarget.tsx +++ b/src/project/ProjectDropTarget.tsx @@ -6,19 +6,19 @@ import { BoxProps } from "@chakra-ui/react"; import { useCallback } from "react"; import FileDropTarget from "../common/FileDropTarget"; -import { useProjectActions } from "./project-hooks"; +import { useImportProject } from "./use-import-project"; interface ProjectDropTargetProps extends BoxProps { children: React.ReactElement; } const ProjectDropTarget = ({ children, ...props }: ProjectDropTargetProps) => { - const actions = useProjectActions(); + const importProject = useImportProject(); const handleDrop = useCallback( (files: File[]) => { - actions.load(files, "drop-load"); + void importProject(files, "drop-load"); }, - [actions] + [importProject] ); return ( + window.sessionStorage.getItem(CURRENT_PROJECT_KEY) ?? undefined; + +const setStoredCurrentProjectId = (id: string | undefined) => { + if (id) { + window.sessionStorage.setItem(CURRENT_PROJECT_KEY, id); + } else { + window.sessionStorage.removeItem(CURRENT_PROJECT_KEY); + } +}; + +/** + * A stable serialization of a project's syncable content, used to detect + * whether the file system state actually differs from what is stored. This + * lets autosave skip no-op writes and, crucially, stops a cross-tab reload + * from bouncing back out as a fresh change. + */ +const serializeProject = ( + name: string | undefined, + files: Record +): string => { + const keys = Object.keys(files).sort(); + return JSON.stringify({ + name: name ?? null, + files: keys.map((k) => [k, files[k]]), + }); +}; + +export interface ProjectsContextValue { + /** All projects in the library, most recently modified first. */ + projects: ProjectRecord[]; + /** True until the initial load from IndexedDB has completed. */ + loading: boolean; + /** The id of the project currently persisted as "open" (session scoped). */ + currentProjectId: string | undefined; + /** The id of the project currently loaded into the editor, if any. */ + loadedProjectId: string | undefined; + /** Reload the project list from storage. */ + refresh: () => Promise; + /** Create a new project, load it into the editor and return its id. */ + createProject: (name: string) => Promise; + /** + * Create a new project whose contents come from a single file-system + * mutation (e.g. loading a hex), and load it into the editor. + * + * Unlike {@link createProject} this performs exactly one file-system change, + * so the editor transitions straight from the previous project to the + * imported one with no intermediate "default project" flash. The previously + * open project is left untouched. Rejects (rolling back the new project) if + * `populateFs` throws. + * + * @param name Fallback project name if the content doesn't specify one. + * @param populateFs Replaces the file system with the imported content. + */ + importProject: ( + name: string, + populateFs: () => Promise + ) => Promise; + /** + * Load a project into the editor working copy. + * Returns false if the project no longer exists. + */ + openProject: (id: string) => Promise; + renameProject: (id: string, name: string) => Promise; + duplicateProject: (id: string, name: string) => Promise; + deleteProject: (id: string) => Promise; +} + +const ProjectsContext = createContext( + undefined +); + +const byTimestampDesc = (a: ProjectRecord, b: ProjectRecord) => + b.timestamp - a.timestamp; + +export const ProjectsProvider = ({ children }: { children: ReactNode }) => { + const fs = useFileSystem(); + const intl = useIntl(); + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(true); + const [currentProjectId, setCurrentProjectId] = useState( + getStoredCurrentProjectId + ); + // The project currently loaded into the in-memory file system. Autosave is + // only active for this project so that a page load (which initialises the + // file system with a default program) can't overwrite a stored project. + const [loadedProjectId, setLoadedProjectId] = useState( + undefined + ); + // Serialization of the last content we loaded or saved for the loaded + // project, used to suppress redundant/echoed autosaves. + const lastSyncedRef = useRef<{ id: string; serialized: string } | null>(null); + // Shows a full-screen spinner while importing a project (e.g. from a hex). + const [importing, setImporting] = useState(false); + + const untitled = intl.formatMessage({ id: "untitled-project" }); + + const refresh = useCallback(async () => { + const all = await projectsDb.getAll(); + all.sort(byTimestampDesc); + setProjects(all); + }, []); + + const setCurrent = useCallback((id: string | undefined) => { + setStoredCurrentProjectId(id); + setCurrentProjectId(id); + }, []); + + const loadIntoFs = useCallback( + async (record: ProjectRecord) => { + await fs.replaceWithMultipleFiles({ + files: record.files, + projectName: record.name, + }); + lastSyncedRef.current = { + id: record.id, + serialized: serializeProject(record.name, record.files), + }; + setLoadedProjectId(record.id); + }, + [fs] + ); + + // Seed / migrate on first load. If the library is empty we migrate any + // existing single-project work (held by the file system's session storage) + // into IndexedDB as the first project. + useEffect(() => { + let cancelled = false; + (async () => { + const all = await projectsDb.getAll(); + if (all.length === 0) { + await fs.initialize(); + const existing = await fs.getPythonProject(); + if (Object.keys(existing.files).length > 0) { + const record: ProjectRecord = { + id: generateId(), + name: existing.projectName ?? untitled, + files: existing.files, + timestamp: Date.now(), + }; + await projectsDb.put(record); + } + } + if (!cancelled) { + await refresh(); + setLoading(false); + } + })(); + return () => { + cancelled = true; + }; + // Intentionally only run once on mount. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const createProject = useCallback( + async (name: string) => { + const record: ProjectRecord = { + id: generateId(), + name, + files: { ...defaultInitialProject.files }, + timestamp: Date.now(), + }; + await projectsDb.put(record); + setCurrent(record.id); + await loadIntoFs(record); + await refresh(); + postProjectSync({ + type: ProjectSyncMessageType.ReloadProject, + projectIds: [record.id], + }); + return record.id; + }, + [loadIntoFs, refresh, setCurrent] + ); + + const importProject = useCallback( + async (name: string, populateFs: () => Promise) => { + const id = generateId(); + // Point the current/loaded project at the new id *before* mutating the + // file system, so autosave targets the new project and can never write + // the imported content back over the previously open one. + setCurrent(id); + setLoadedProjectId(id); + lastSyncedRef.current = null; + // The overlay hides the file-system transition (previous project -> + // imported project) and the navigation that follows. + setImporting(true); + try { + try { + await populateFs(); + } catch (e) { + // Roll back: nothing was persisted under this id yet. + await projectsDb.delete(id); + setLoadedProjectId(undefined); + setCurrent(undefined); + throw e; + } + const pythonProject = await fs.getPythonProject(); + const record: ProjectRecord = { + id, + name: pythonProject.projectName ?? name, + files: pythonProject.files, + timestamp: Date.now(), + }; + await projectsDb.put(record); + lastSyncedRef.current = { + id, + serialized: serializeProject(record.name, record.files), + }; + await refresh(); + postProjectSync({ + type: ProjectSyncMessageType.ReloadProject, + projectIds: [id], + }); + return id; + } finally { + setImporting(false); + } + }, + [fs, refresh, setCurrent] + ); + + const openProject = useCallback( + async (id: string) => { + if (loadedProjectId === id) { + setCurrent(id); + return true; + } + const record = await projectsDb.get(id); + if (!record) { + return false; + } + setCurrent(id); + await loadIntoFs(record); + // Bump recency. Deliberately not broadcast: it must not cause other tabs + // editing the same project to reload and lose in-flight edits. + await projectsDb.put({ ...record, timestamp: Date.now() }); + await refresh(); + return true; + }, + [loadIntoFs, loadedProjectId, refresh, setCurrent] + ); + + const renameProject = useCallback( + async (id: string, name: string) => { + const record = await projectsDb.get(id); + if (!record) { + return; + } + await projectsDb.put({ ...record, name, timestamp: Date.now() }); + if (id === loadedProjectId) { + await fs.setProjectName(name); + lastSyncedRef.current = { + id, + serialized: serializeProject(name, record.files), + }; + } + await refresh(); + postProjectSync({ + type: ProjectSyncMessageType.ReloadProject, + projectIds: [id], + }); + }, + [fs, loadedProjectId, refresh] + ); + + const duplicateProject = useCallback( + async (id: string, name: string) => { + const record = await projectsDb.get(id); + if (!record) { + return id; + } + const newRecord: ProjectRecord = { + id: generateId(), + name, + files: { ...record.files }, + timestamp: Date.now(), + }; + await projectsDb.put(newRecord); + await refresh(); + postProjectSync({ + type: ProjectSyncMessageType.ReloadProject, + projectIds: [newRecord.id], + }); + return newRecord.id; + }, + [refresh] + ); + + const deleteProject = useCallback( + async (id: string) => { + await projectsDb.delete(id); + if (id === currentProjectId) { + setCurrent(undefined); + } + if (id === loadedProjectId) { + setLoadedProjectId(undefined); + lastSyncedRef.current = null; + } + await refresh(); + postProjectSync({ + type: ProjectSyncMessageType.DeleteProject, + projectIds: [id], + }); + }, + [currentProjectId, loadedProjectId, refresh, setCurrent] + ); + + // Autosave the currently loaded project back to IndexedDB as it is edited, + // and broadcast the change to other tabs. + useEffect(() => { + if (!loadedProjectId) { + return; + } + const save = debounce(async () => { + const pythonProject = await fs.getPythonProject(); + const serialized = serializeProject( + pythonProject.projectName, + pythonProject.files + ); + if ( + lastSyncedRef.current?.id === loadedProjectId && + lastSyncedRef.current.serialized === serialized + ) { + // No real change (e.g. this edit came from loading a cross-tab update). + return; + } + const existing = await projectsDb.get(loadedProjectId); + await projectsDb.put({ + id: loadedProjectId, + name: pythonProject.projectName ?? existing?.name ?? untitled, + files: pythonProject.files, + timestamp: Date.now(), + }); + lastSyncedRef.current = { id: loadedProjectId, serialized }; + postProjectSync({ + type: ProjectSyncMessageType.ReloadProject, + projectIds: [loadedProjectId], + }); + }, 1_000); + fs.addEventListener("project_updated", save); + fs.addEventListener("file_text_updated", save); + return () => { + fs.removeEventListener("project_updated", save); + fs.removeEventListener("file_text_updated", save); + save.cancel(); + }; + }, [fs, loadedProjectId, untitled]); + + // React to changes broadcast from other tabs. + useEffect(() => { + return addProjectSyncListener((message) => { + void (async () => { + // Keep the project library list current everywhere. + await refresh(); + if (!loadedProjectId || !message.projectIds.includes(loadedProjectId)) { + return; + } + if (message.type === ProjectSyncMessageType.ReloadProject) { + const record = await projectsDb.get(loadedProjectId); + if (record) { + // Reloads editor content; dedup ref prevents this echoing back out. + await loadIntoFs(record); + } + } else if (message.type === ProjectSyncMessageType.DeleteProject) { + // The open project was deleted elsewhere. Clearing loadedProjectId + // makes ProjectGate redirect back to the home page. + setLoadedProjectId(undefined); + setCurrent(undefined); + lastSyncedRef.current = null; + } + })(); + }); + }, [loadIntoFs, loadedProjectId, refresh, setCurrent]); + + const value = useMemo( + () => ({ + projects, + loading, + currentProjectId, + loadedProjectId, + refresh, + createProject, + importProject, + openProject, + renameProject, + duplicateProject, + deleteProject, + }), + [ + projects, + loading, + currentProjectId, + loadedProjectId, + refresh, + createProject, + importProject, + openProject, + renameProject, + duplicateProject, + deleteProject, + ] + ); + + return ( + + + {children} + + ); +}; + +export const useProjects = (): ProjectsContextValue => { + const value = useContext(ProjectsContext); + if (!value) { + throw new Error("Missing ProjectsProvider"); + } + return value; +}; + +export { getStoredCurrentProjectId }; diff --git a/src/project/use-import-project.ts b/src/project/use-import-project.ts new file mode 100644 index 000000000..2124e30e2 --- /dev/null +++ b/src/project/use-import-project.ts @@ -0,0 +1,82 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { isMakeCodeForV1Hex } from "@microbit/microbit-universal-hex"; +import { useCallback } from "react"; +import { useIntl } from "react-intl"; +import useActionFeedback from "../common/use-action-feedback"; +import { useFileSystem } from "../fs/fs-hooks"; +import { getLowercaseFileExtension, readFileAsText } from "../fs/fs-util"; +import { router } from "../router"; +import { createEditorUrl } from "../urls"; +import { LoadType } from "./project-actions"; +import { useProjectActions } from "./project-hooks"; +import { useProjects } from "./projects-hooks"; + +const isMakeCodeHex = (hex: string): boolean => { + try { + return isMakeCodeForV1Hex(hex); + } catch { + return false; + } +}; + +/** + * Returns a handler that imports dropped/selected files. + * + * A single hex file becomes a brand new project (leaving the currently open + * project untouched) and we navigate to the editor. Other files fall back to + * the existing load-into-current-project behaviour. + */ +export const useImportProject = () => { + const intl = useIntl(); + const fs = useFileSystem(); + const actions = useProjectActions(); + const actionFeedback = useActionFeedback(); + const { importProject } = useProjects(); + + return useCallback( + async (files: File[], loadType: LoadType = "file-upload") => { + if (files.length === 0) { + return; + } + const isSingleHex = + files.length === 1 && + getLowercaseFileExtension(files[0].name) === "hex"; + if (!isSingleHex) { + // Non-hex files keep the existing behaviour (load into the open project). + void actions.load(files, loadType); + return; + } + + // A hex creates a new project from its contents. Importing in a single + // file-system step (behind the loading overlay) avoids showing the + // previous project, then a default project, then the imported one. + const file = files[0]; + const projectName = file.name.replace(/\.hex$/i, ""); + const hex = await readFileAsText(file); + try { + await importProject(projectName, () => + fs.replaceWithHexContents(projectName, hex) + ); + void router.navigate(createEditorUrl()); + actionFeedback.success({ + title: intl.formatMessage({ id: "project-loaded" }), + }); + } catch (e: any) { + actionFeedback.expectedError({ + title: intl.formatMessage( + { id: "load-error-title" }, + { fileCount: 1 } + ), + description: isMakeCodeHex(hex) + ? intl.formatMessage({ id: "load-error-makecode-info" }) + : e.message, + }); + } + }, + [actionFeedback, actions, fs, importProject, intl] + ); +}; diff --git a/src/router-hooks.tsx b/src/router-hooks.tsx index 0ccf1bac9..9bb8d5f1c 100644 --- a/src/router-hooks.tsx +++ b/src/router-hooks.tsx @@ -1,28 +1,29 @@ /** - * A simple custom router. We don't have pages as such, but the different UI areas - * use query parameters to keep some state, for example to allow navigating back from - * drilling down into the documentation in the side panel or making tab selections. + * Editor routing. * - * Which UI state is encoded into the URL might be subject to change in future - * based on user feedback and discussion. + * The different UI areas (documentation tabs, drill-down anchors) encode some + * of their state in the URL. This used to be a bespoke router; it is now a thin + * compatibility layer over react-router so the rest of the app can keep using + * the same [state, setState] API. * - * (c) 2021-2022, Micro:bit Educational Foundation and contributors + * The editor lives under `/project` with tabs at `/project/` and + * documentation anchors at `/project//`. + * + * (c) 2021-2026, Micro:bit Educational Foundation and contributors * * SPDX-License-Identifier: MIT */ -import { - createContext, - ReactNode, - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from "react"; +import { useCallback, useMemo } from "react"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; import { baseUrl } from "./base"; import { useLogging } from "./logging/logging-hooks"; -export type TabName = "api" | "ideas" | "reference" | "project"; +export type TabName = "api" | "ideas" | "reference" | "files"; + +const tabNames: TabName[] = ["api", "ideas", "reference", "files"]; + +const isTabName = (value: string | undefined): value is TabName => + !!value && (tabNames as string[]).includes(value); /** * An anchor-like navigation used for scroll positions. @@ -33,8 +34,6 @@ export type TabName = "api" | "ideas" | "reference" | "project"; export interface Anchor { id: string; } -const anchorForParam = (param: string | null): Anchor | undefined => - param ? { id: param } : undefined; export interface RouterState { tab?: TabName; @@ -53,85 +52,71 @@ type RouterContextValue = [ (state: RouterState, source?: NavigationSource) => void ]; -const RouterContext = createContext(undefined); +export const editorBasePath = "/project"; -const parse = (pathname: string): RouterState => { - pathname = pathname.slice(baseUrl.length); - if (pathname) { - const parts = pathname.split("/"); - const tab = parts[0]; - if ( - tab === "api" || - tab === "reference" || - tab === "ideas" || - tab === "project" - ) { - return { tab, slug: anchorForParam(parts[1]) }; - } - } - return {}; +const buildEditorPath = (state: RouterState): string => { + const parts = [state.tab, state.slug?.id].filter( + (x): x is string => !!x + ); + return [editorBasePath, ...parts].join("/"); }; +/** + * Build a full href (including the app base URL) for a router state. + * Used for link targets; navigation itself goes through the router. + */ +export const toUrl = (state: RouterState): string => + baseUrl + buildEditorPath(state).replace(/^\//, ""); + /** * The full router state. - * Consider using useRouterParam instead if you only care about one parameter. + * Consider using useRouterTabSlug instead if you only care about one parameter. * * Updating the state updates the URL. * * @return a [state, setState] pair. */ export const useRouterState = (): RouterContextValue => { - const value = useContext(RouterContext); - if (!value) { - throw new Error("Missing provider!"); - } - return value; -}; + const params = useParams(); + const location = useLocation(); + const navigate = useNavigate(); + const logging = useLogging(); -export const toUrl = (state: RouterState): string => { - const parts = [state.tab, state.slug?.id]; - const pathname = baseUrl + parts.filter((x): x is string => !!x).join("/"); - return window.location.toString().split("/", 1)[0] + pathname; -}; + const tab = isTabName(params.tab) ? params.tab : undefined; + const slugId = params.slug; + const focus = (location.state as { focus?: boolean } | null)?.focus ?? false; + // Tie identity to location.key so that navigating to the same anchor again + // (e.g. clicking the same search result twice) produces a fresh state object + // and re-triggers scroll/focus effects, matching the previous behaviour. + const state = useMemo( + () => ({ + tab, + slug: slugId ? { id: slugId } : undefined, + focus, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [tab, slugId, focus, location.key] + ); -export const RouterProvider = ({ children }: { children: ReactNode }) => { - const logging = useLogging(); - const [state, setState] = useState(parse(window.location.pathname)); - useEffect(() => { - // This detects browser navigation but not our programatic changes, - // so we need to update state there ourselves. - const listener = (_: PopStateEvent) => { - const newState = parse(window.location.pathname); - setState(newState); - }; - window.addEventListener("popstate", listener); - return () => { - window.removeEventListener("popstate", listener); - }; - }, [setState]); - const navigate = useCallback( + const setState = useCallback( (newState: RouterState, source?: NavigationSource) => { if (source) { - const parts = [newState.tab, newState.slug?.id]; - const message = parts.filter((x): x is string => !!x).join("-"); + const parts = [newState.tab, newState.slug?.id].filter( + (x): x is string => !!x + ); logging.event({ type: source, - message, + message: parts.join("-"), }); } - const url = toUrl(newState); - window.history.pushState(newState, "", url); - - setState(newState); + navigate(buildEditorPath(newState), { + state: { focus: newState.focus }, + }); }, - [logging, setState] - ); - const value: RouterContextValue = useMemo(() => { - return [state, navigate]; - }, [state, navigate]); - return ( - {children} + [logging, navigate] ); + + return [state, setState]; }; /** diff --git a/src/router.tsx b/src/router.tsx new file mode 100644 index 000000000..ec8772af4 --- /dev/null +++ b/src/router.tsx @@ -0,0 +1,22 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { createBrowserRouter } from "react-router-dom"; +import HomePage from "./homepage/HomePage"; +import ProjectsPage from "./homepage/ProjectsPage"; +import { routerBasename } from "./urls"; +import ProjectGate from "./workbench/ProjectGate"; + +export const router = createBrowserRouter( + [ + { path: "/", element: }, + { path: "/projects", element: }, + { path: "/project", element: }, + { path: "/project/:tab", element: }, + { path: "/project/:tab/:slug", element: }, + { path: "*", element: }, + ], + { basename: routerBasename } +); diff --git a/src/settings/SettingsMenu.tsx b/src/settings/SettingsMenu.tsx index 7f6ed1ed3..e5a2f608e 100644 --- a/src/settings/SettingsMenu.tsx +++ b/src/settings/SettingsMenu.tsx @@ -26,12 +26,13 @@ import { SettingsDialog } from "./SettingsDialog"; interface SettingsMenuProps extends ThemingProps<"Menu"> { size?: ThemeTypings["components"]["Button"]["sizes"]; + fontSize?: string; } /** * The settings button triggers a menu with main and other settings. */ -const SettingsMenu = ({ size, ...props }: SettingsMenuProps) => { +const SettingsMenu = ({ size, fontSize = "xl", ...props }: SettingsMenuProps) => { const languageDisclosure = useDisclosure(); const intl = useIntl(); const dialogs = useDialogs(); @@ -59,7 +60,7 @@ const SettingsMenu = ({ size, ...props }: SettingsMenuProps) => { data-testid="settings" aria-label={intl.formatMessage({ id: "settings" })} size={size} - fontSize="xl" + fontSize={fontSize} variant="sidebar" icon={} color="white" diff --git a/src/urls.ts b/src/urls.ts new file mode 100644 index 000000000..15bb91969 --- /dev/null +++ b/src/urls.ts @@ -0,0 +1,16 @@ +/** + * Internal app routes (relative to the router basename). + * + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { baseUrl } from "./base"; + +/** react-router basename derived from the Vite base URL. */ +export const routerBasename = + baseUrl === "/" ? undefined : baseUrl.replace(/\/$/, ""); + +export const createHomePageUrl = () => "/"; +export const createEditorUrl = () => "/project"; +export const createProjectsPageUrl = () => "/projects"; diff --git a/src/workbench/HelpMenu.tsx b/src/workbench/HelpMenu.tsx index 45940aec2..adcd9ed04 100644 --- a/src/workbench/HelpMenu.tsx +++ b/src/workbench/HelpMenu.tsx @@ -32,12 +32,13 @@ import FeedbackForm from "./FeedbackForm"; interface HelpMenuProps extends ThemingProps<"Menu"> { size?: ThemeTypings["components"]["Button"]["sizes"]; + fontSize?: string; } /** * A help button that triggers a drop-down menu with actions. */ -const HelpMenu = ({ size, ...props }: HelpMenuProps) => { +const HelpMenu = ({ size, fontSize = "xl", ...props }: HelpMenuProps) => { const aboutDialogDisclosure = useDisclosure(); const intl = useIntl(); const dialogs = useDialogs(); @@ -69,7 +70,7 @@ const HelpMenu = ({ size, ...props }: HelpMenuProps) => { as={IconButton} aria-label={intl.formatMessage({ id: "help" })} size={size} - fontSize="xl" + fontSize={fontSize} variant="sidebar" icon={} color="white" diff --git a/src/workbench/ProjectGate.tsx b/src/workbench/ProjectGate.tsx new file mode 100644 index 000000000..93ad76812 --- /dev/null +++ b/src/workbench/ProjectGate.tsx @@ -0,0 +1,66 @@ +/** + * (c) 2026, Micro:bit Educational Foundation and contributors + * + * SPDX-License-Identifier: MIT + */ +import { Flex, Spinner } from "@chakra-ui/react"; +import { useEffect, useRef, useState } from "react"; +import { Navigate } from "react-router-dom"; +import { + getStoredCurrentProjectId, + useProjects, +} from "../project/projects-hooks"; +import Workbench from "./Workbench"; + +type Status = "loading" | "ready" | "missing"; + +/** + * Ensures a project is loaded into the editor before rendering the workbench. + * + * The "current" project id is held in session storage. If it's missing or no + * longer exists we redirect back to the home page. + */ +const ProjectGate = () => { + const { openProject, loadedProjectId } = useProjects(); + const openProjectRef = useRef(openProject); + openProjectRef.current = openProject; + const [status, setStatus] = useState("loading"); + + useEffect(() => { + let cancelled = false; + const id = getStoredCurrentProjectId(); + if (!id) { + setStatus("missing"); + return; + } + openProjectRef.current(id).then((ok) => { + if (!cancelled) { + setStatus(ok ? "ready" : "missing"); + } + }); + return () => { + cancelled = true; + }; + }, []); + + useEffect(() => { + // The open project was deleted (possibly from another tab): go home. + if (status === "ready" && loadedProjectId === undefined) { + setStatus("missing"); + } + }, [status, loadedProjectId]); + + if (status === "missing") { + return ; + } + if (status !== "ready") { + return ( + + + + ); + } + return ; +}; + +export default ProjectGate; diff --git a/src/workbench/SideBar.tsx b/src/workbench/SideBar.tsx index 93f2b678f..24230c7e8 100644 --- a/src/workbench/SideBar.tsx +++ b/src/workbench/SideBar.tsx @@ -25,7 +25,7 @@ import ApiArea from "../documentation/ApiArea"; import IdeasArea from "../documentation/IdeasArea"; import ReferenceArea from "../documentation/ReferenceArea"; import { flags } from "../flags"; -import ProjectArea from "../project/ProjectArea"; +import FilesArea from "../project/FilesArea"; import { TabName, useRouterState } from "../router-hooks"; import SettingsMenu from "../settings/SettingsMenu"; import HelpMenu from "./HelpMenu"; @@ -97,11 +97,11 @@ const SideBar = ({ mb: "auto", }, { - id: "project" as const, - title: intl.formatMessage({ id: "project-tab" }), + id: "files" as const, + title: intl.formatMessage({ id: "files-tab" }), icon: VscFiles, contents: ( - diff --git a/src/workbench/SideBarHeader.tsx b/src/workbench/SideBarHeader.tsx index 64335d74a..1a4cf182e 100644 --- a/src/workbench/SideBarHeader.tsx +++ b/src/workbench/SideBarHeader.tsx @@ -9,9 +9,7 @@ import { Container, Fade, Flex, - HStack, IconButton, - Link, Modal, ModalBody, ModalContent, @@ -22,15 +20,14 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { RiCloseLine, RiSearch2Line } from "react-icons/ri"; import { useIntl } from "react-intl"; import CollapsibleButton from "../common/CollapsibleButton"; +import HomeButton from "../common/HomeButton"; import HideSplitViewButton from "../common/SplitView/HideSplitViewButton"; import { useResizeObserverContentRect } from "../common/use-resize-observer"; import { zIndexSidebarHeader } from "../common/zIndex"; -import { useDeployment } from "../deployment"; import { topBarHeight } from "../deployment/misc"; import { supportedSearchLanguages } from "../documentation/search/search.worker"; import { useSearch } from "../documentation/search/search-hooks"; import SearchDialog from "../documentation/search/SearchDialog"; -import { microbitOrgUrl } from "../external-links"; import { useLogging } from "../logging/logging-hooks"; import { RouterState, useRouterState } from "../router-hooks"; import { useSettings } from "../settings/settings"; @@ -51,7 +48,6 @@ const SideBarHeader = ({ }: SideBarHeaderProps) => { const intl = useIntl(); const logging = useLogging(); - const brand = useDeployment(); const searchModal = useDisclosure(); const { results, query, setQuery } = useSearch(); const [, setRouterState] = useRouterState(); @@ -182,34 +178,19 @@ const SideBarHeader = ({ } alignItems="center" justifyContent="space-between" - pr={4} + pr={sidebarShown ? 4 : 0} transition="height .2s" position="relative" > - - - - {brand.squareLogo} - - {!query && sidebarShown && ( - - {brand.horizontalLogo} - - )} - - + + {searchAvailable && !query && sidebarShown && ( { const intl = useIntl(); + useProjectScopedSelection(); const [maybeInvalidSelection, setSelection] = useSelection(); const { files } = useProject(); const selection = defaultSelection(maybeInvalidSelection, files); diff --git a/src/workbench/use-selection.tsx b/src/workbench/use-selection.tsx index 7834787ba..671e84461 100644 --- a/src/workbench/use-selection.tsx +++ b/src/workbench/use-selection.tsx @@ -8,10 +8,13 @@ import React, { ReactNode, SetStateAction, useContext, + useLayoutEffect, + useRef, useState, } from "react"; import { MAIN_FILE } from "../fs/fs"; +import { useProjects } from "../project/projects-hooks"; const Selection = React.createContext< [WorkbenchSelection, Dispatch>] | undefined >(undefined); @@ -56,3 +59,30 @@ export const SelectionProvider = ({ children }: { children: ReactNode }) => { }); return {children}; }; + +/** + * Resets the (global) workbench selection to main.py whenever a different + * project becomes active, so switching/loading/creating a project always + * opens main.py rather than leaking the previously open file. + * + * Note: this deliberately does not remember the open file per project. See + * team notes if we want to revisit per-project file memory. + * + * Call once from the workbench. + */ +export const useProjectScopedSelection = () => { + const { loadedProjectId } = useProjects(); + const [, setSelection] = useSelection(); + const activeProjectRef = useRef(undefined); + + // useLayoutEffect (not useEffect) so the reset happens before the browser + // paints: reopening a project must not briefly show the previously active + // file before switching back to main.py. + useLayoutEffect(() => { + if (activeProjectRef.current === loadedProjectId) { + return; + } + activeProjectRef.current = loadedProjectId; + setSelection({ file: MAIN_FILE, location: { line: undefined } }); + }, [loadedProjectId, setSelection]); +};