diff --git a/bin/compile-lang.mjs b/bin/compile-lang.mjs index db0506e65..ed22aafab 100644 --- a/bin/compile-lang.mjs +++ b/bin/compile-lang.mjs @@ -21,10 +21,12 @@ import { fileURLToPath } from "node:url"; const require = createRequire(import.meta.url); const root = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); const formatjs = require.resolve("@formatjs/cli/bin/formatjs"); -const packageLangDirs = ["@microbit/ui"].map((name) => ({ - name, - dir: path.dirname(require.resolve(`${name}/lang/ui.en.json`)), -})); +const packageLangDirs = ["@microbit/ui", "@microbit/ui-patterns"].map( + (name) => ({ + name, + dir: path.dirname(require.resolve(`${name}/lang/ui.en.json`)), + }) +); for (const file of fs.readdirSync(path.join(root, "lang")).sort()) { const inputs = [path.join(root, "lang", file)]; diff --git a/lang/ui.ca.json b/lang/ui.ca.json index e9a2d2221..78dd973ff 100644 --- a/lang/ui.ca.json +++ b/lang/ui.ca.json @@ -363,10 +363,6 @@ "defaultMessage": "Ajuda i suport", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Ajuda a traduir", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Ressaltar l'estructura del programa", "description": "Code structure setting label" diff --git a/lang/ui.de.json b/lang/ui.de.json index acd7652e7..70ccd954c 100644 --- a/lang/ui.de.json +++ b/lang/ui.de.json @@ -363,10 +363,6 @@ "defaultMessage": "Hilfe & Support", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Beim Übersetzen helfen", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Codestruktur hervorheben", "description": "Code structure setting label" diff --git a/lang/ui.en.json b/lang/ui.en.json index 8fbd14540..8bc5f5158 100644 --- a/lang/ui.en.json +++ b/lang/ui.en.json @@ -363,10 +363,6 @@ "defaultMessage": "Help & support", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Help translate", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Highlight code structure", "description": "Code structure setting label" diff --git a/lang/ui.es-es.json b/lang/ui.es-es.json index 78f29c704..a1eb2cd5d 100644 --- a/lang/ui.es-es.json +++ b/lang/ui.es-es.json @@ -363,10 +363,6 @@ "defaultMessage": "Ayuda y asistencia", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Ayudar a traducir", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Resaltar la estructura del código", "description": "Code structure setting label" diff --git a/lang/ui.fr.json b/lang/ui.fr.json index 819aa27bd..226195341 100644 --- a/lang/ui.fr.json +++ b/lang/ui.fr.json @@ -363,10 +363,6 @@ "defaultMessage": "Aide et support", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Contribuer à la traduction", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Surligner la structure du code", "description": "Code structure setting label" diff --git a/lang/ui.ga-ie.json b/lang/ui.ga-ie.json index e1d9df167..8c1eb339b 100644 --- a/lang/ui.ga-ie.json +++ b/lang/ui.ga-ie.json @@ -363,10 +363,6 @@ "defaultMessage": "Cabhair & tacaíocht", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Cabhair a aistriú", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Aibhsigh struchtúr an chóid", "description": "Code structure setting label" diff --git a/lang/ui.ja.json b/lang/ui.ja.json index 8b9276680..4aacc1117 100644 --- a/lang/ui.ja.json +++ b/lang/ui.ja.json @@ -363,10 +363,6 @@ "defaultMessage": "ヘルプとサポート", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "翻訳を手伝う", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "コード構造を強調表示", "description": "Code structure setting label" diff --git a/lang/ui.ko.json b/lang/ui.ko.json index 591e48959..71f5b0135 100644 --- a/lang/ui.ko.json +++ b/lang/ui.ko.json @@ -363,10 +363,6 @@ "defaultMessage": "도움말 및 지원", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "번역 지원", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "코드 구조 하이라이트", "description": "Code structure setting label" diff --git a/lang/ui.lol.json b/lang/ui.lol.json index 9281c987d..ded9f608b 100644 --- a/lang/ui.lol.json +++ b/lang/ui.lol.json @@ -363,10 +363,6 @@ "defaultMessage": "crwdns331530:0crwdne331530:0", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "crwdns331532:0crwdne331532:0", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "crwdns331534:0crwdne331534:0", "description": "Code structure setting label" diff --git a/lang/ui.nl.json b/lang/ui.nl.json index eb81ee6e0..e9154aaaa 100644 --- a/lang/ui.nl.json +++ b/lang/ui.nl.json @@ -363,10 +363,6 @@ "defaultMessage": "Hulp & Ondersteuning", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Help met vertalen", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Code structuur markeren", "description": "Code structure setting label" diff --git a/lang/ui.pl.json b/lang/ui.pl.json index 8a275484e..9868d1a96 100644 --- a/lang/ui.pl.json +++ b/lang/ui.pl.json @@ -363,10 +363,6 @@ "defaultMessage": "Pomoc i wsparcie", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "Pomóż tłumaczyć", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "Podświetl strukturę kodu", "description": "Code structure setting label" diff --git a/lang/ui.zh-cn.json b/lang/ui.zh-cn.json index 9cd097b35..535389fe7 100644 --- a/lang/ui.zh-cn.json +++ b/lang/ui.zh-cn.json @@ -363,10 +363,6 @@ "defaultMessage": "帮助与支持", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "帮助翻译", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "突出显示代码结构", "description": "Code structure setting label" diff --git a/lang/ui.zh-tw.json b/lang/ui.zh-tw.json index ca7214958..36f54122d 100644 --- a/lang/ui.zh-tw.json +++ b/lang/ui.zh-tw.json @@ -363,10 +363,6 @@ "defaultMessage": "說明與支援", "description": "Menu item for link to support site" }, - "help-translate": { - "defaultMessage": "說明翻譯", - "description": "Help translate menu option text" - }, "highlight-code-structure": { "defaultMessage": "強調程式碼結構", "description": "Code structure setting label" diff --git a/package-lock.json b/package-lock.json index 541935ab4..e45ab8a66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "@codemirror/view": "^6.26.3", "@microbit/microbit-connection": "^1.0.0", "@microbit/microbit-fs": "^0.10.0", - "@microbit/ui": "0.1.0-alpha.24", + "@microbit/ui": "0.1.0-alpha.26", + "@microbit/ui-patterns": "0.1.0-alpha.3", "@sanity/block-content-to-react": "^3.0.0", "@sanity/image-url": "^1.0.1", "@testing-library/jest-dom": "^5.14.1", @@ -3266,9 +3267,9 @@ } }, "node_modules/@microbit/ui": { - "version": "0.1.0-alpha.24", - "resolved": "https://registry.npmjs.org/@microbit/ui/-/ui-0.1.0-alpha.24.tgz", - "integrity": "sha512-HwzZ3jZCQ8KXGRsnCzksOdLqQJV9/J7BIhSRw46JudZQQazShskGQ6RmZy7udBFVZuBEQabRoYVY50C1u7Grxg==", + "version": "0.1.0-alpha.26", + "resolved": "https://registry.npmjs.org/@microbit/ui/-/ui-0.1.0-alpha.26.tgz", + "integrity": "sha512-h2PGINUBXM3LnmJvTKuvSMqkY32BB8BbRndl4+a3oKAwWW46R7G+6pzHDnQ8YFMVFeD2ElpNyHxA4JOkfyPNhQ==", "license": "MIT", "peerDependencies": { "@pandacss/dev": "^1.11.4", @@ -3278,6 +3279,37 @@ "react-intl": "^6.6.8 || ^7.0.0" } }, + "node_modules/@microbit/ui-patterns": { + "version": "0.1.0-alpha.3", + "resolved": "https://registry.npmjs.org/@microbit/ui-patterns/-/ui-patterns-0.1.0-alpha.3.tgz", + "integrity": "sha512-sP1bWehNUssBiLGn4JqG68dp9AFkxy9BxVKTmXYWv7GH22no2XlRgvg11+7XR0LYJNCAcJ3vBdP7OmB/hukBvg==", + "license": "MIT", + "dependencies": { + "@formatjs/intl-localematcher": "^0.8.0" + }, + "peerDependencies": { + "@microbit/ui": "*", + "@pandacss/dev": "^1.11.4", + "react": "^18.3.1", + "react-icons": "^4.12.0 || ^5.0.0", + "react-intl": "^6.6.8 || ^7.0.0" + } + }, + "node_modules/@microbit/ui-patterns/node_modules/@formatjs/fast-memoize": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz", + "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==", + "license": "MIT" + }, + "node_modules/@microbit/ui-patterns/node_modules/@formatjs/intl-localematcher": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.13.tgz", + "integrity": "sha512-kHEAFOkeJSPNi7c5PaKaRjxcBrJwzzt81ifUu+8uve1EDW/VJl83KsxmqgqNZLzcFEhSliZGvx3+pk/RH0IOmg==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "3.1.7" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", diff --git a/package.json b/package.json index 3927d349d..16dabaf0b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "@codemirror/view": "^6.26.3", "@microbit/microbit-connection": "^1.0.0", "@microbit/microbit-fs": "^0.10.0", - "@microbit/ui": "0.1.0-alpha.24", + "@microbit/ui": "0.1.0-alpha.26", + "@microbit/ui-patterns": "0.1.0-alpha.3", "@sanity/block-content-to-react": "^3.0.0", "@sanity/image-url": "^1.0.1", "@testing-library/jest-dom": "^5.14.1", diff --git a/panda.config.ts b/panda.config.ts index 99f9cd4ae..f8afe2a94 100644 --- a/panda.config.ts +++ b/panda.config.ts @@ -51,6 +51,7 @@ export default defineConfig({ // still works via the preset's staticCss), so a wrong path shows up only as // broken non-recipe styling — check the resolved node_modules path. "./node_modules/@microbit/ui/src/**/*.{ts,tsx}", + "./node_modules/@microbit/ui-patterns/src/**/*.{ts,tsx}", ], outdir: "styled-system", }); diff --git a/src/language-server/pyright.ts b/src/language-server/pyright.ts index fc99e241f..026081e8a 100644 --- a/src/language-server/pyright.ts +++ b/src/language-server/pyright.ts @@ -33,6 +33,9 @@ export const pyright = async ( language: string, toast: ToastFn ): Promise => { + // The typeshed..json imports and the server's locale are + // lowercase; language ids use canonical BCP 47 casing. + language = language.toLowerCase(); // For jsdom. if (!window.Worker) { return undefined; diff --git a/src/messages/TranslationProvider.tsx b/src/messages/TranslationProvider.tsx index 418f87e1a..34a36c436 100644 --- a/src/messages/TranslationProvider.tsx +++ b/src/messages/TranslationProvider.tsx @@ -14,7 +14,8 @@ import { import { useToast } from "@microbit/ui"; async function loadLocaleData(locale: string) { - switch (locale) { + // Language ids use canonical BCP 47 casing; the catalog files are lowercase. + switch (locale.toLowerCase()) { // Add further cases explicitly for code splitting. // The need for this might be worth revisiting. case "ca": diff --git a/src/messages/ui.ca.json b/src/messages/ui.ca.json index 9a4bf3218..536f503f9 100644 --- a/src/messages/ui.ca.json +++ b/src/messages/ui.ca.json @@ -697,12 +697,6 @@ "value": "Ajuda i suport" } ], - "help-translate": [ - { - "type": 0, - "value": "Ajuda a traduir" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2367,6 +2361,60 @@ "value": "Torna-ho a provar" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Ajuda a traduir" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Idioma" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Completament compatible" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Parcialment compatible" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Compatible" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Incompatible" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Traduccions compatibles per a:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "L'idioma no està disponible totalment" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2397,6 +2445,12 @@ "value": "S'està carregant" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.de.json b/src/messages/ui.de.json index 065d88531..72a10e4fc 100644 --- a/src/messages/ui.de.json +++ b/src/messages/ui.de.json @@ -747,12 +747,6 @@ "value": "Hilfe & Support" } ], - "help-translate": [ - { - "type": 0, - "value": "Beim Übersetzen helfen" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2457,6 +2451,60 @@ "value": "Erneut versuchen" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Beim Übersetzen helfen" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Sprache" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Vollständig unterstützt" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Teilweise unterstützt" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Unterstützt" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Nicht unterstützt" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Übersetzungen unterstützt für:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Sprache nicht vollständig unterstützt" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2487,6 +2535,12 @@ "value": "Laden" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 1, diff --git a/src/messages/ui.en.json b/src/messages/ui.en.json index 2369c244a..2d1f376a1 100644 --- a/src/messages/ui.en.json +++ b/src/messages/ui.en.json @@ -707,12 +707,6 @@ "value": "Help & support" } ], - "help-translate": [ - { - "type": 0, - "value": "Help translate" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2409,6 +2403,60 @@ "value": "Try again" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Help translate" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Language" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Fully supported" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Partially supported" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Supported" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Unsupported" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Translations supported for:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Language not fully supported" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2439,6 +2487,12 @@ "value": "Loading" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.es-es.json b/src/messages/ui.es-es.json index 68f9d0f86..66e9eda59 100644 --- a/src/messages/ui.es-es.json +++ b/src/messages/ui.es-es.json @@ -711,12 +711,6 @@ "value": "Ayuda y asistencia" } ], - "help-translate": [ - { - "type": 0, - "value": "Ayudar a traducir" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2397,6 +2391,60 @@ "value": "Inténtalo de nuevo" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Ayudar a traducir" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Idioma" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Totalmente compatible" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Parcialmente compatible" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Compatible" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "No compatible" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Traducciones compatibles con:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Idioma no es totalmente compatible" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2427,6 +2475,12 @@ "value": "Cargando" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.fr.json b/src/messages/ui.fr.json index 0c8b259c9..4d4f144fc 100644 --- a/src/messages/ui.fr.json +++ b/src/messages/ui.fr.json @@ -707,12 +707,6 @@ "value": "Aide et support" } ], - "help-translate": [ - { - "type": 0, - "value": "Contribuer à la traduction" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2397,6 +2391,60 @@ "value": "Réessayer" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Contribuer à la traduction" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Langue" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Entièrement pris en charge" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Partiellement pris en charge" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Pris en charge" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Non pris en charge" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Traductions prises en charge pour :" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "La langue n'est pas entièrement prise en charge" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2427,6 +2475,12 @@ "value": "Chargement" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.ga-ie.json b/src/messages/ui.ga-ie.json index a1f654172..182206527 100644 --- a/src/messages/ui.ga-ie.json +++ b/src/messages/ui.ga-ie.json @@ -755,12 +755,6 @@ "value": "Cabhair & tacaíocht" } ], - "help-translate": [ - { - "type": 0, - "value": "Cabhair a aistriú" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2547,6 +2541,60 @@ "value": "Bain triail as arís" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Cabhair a aistriú" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Teanga" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Fully supported" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Partially supported" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Supported" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Unsupported" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Translations supported for:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Language not fully supported" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2577,6 +2625,12 @@ "value": "Ag lódáil" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.ja.json b/src/messages/ui.ja.json index 7bf1113b4..ffac65c0e 100644 --- a/src/messages/ui.ja.json +++ b/src/messages/ui.ja.json @@ -735,12 +735,6 @@ "value": "ヘルプとサポート" } ], - "help-translate": [ - { - "type": 0, - "value": "翻訳を手伝う" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2391,6 +2385,60 @@ "value": "もう一度やり直す" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "翻訳を手伝う" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "言語" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "完全に対応" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "部分的に対応" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "対応" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "非対応" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "サポートされている翻訳:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "言語が完全にサポートされていません" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2421,6 +2469,12 @@ "value": "読み込み中" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 1, diff --git a/src/messages/ui.ko.json b/src/messages/ui.ko.json index db96d7d8a..07aaa44ab 100644 --- a/src/messages/ui.ko.json +++ b/src/messages/ui.ko.json @@ -719,12 +719,6 @@ "value": "도움말 및 지원" } ], - "help-translate": [ - { - "type": 0, - "value": "번역 지원" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2397,6 +2391,60 @@ "value": "다시 시도" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "번역 지원" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "언어 선택" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "완벽 지원" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "일부 지원" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "지원됨" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "지원되지 않음" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "번역 지원 언어:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "완전히 지원되지 않는 언어" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2427,6 +2475,12 @@ "value": "불러오는 중..." } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 1, diff --git a/src/messages/ui.lol.json b/src/messages/ui.lol.json index f54167101..d3c81018d 100644 --- a/src/messages/ui.lol.json +++ b/src/messages/ui.lol.json @@ -697,12 +697,6 @@ "value": "crwdns331530:0crwdne331530:0" } ], - "help-translate": [ - { - "type": 0, - "value": "crwdns331532:0crwdne331532:0" - } - ], "highlight-code-structure": [ { "type": 0, @@ -1983,6 +1977,60 @@ "value": "crwdns331770:0crwdne331770:0" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Help translate" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Language" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Fully supported" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Partially supported" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Supported" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Unsupported" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Translations supported for:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Language not fully supported" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2013,6 +2061,12 @@ "value": "Loading" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.nl.json b/src/messages/ui.nl.json index a69bbc03e..b0f5ff1fe 100644 --- a/src/messages/ui.nl.json +++ b/src/messages/ui.nl.json @@ -719,12 +719,6 @@ "value": "Hulp & Ondersteuning" } ], - "help-translate": [ - { - "type": 0, - "value": "Help met vertalen" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2439,6 +2433,60 @@ "value": "Probeer het opnieuw" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Help met vertalen" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Taal" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Volledig ondersteund" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Gedeeltelijk ondersteund" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Ondersteund" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Niet ondersteund" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Vertalingen ondersteund voor:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Taal niet volledig ondersteund" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2469,6 +2517,12 @@ "value": "Laden" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.pl.json b/src/messages/ui.pl.json index 4d9086197..5c0559c61 100644 --- a/src/messages/ui.pl.json +++ b/src/messages/ui.pl.json @@ -723,12 +723,6 @@ "value": "Pomoc i wsparcie" } ], - "help-translate": [ - { - "type": 0, - "value": "Pomóż tłumaczyć" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2495,6 +2489,60 @@ "value": "Spróbuj ponownie" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "Pomóż tłumaczyć" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "Język" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "W pełni wspierane" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Częściowo wspierane" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Wspierane" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Niewspierany" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Tłumaczenia wspierane dla:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Język nie w pełni obsługiwany" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2525,6 +2573,12 @@ "value": "Ładowanie" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.zh-cn.json b/src/messages/ui.zh-cn.json index 1b7b767da..088c92975 100644 --- a/src/messages/ui.zh-cn.json +++ b/src/messages/ui.zh-cn.json @@ -707,12 +707,6 @@ "value": "帮助与支持" } ], - "help-translate": [ - { - "type": 0, - "value": "帮助翻译" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2410,6 +2404,60 @@ "value": "重试" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "帮助翻译" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "语言" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "Fully supported" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "Partially supported" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "Supported" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "Unsupported" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "Translations supported for:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "Language not fully supported" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2440,6 +2488,12 @@ "value": "正在加载" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/messages/ui.zh-tw.json b/src/messages/ui.zh-tw.json index aad0335b2..a009a1496 100644 --- a/src/messages/ui.zh-tw.json +++ b/src/messages/ui.zh-tw.json @@ -703,12 +703,6 @@ "value": "說明與支援" } ], - "help-translate": [ - { - "type": 0, - "value": "說明翻譯" - } - ], "highlight-code-structure": [ { "type": 0, @@ -2409,6 +2403,60 @@ "value": "重試一次" } ], + "ui-patterns.help-translate": [ + { + "type": 0, + "value": "說明翻譯" + } + ], + "ui-patterns.language": [ + { + "type": 0, + "value": "語言" + } + ], + "ui-patterns.language-fully-supported-heading": [ + { + "type": 0, + "value": "完全支援" + } + ], + "ui-patterns.language-partially-supported-heading": [ + { + "type": 0, + "value": "部分支持" + } + ], + "ui-patterns.language-preview-notice": [ + { + "type": 0, + "value": "* These languages are an early preview of in-progress translations." + } + ], + "ui-patterns.language-support-checked": [ + { + "type": 0, + "value": "支援" + } + ], + "ui-patterns.language-support-unchecked": [ + { + "type": 0, + "value": "不支援" + } + ], + "ui-patterns.language-supported-for": [ + { + "type": 0, + "value": "支援的翻譯:" + } + ], + "ui-patterns.language-toast-title": [ + { + "type": 0, + "value": "語言未完全支持" + } + ], "ui.breadcrumb": [ { "type": 0, @@ -2439,6 +2487,12 @@ "value": "正在載入" } ], + "ui.new-tab-notice": [ + { + "type": 0, + "value": "opens in a new tab" + } + ], "ui.numberfield-decrease": [ { "type": 0, diff --git a/src/settings/LanguageDialog.tsx b/src/settings/LanguageDialog.tsx index 2a6ba1f2a..c42521cde 100644 --- a/src/settings/LanguageDialog.tsx +++ b/src/settings/LanguageDialog.tsx @@ -4,30 +4,23 @@ * SPDX-License-Identifier: MIT */ import { - Button, - Icon, - Link, - Modal, - ModalBody, - ModalFooter, - ModalHeader, - Text, -} from "@microbit/ui"; -import { useCallback } from "react"; -import { RiExternalLinkLine } from "react-icons/ri"; -import { FormattedMessage } from "react-intl"; -import { Grid, HStack, VStack } from "styled-system/jsx"; + LanguageDialog as SharedLanguageDialog, + LanguageDialogLanguage, +} from "@microbit/ui-patterns"; +import { RefObject, useCallback, useMemo } from "react"; import { deployment } from "../deployment"; -import { Language, supportedLanguages, useSettings } from "./settings"; +import { supportedLanguages, useSettings } from "./settings"; interface LanguageDialogProps { isOpen: boolean; onClose: () => void; - finalFocusRef: React.RefObject; + finalFocusRef: RefObject; } /** - * Language setting dialog. + * Language setting dialog: @microbit/ui-patterns' LanguageDialog fed our + * supported languages (all fully supported; preview markers for in-progress + * translations). */ export const LanguageDialog = ({ isOpen, @@ -35,97 +28,28 @@ export const LanguageDialog = ({ finalFocusRef, }: LanguageDialogProps) => { const [settings, setSettings] = useSettings(); - const handleChooseLanguage = useCallback( + const languages = useMemo( + () => + supportedLanguages.map((language) => ({ + id: language.id, + preview: language.preview, + })), + [] + ); + const handleSelectLanguage = useCallback( (languageId: string) => { - setSettings({ - ...settings, - languageId, - }); - onClose(); + setSettings({ ...settings, languageId }); }, - [settings, setSettings, onClose] + [settings, setSettings] ); - const hasPreviewLanguages = supportedLanguages.some((l) => l.preview); return ( - - - - - - - - {supportedLanguages.map((language) => ( - - ))} - - - - - - - {hasPreviewLanguages && ( - - * These languages are an early preview of in-progress translations. - - )} - - - - - - ); -}; - -interface LanguageCardProps { - language: Language; - onChooseLanguage: (languageId: string) => void; -} - -const LanguageCard = ({ language, onChooseLanguage }: LanguageCardProps) => { - return ( - + languages={languages} + onSelectLanguage={handleSelectLanguage} + translationLinkHref={deployment.translationLink} + /> ); }; diff --git a/src/settings/settings.test.ts b/src/settings/settings.test.ts index 1d7748ed3..50f5e4a6a 100644 --- a/src/settings/settings.test.ts +++ b/src/settings/settings.test.ts @@ -36,4 +36,18 @@ describe("isValidSettingsObject", () => { }) ).toEqual(false); }); + it("accepts stored language ids from before the canonical-casing move", () => { + expect( + isValidSettingsObject({ + ...defaultSettings, + languageId: "zh-cn", + }) + ).toEqual(true); + expect( + isValidSettingsObject({ + ...defaultSettings, + languageId: "zh-CN", + }) + ).toEqual(true); + }); }); diff --git a/src/settings/settings.tsx b/src/settings/settings.tsx index e6663d3db..0811992d4 100644 --- a/src/settings/settings.tsx +++ b/src/settings/settings.tsx @@ -3,16 +3,26 @@ * * SPDX-License-Identifier: MIT */ +import { + getDefaultLanguageId, + type KnownLanguageId, +} from "@microbit/ui-patterns"; import { createContext, ReactNode, useContext } from "react"; import { useStorage } from "../common/use-storage"; import { defaultCodeFontSizePt } from "../deployment/misc"; import { stage } from "../environment"; import { flags } from "../flags"; +/** + * Names and display order come from @microbit/ui-patterns' language + * registry; this list holds only what the registry can't know. + */ export interface Language { - id: string; - name: string; - enName: string; + // The shared registry's id union, so `tsc` catches an id the family + // doesn't know. Canonical BCP 47 casing (also our Crowdin codes); lowercase + // ids from stored settings and ?l= links are still accepted on read. + id: KnownLanguageId; + // An early preview of an in-progress translation, enabled on beta only. preview?: boolean; } @@ -26,68 +36,18 @@ export const fallbackLocale = "en"; // // Tag new languages with `preview: true` to enable for beta only. const allLanguages: Language[] = [ - { - id: "en", - name: "English", - enName: "English", - }, - { - id: "ca", - name: "Català", - enName: "Catalan", - }, - { - id: "zh-cn", - name: "简体中文", - enName: "Chinese (Simplified)", - }, - { - id: "zh-tw", - name: "繁體中文", - enName: "Chinese (Traditional)", - }, - { - id: "nl", - name: "Nederlands", - enName: "Dutch", - }, - { - id: "fr", - name: "Français", - enName: "French", - }, - { - id: "de", - name: "Deutsch", - enName: "German", - preview: true, - }, - { - id: "ga-ie", - name: "Gaeilge", - enName: "Irish", - preview: true, - }, - { - id: "ja", - name: "日本語", - enName: "Japanese", - }, - { - id: "ko", - name: "한국어", - enName: "Korean", - }, - { - id: "pl", - name: "Polski", - enName: "Polish", - }, - { - id: "es-es", - name: "Español", - enName: "Spanish", - }, + { id: "en" }, + { id: "ca" }, + { id: "zh-CN" }, + { id: "zh-TW" }, + { id: "nl" }, + { id: "fr" }, + { id: "de", preview: true }, + { id: "ga-IE", preview: true }, + { id: "ja" }, + { id: "ko" }, + { id: "pl" }, + { id: "es-ES" }, ]; export const supportedLanguages: Language[] = allLanguages.filter( (l) => stage !== "PRODUCTION" || !l.preview @@ -103,15 +63,32 @@ export const parameterHelpOptions: ParameterHelpOption[] = [ "manual", ]; +// Case-insensitive: stored settings and inbound ?l= links predate the move +// to canonical id casing (zh-cn -> zh-CN etc.) and must keep working. +const findSupportedLanguage = (id: unknown): Language | undefined => + typeof id === "string" + ? supportedLanguages.find((x) => x.id.toLowerCase() === id.toLowerCase()) + : undefined; + +const languageHint = (): string | null => + new URLSearchParams(window.location.search).get("l"); + export const getLanguageFromQuery = (): string => { - const searchParams = new URLSearchParams(window.location.search); - const l = searchParams.get("l"); - const supportedLanguage = supportedLanguages.find((x) => x.id === l); - return supportedLanguage?.id || supportedLanguages[0].id; + return findSupportedLanguage(languageHint())?.id || supportedLanguages[0].id; }; +/** + * The first-run language: the ?l= hint, then the browser/OS preferences, + * matched against the supported languages; English otherwise. + */ +export const getDefaultLanguage = (): string => + getDefaultLanguageId({ + autoSelectableIds: supportedLanguages.map((l) => l.id), + languageHint: languageHint(), + }); + export const defaultSettings: Settings = { - languageId: getLanguageFromQuery(), + languageId: getDefaultLanguage(), fontSize: defaultCodeFontSizePt, codeStructureHighlight: "full", parameterHelp: "automatic", @@ -133,7 +110,7 @@ export const isValidSettingsObject = (value: unknown): value is Settings => { if ( object.languageId && object.languageId !== inContextTranslationLangId && - !supportedLanguages.find((x) => x.id === object.languageId) + !findSupportedLanguage(object.languageId) ) { return false; }