From 9288e07c221ee20939a4c19e2d717ab1663d675d Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 22 Feb 2024 18:53:31 +0100 Subject: [PATCH 001/100] feat(kumascript): add mdn.cache() utility Allows to cache the output of the macro, if it only depends on locale and args. --- kumascript/src/api/mdn.ts | 5 +++++ kumascript/src/templates.ts | 32 +++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/kumascript/src/api/mdn.ts b/kumascript/src/api/mdn.ts index f750cef3afe7..e14914fcbb3f 100644 --- a/kumascript/src/api/mdn.ts +++ b/kumascript/src/api/mdn.ts @@ -1,6 +1,7 @@ import got from "got"; import { KumaThis } from "../environment.js"; import * as util from "./util.js"; +import { toggleRenderCache } from "../templates.js"; // Module level caching for repeat calls to fetchWebExtExamples(). let webExtExamples: any = null; @@ -121,6 +122,10 @@ const mdn = { */ escapeQuotes: util.escapeQuotes, + cache(this: KumaThis) { + toggleRenderCache(true); + }, + /** * Throw a deprecation error. */ diff --git a/kumascript/src/templates.ts b/kumascript/src/templates.ts index 386385cf6ca5..991079f402b2 100644 --- a/kumascript/src/templates.ts +++ b/kumascript/src/templates.ts @@ -27,11 +27,20 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ejs from "ejs"; +import { LRUCache } from "lru-cache"; const DEFAULT_MACROS_DIRECTORY = path.normalize( fileURLToPath(new URL("../macros", import.meta.url)) ); +const RENDER_CACHE = new LRUCache({ max: 100 }); + +export let isRenderCacheEnabled = false; + +export function toggleRenderCache(value: boolean) { + isRenderCacheEnabled = value; +} + export default class Templates { private macroDirectory: string; private macroNameToPath: Map; @@ -102,11 +111,24 @@ export default class Templates { throw new ReferenceError(`Unknown macro ${name}`); } try { - const rendered = await ejs.renderFile(path, args, { - async: true, - cache: args.cache || process.env.NODE_ENV === "production", - }); - return rendered.trim(); + const cacheKey = `${args.env.locale}:${name}:${JSON.stringify(args.$$)}`; + + let output = RENDER_CACHE.get(cacheKey); + + if (!output) { + output = await ejs.renderFile(path, args, { + async: true, + cache: args.cache || process.env.NODE_ENV === "production", + }); + output = output.trim(); + } + + if (isRenderCacheEnabled) { + RENDER_CACHE.set(cacheKey, output); + toggleRenderCache(false); + } + + return output; } catch (error) { console.error( `The ${name} macro on ${args.env.url} failed to render.`, From 0d7221b635188222347fd68232b8503fc4da20ac Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 22 Feb 2024 19:08:33 +0100 Subject: [PATCH 002/100] chore(kumascript): cache some sidebars --- kumascript/macros/AddonSidebar.ejs | 3 ++- kumascript/macros/CSSRef.ejs | 2 ++ kumascript/macros/GlossarySidebar.ejs | 2 ++ kumascript/macros/JsSidebar.ejs | 2 ++ kumascript/macros/LearnSidebar.ejs | 1 + kumascript/macros/SubpagesWithSummaries.ejs | 2 +- 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/kumascript/macros/AddonSidebar.ejs b/kumascript/macros/AddonSidebar.ejs index 39b2cf1394d6..76cedfb481dc 100644 --- a/kumascript/macros/AddonSidebar.ejs +++ b/kumascript/macros/AddonSidebar.ejs @@ -1,6 +1,7 @@ <% +mdn.cache(); + var locale = env.locale; -var slug = env.slug; var baseURL = '/' + locale + '/docs/Mozilla/Add-ons/'; var text = mdn.localStringMap({ diff --git a/kumascript/macros/CSSRef.ejs b/kumascript/macros/CSSRef.ejs index 2bfbb2ae296f..afe5ba637937 100644 --- a/kumascript/macros/CSSRef.ejs +++ b/kumascript/macros/CSSRef.ejs @@ -1,5 +1,7 @@ <% +mdn.cache(); + const text = mdn.localStringMap({ 'en-US': { 'Tutorials': 'Tutorials', diff --git a/kumascript/macros/GlossarySidebar.ejs b/kumascript/macros/GlossarySidebar.ejs index 5481cb69cb7a..de50c92eb1a5 100644 --- a/kumascript/macros/GlossarySidebar.ejs +++ b/kumascript/macros/GlossarySidebar.ejs @@ -1,4 +1,6 @@ <% +mdn.cache(); + async function renderRootItem(slug) { const [link, title] = await getPageLinkAndTitle(slug); return `
  • ${title}
  • ` diff --git a/kumascript/macros/JsSidebar.ejs b/kumascript/macros/JsSidebar.ejs index ca672cd11d41..5942f97005f5 100644 --- a/kumascript/macros/JsSidebar.ejs +++ b/kumascript/macros/JsSidebar.ejs @@ -1,4 +1,6 @@ <% +mdn.cache(); + var currentSection = $0; var locale = env.locale; diff --git a/kumascript/macros/LearnSidebar.ejs b/kumascript/macros/LearnSidebar.ejs index 38ad1e9b61fa..3dd2f0a3a775 100644 --- a/kumascript/macros/LearnSidebar.ejs +++ b/kumascript/macros/LearnSidebar.ejs @@ -1,4 +1,5 @@ <% +mdn.cache(); const l10nStrings = mdn.localStringMap({ 'en-US': { diff --git a/kumascript/macros/SubpagesWithSummaries.ejs b/kumascript/macros/SubpagesWithSummaries.ejs index 38546f588c6c..edebb70727b6 100644 --- a/kumascript/macros/SubpagesWithSummaries.ejs +++ b/kumascript/macros/SubpagesWithSummaries.ejs @@ -6,7 +6,6 @@ // // $0 A list of pages to output instead of the subpages of the current page; // OPTIONAL. - function pageSorter(a, b) { return a.title.localeCompare(b.title); } @@ -15,6 +14,7 @@ var termList; var html = ""; if ($0 && ($0 != undefined)) { + mdn.cache(); termList = JSON.parse($0); } else { termList = await page.subpagesExpand(); From 8116d6cac866eb33dce96cba48e36741744966ab Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 22 Feb 2024 19:22:23 +0100 Subject: [PATCH 003/100] fixup! chore(kumascript): cache some sidebars --- kumascript/macros/AddonSidebar.ejs | 1 - 1 file changed, 1 deletion(-) diff --git a/kumascript/macros/AddonSidebar.ejs b/kumascript/macros/AddonSidebar.ejs index 76cedfb481dc..34b027e3c926 100644 --- a/kumascript/macros/AddonSidebar.ejs +++ b/kumascript/macros/AddonSidebar.ejs @@ -1,5 +1,4 @@ <% -mdn.cache(); var locale = env.locale; var baseURL = '/' + locale + '/docs/Mozilla/Add-ons/'; From 73a1b06581da04323d7a568008e0c8340854ea40 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 22 Feb 2024 19:31:51 +0100 Subject: [PATCH 004/100] fixup! feat(kumascript): add mdn.cache() utility --- kumascript/src/templates.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kumascript/src/templates.ts b/kumascript/src/templates.ts index 991079f402b2..f46b246fdf8a 100644 --- a/kumascript/src/templates.ts +++ b/kumascript/src/templates.ts @@ -111,7 +111,7 @@ export default class Templates { throw new ReferenceError(`Unknown macro ${name}`); } try { - const cacheKey = `${args.env.locale}:${name}:${JSON.stringify(args.$$)}`; + const cacheKey = `${args?.env?.locale}:${name}:${JSON.stringify(args.$$)}`; let output = RENDER_CACHE.get(cacheKey); @@ -131,7 +131,7 @@ export default class Templates { return output; } catch (error) { console.error( - `The ${name} macro on ${args.env.url} failed to render.`, + `The ${name} macro on ${args?.env?.url} failed to render.`, error ); throw error; From 48cc6fd34635d02efcf767a8e6d563480caf74a6 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 23 Apr 2024 23:41:23 +0200 Subject: [PATCH 005/100] chore(locales): add de --- .../src/document/molecules/localized-content-note/index.tsx | 4 ++++ libs/constants/index.js | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index 074e10ba97a6..d3d07b90954f 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -8,6 +8,10 @@ export function LocalizedContentNote({ locale: string; }) { const activeLocaleNoteContent = { + de: { + linkText: "Diese Seite wurde automatisch aus dem Englischen übersetzt.", + url: "/en-US/docs/MDN/Community/Contributing/Translated_content#active_locales", + }, "en-US": { linkText: "This page was translated from English by the community. Learn more and join the MDN Web Docs community.", diff --git a/libs/constants/index.js b/libs/constants/index.js index e8ab63891089..5835cfce65bf 100644 --- a/libs/constants/index.js +++ b/libs/constants/index.js @@ -1,5 +1,5 @@ export const VALID_LOCALES = new Map( - ["en-US", "es", "fr", "ja", "ko", "pt-BR", "ru", "zh-CN", "zh-TW"].map( + ["de", "en-US", "es", "fr", "ja", "ko", "pt-BR", "ru", "zh-CN", "zh-TW"].map( (x) => [x.toLowerCase(), x] ) ); @@ -10,7 +10,7 @@ export const RETIRED_LOCALES = new Map( "bg", "bn", "ca", - "de", + //"de", "el", "fa", "fi", @@ -49,6 +49,7 @@ export const LOCALE_ALIASES = new Map([ // gets set in the client! export const PREFERRED_LOCALE_COOKIE_NAME = "preferredlocale"; export const ACTIVE_LOCALES = new Set([ + "de", "en-us", "es", "fr", From 656ab825471a5c3f0a23fe8e54f33daf797a3531 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 23 Apr 2024 23:59:35 +0200 Subject: [PATCH 006/100] enhance(i18n): localize "(en-US)" suffix --- build/flaws/broken-links.ts | 1 - client/src/document/index.scss | 42 ++++++++++++++++++++++++++++++++++ kumascript/src/api/web.ts | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/build/flaws/broken-links.ts b/build/flaws/broken-links.ts index 6650466d31a6..412561cb966a 100644 --- a/build/flaws/broken-links.ts +++ b/build/flaws/broken-links.ts @@ -54,7 +54,6 @@ function mutateLink( // As we still suggest the translated version even if we only // have an English (US) version. $element.attr("href", enUSFallback); - $element.append(` (${DEFAULT_LOCALE})`); $element.addClass("only-in-en-us"); $element.attr("title", "Currently only available in English (US)"); } else if (suggestion) { diff --git a/client/src/document/index.scss b/client/src/document/index.scss index 3c55c8536ca1..7010829d8ba9 100644 --- a/client/src/document/index.scss +++ b/client/src/document/index.scss @@ -942,3 +942,45 @@ kbd { opacity: 0.4; } } + +html a.only-in-en-us:after { + content: "(en-US)"; + font-size: smaller; + vertical-align: super; +} + +html[lang="de"] a.only-in-en-us:after { + content: "(engl.)"; +} + +html[lang="es"] a.only-in-en-us:after { + content: "(inglés)"; +} + +html[lang="fr"] a.only-in-en-us:after { + content: "(angl.)"; +} + +html[lang="ja"] a.only-in-en-us:after { + content: "(英語)"; +} + +html[lang="ko"] a.only-in-en-us:after { + content: "(영어)"; +} + +html[lang="ru"] a.only-in-en-us:after { + content: "(англ.)"; +} + +html[lang="pt-BR"] a.only-in-en-us:after { + content: "(inglês)"; +} + +html[lang="zh-CN"] a.only-in-en-us:after { + content: "(英语)"; +} + +html[lang="zh-TW"] a.only-in-en-us:after { + content: "(英語)"; +} diff --git a/kumascript/src/api/web.ts b/kumascript/src/api/web.ts index 5697b10a7425..3fb5e8fa07dc 100644 --- a/kumascript/src/api/web.ts +++ b/kumascript/src/api/web.ts @@ -155,7 +155,7 @@ const web = { return ( '${content} (en-US)` + `href="${enUSPage.url}"${flawAttribute}>${content}` ); } } From 8158d81f23c8441d96f56b4390cc72f2c4a053ec Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 24 Apr 2024 00:30:28 +0200 Subject: [PATCH 007/100] chore(xyz-build): update --- .github/workflows/xyz-build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/xyz-build.yml b/.github/workflows/xyz-build.yml index a9550e925b2e..8327634b5342 100644 --- a/.github/workflows/xyz-build.yml +++ b/.github/workflows/xyz-build.yml @@ -67,10 +67,12 @@ jobs: - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: - repository: mdn/translated-content + repository: mdn/mtc + ref: wip path: mdn/translated-content # See matching warning for mdn/content checkout step fetch-depth: 0 + token: ${{ secrets.MDN_MTC_PAT }} - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} @@ -166,11 +168,11 @@ jobs: yarn build:sw yarn build:prepare - yarn tool sync-translated-content + #yarn tool sync-translated-content # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. - for locale in en-us es fr ja ko pt-br ru zh-cn zh-tw; do + for locale in en-us de; do yarn build --locale $locale 2>&1 | sed "s/^/[$locale] /" & pids+=($!) done From b7905e60a330d2bfee5264bd22dd58bec9d059c4 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 10 Jun 2024 18:09:29 +0200 Subject: [PATCH 008/100] feat(workflows): add test-de-build --- .github/workflows/test-de-build.yml | 278 ++++++++++++++++++++++++++++ .github/workflows/xyz-build.yml | 8 +- 2 files changed, 281 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/test-de-build.yml diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml new file mode 100644 index 000000000000..59bc67ddd427 --- /dev/null +++ b/.github/workflows/test-de-build.yml @@ -0,0 +1,278 @@ +name: Test-DE Build + +env: + DEFAULT_NOTES: "" + +on: + workflow_dispatch: + inputs: + notes: + description: "Notes" + required: false + default: ${DEFAULT_NOTES} + + invalidate: + description: "Invalidate CDN (use only in exceptional circumstances)" + type: boolean + required: false + default: false + + workflow_call: + secrets: + GCP_PROJECT_NAME: + required: true + WIP_PROJECT_ID: + required: true + +permissions: + contents: read + id-token: write + +jobs: + build: + environment: test-de + runs-on: ubuntu-latest + + # Only run the scheduled workflows on the main repo. + if: github.repository == 'mdn/yari' + + steps: + - uses: actions/checkout@v4 + + - uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} + with: + repository: mdn/content + path: mdn/content + # Yes, this means fetch EVERY COMMIT EVER. + # It's probably not sustainable in the far future (e.g. past 2021) + # but for now it's good enough. We'll need all the history + # so we can figure out each document's last-modified date. + fetch-depth: 0 + + - uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD }} + with: + repository: mdn/mdn-studio + path: mdn/mdn-studio + lfs: true + token: ${{ secrets.MDN_STUDIO_PAT }} + + - uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD }} + with: + repository: mdn/curriculum + path: mdn/curriculum + + - uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} + with: + repository: mdn/translated-content-de + path: mdn/translated-content + # See matching warning for mdn/content checkout step + fetch-depth: 0 + token: ${{ secrets.MDN_MTC_PAT }} + + - uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD }} + with: + repository: mdn/mdn-contributor-spotlight + path: mdn/mdn-contributor-spotlight + + - name: Setup Node.js environment + if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + cache: yarn + + - name: Install all yarn packages + if: ${{ ! vars.SKIP_BUILD }} + run: yarn --frozen-lockfile + env: + # https://github.com/microsoft/vscode-ripgrep#github-api-limit-note + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Print information about build + run: | + echo "notes: ${{ github.event.inputs.notes || env.DEFAULT_NOTES }}" + + - name: Print information about CPU + run: cat /proc/cpuinfo + + - name: Build everything + if: ${{ ! vars.SKIP_BUILD }} + env: + # Remember, the mdn/content repo got cloned into `pwd` into a + # sub-folder called "mdn/content" + CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files + CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files + CONTRIBUTOR_SPOTLIGHT_ROOT: ${{ github.workspace }}/mdn/mdn-contributor-spotlight/contributors + BLOG_ROOT: ${{ github.workspace }}/mdn/mdn-studio/content/posts + CURRICULUM_ROOT: ${{ github.workspace }}/mdn/curriculum + BASE_URL: "https://de.test.developer.allizom.org" + + # The default for this environment variable is geared for writers + # (aka. local development). Usually defaults are supposed to be for + # secure production but this is an exception and default + # is not insecure. + BUILD_LIVE_SAMPLES_BASE_URL: https://live.play.de.test.mdn.allizom.net + BUILD_LEGACY_LIVE_SAMPLES_BASE_URL: https://live.play.de.test.mdn.allizom.net + + # Use the stage version of interactive examples. + BUILD_INTERACTIVE_EXAMPLES_BASE_URL: https://interactive-examples.mdn.allizom.net + + # Now is not the time to worry about flaws. + BUILD_FLAW_LEVELS: "*:ignore" + + # This enables the Plus call-to-action banner and the Plus landing page + REACT_APP_ENABLE_PLUS: true + + # This adds the ability to sign in (stage only for now) + REACT_APP_DISABLE_AUTH: false + + # Use the stage version of interactive examples in react app + REACT_APP_INTERACTIVE_EXAMPLES_BASE_URL: https://interactive-examples.mdn.allizom.net + + # Firefox Accounts and SubPlat settings + REACT_APP_FXA_SIGNIN_URL: /users/fxa/login/authenticate/ + REACT_APP_FXA_SETTINGS_URL: https://accounts.stage.mozaws.net/settings/ + REACT_APP_MDN_PLUS_SUBSCRIBE_URL: https://accounts.stage.mozaws.net/subscriptions/products/prod_Jtbg9tyGyLRuB0 + REACT_APP_MDN_PLUS_5M_PLAN: price_1JFoTYKb9q6OnNsLalexa03p + REACT_APP_MDN_PLUS_5Y_PLAN: price_1JpIPwKb9q6OnNsLJLsIqMp7 + REACT_APP_MDN_PLUS_10M_PLAN: price_1K6X7gKb9q6OnNsLi44HdLcC + REACT_APP_MDN_PLUS_10Y_PLAN: price_1K6X8VKb9q6OnNsLFlUcEiu4 + + # No surveys. + + # Telemetry. + REACT_APP_GLEAN_CHANNEL: test-de + REACT_APP_GLEAN_ENABLED: true + + # Newsletter + REACT_APP_NEWSLETTER_ENABLED: false + + # Placement + REACT_APP_PLACEMENT_ENABLED: false + + # Playground + REACT_APP_PLAYGROUND_BASE_HOST: play.de.test.mdn.allizom.net + run: | + + # Info about which CONTENT_* environment variables were set and to what. + echo "CONTENT_ROOT=$CONTENT_ROOT" + echo "CONTENT_TRANSLATED_ROOT=$CONTENT_TRANSLATED_ROOT" + echo "BLOG_ROOT=$BLOG_ROOT" + # Build the ServiceWorker first + yarn build:sw + yarn build:prepare + + #yarn tool sync-translated-content + + # Build using one process per locale. + # Note: We have 4 cores, but 9 processes is a reasonable number. + for locale in en-us de; do + yarn build --locale $locale 2>&1 | sed "s/^/[$locale] /" & + pids+=($!) + done + + for pid in "${pids[@]}"; do + wait $pid + done + + du -sh client/build + + # Build the blog + yarn build:blog + + # Build the curriculum + yarn build:curriculum + + # Generate sitemap index file + yarn build --sitemap-index + + # Generate whatsdeployed files. + yarn tool whatsdeployed --output client/build/_whatsdeployed/code.json + yarn tool whatsdeployed $CONTENT_ROOT --output client/build/_whatsdeployed/content.json + yarn tool whatsdeployed $CONTENT_TRANSLATED_ROOT --output client/build/_whatsdeployed/translated-content.json + + - name: Authenticate with GCP + uses: google-github-actions/auth@v2 + with: + token_format: access_token + service_account: deploy-test-de-content@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com + workload_identity_provider: projects/${{ secrets.WIP_PROJECT_ID }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions + + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 + + - name: Sync build + if: ${{ ! vars.SKIP_BUILD }} + run: |- + gsutil -q -m -h "Cache-Control: public, max-age=3600" cp -r client/build/static gs://${{ vars.GCP_BUCKET_NAME }}/main/ + gsutil -q -m -h "Cache-Control: public, max-age=3600" rsync -cdrj html,json,txt -y "^static/" client/build gs://${{ vars.GCP_BUCKET_NAME }}/main + + - name: Authenticate with GCP + if: ${{ ! vars.SKIP_FUNCTION }} + uses: google-github-actions/auth@v2 + with: + token_format: access_token + service_account: deploy-test-de-nonprod-mdn-ing@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com + workload_identity_provider: projects/${{ secrets.WIP_PROJECT_ID }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions + + - name: Setup gcloud + if: ${{ ! vars.SKIP_FUNCTION }} + uses: google-github-actions/setup-gcloud@v2 + with: + install_components: "beta" + + - name: Generate redirects map + if: ${{ ! vars.SKIP_FUNCTION }} + working-directory: cloud-function + env: + CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files + CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files + run: | + npm ci + npm run build-redirects + + - name: Deploy Function + if: ${{ ! vars.SKIP_FUNCTION }} + run: |- + for region in europe-west3; do + gcloud beta functions deploy mdn-nonprod-test-de-$region \ + --gen2 \ + --runtime=nodejs18 \ + --region=$region \ + --source=cloud-function \ + --trigger-http \ + --allow-unauthenticated \ + --entry-point=mdnHandler \ + --concurrency=100 \ + --min-instances=1 \ + --max-instances=100 \ + --memory=2GB \ + --timeout=120s \ + --set-env-vars="ORIGIN_MAIN=de.test.developer.allizom.org" \ + --set-env-vars="ORIGIN_LIVE_SAMPLES=live.play.de.test.mdn.allizom.net" \ + --set-env-vars="ORIGIN_PLAY=play.de.test.mdn.allizom.net" \ + --set-env-vars="SOURCE_CONTENT=https://storage.googleapis.com/${{ vars.GCP_BUCKET_NAME }}/main/" \ + --set-env-vars="SOURCE_API=https://api.developer.allizom.org/" \ + --set-env-vars="SENTRY_DSN=${{ secrets.SENTRY_DSN_CLOUD_FUNCTION }}" \ + --set-env-vars="SENTRY_ENVIRONMENT=test-de" \ + --set-env-vars="SENTRY_TRACES_SAMPLE_RATE=${{ vars.SENTRY_TRACES_SAMPLE_RATE }}" \ + --set-env-vars="SENTRY_RELEASE=${{ github.sha }}" \ + --set-secrets="KEVEL_SITE_ID=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-kevel-site-id/versions/latest" \ + --set-secrets="KEVEL_NETWORK_ID=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-kevel-network-id/versions/latest" \ + --set-secrets="SIGN_SECRET=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-sign-secret/versions/latest" \ + 2>&1 | sed "s/^/[$region] /" & + pids+=($!) + done + + for pid in "${pids[@]}"; do + wait $pid + done + + - name: Invalidate CDN + if: ${{ github.event.inputs.invalidate }} + run: gcloud compute url-maps invalidate-cdn-cache ${{ secrets.GCP_LOAD_BALANCER_NAME }} --path "/*" --async diff --git a/.github/workflows/xyz-build.yml b/.github/workflows/xyz-build.yml index 094a46acfddd..88d8b9a40b76 100644 --- a/.github/workflows/xyz-build.yml +++ b/.github/workflows/xyz-build.yml @@ -67,12 +67,10 @@ jobs: - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: - repository: mdn/mtc - ref: wip + repository: mdn/translated-content path: mdn/translated-content # See matching warning for mdn/content checkout step fetch-depth: 0 - token: ${{ secrets.MDN_MTC_PAT }} - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} @@ -168,11 +166,11 @@ jobs: yarn build:sw yarn build:prepare - #yarn tool sync-translated-content + yarn tool sync-translated-content # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. - for locale in en-us de; do + for locale in en-us es fr ja ko pt-br ru zh-cn zh-tw; do yarn build --locale $locale 2>&1 | sed "s/^/[$locale] /" & pids+=($!) done From 35a15170945fea5df49850c4fa4682c8b5877149 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 11 Jun 2024 12:36:19 +0200 Subject: [PATCH 009/100] chore(test-de): use mdnyalp.dev --- .github/workflows/test-de-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 59bc67ddd427..def0b47af887 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -116,8 +116,8 @@ jobs: # (aka. local development). Usually defaults are supposed to be for # secure production but this is an exception and default # is not insecure. - BUILD_LIVE_SAMPLES_BASE_URL: https://live.play.de.test.mdn.allizom.net - BUILD_LEGACY_LIVE_SAMPLES_BASE_URL: https://live.play.de.test.mdn.allizom.net + BUILD_LIVE_SAMPLES_BASE_URL: https://live.de.test.mdnyalp.dev + BUILD_LEGACY_LIVE_SAMPLES_BASE_URL: https://live.de.test.mdnyalp.dev # Use the stage version of interactive examples. BUILD_INTERACTIVE_EXAMPLES_BASE_URL: https://interactive-examples.mdn.allizom.net @@ -254,8 +254,8 @@ jobs: --memory=2GB \ --timeout=120s \ --set-env-vars="ORIGIN_MAIN=de.test.developer.allizom.org" \ - --set-env-vars="ORIGIN_LIVE_SAMPLES=live.play.de.test.mdn.allizom.net" \ - --set-env-vars="ORIGIN_PLAY=play.de.test.mdn.allizom.net" \ + --set-env-vars="ORIGIN_LIVE_SAMPLES=live.de.test.mdnyalp.dev" \ + --set-env-vars="ORIGIN_PLAY=de.test.mdnyalp.dev" \ --set-env-vars="SOURCE_CONTENT=https://storage.googleapis.com/${{ vars.GCP_BUCKET_NAME }}/main/" \ --set-env-vars="SOURCE_API=https://api.developer.allizom.org/" \ --set-env-vars="SENTRY_DSN=${{ secrets.SENTRY_DSN_CLOUD_FUNCTION }}" \ From 37c09c2143356a1e1fbab92dc5fc7f2270ccf917 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 11 Jun 2024 13:22:29 +0200 Subject: [PATCH 010/100] fix(build): avoid error with check-images --- build/check-images.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/build/check-images.ts b/build/check-images.ts index 3d6546c2d252..b4053ff541fa 100644 --- a/build/check-images.ts +++ b/build/check-images.ts @@ -179,21 +179,25 @@ export function checkImageReferences( // image name, if the file didn't exist the document doesn't exist. const parentDocument = Document.findByURL(path.dirname(finalSrc)); - // Base the final URL on the parent document + image file name lowercase. - finalSrc = `${parentDocument.url}/${path - .basename(finalSrc) - .toLowerCase()}`; + if (parentDocument) { + // Base the final URL on the parent document + image file name lowercase. + finalSrc = `${parentDocument.url}/${path + .basename(finalSrc) + .toLowerCase()}`; - if (src.startsWith("/")) { - // E.g. Date: Tue, 11 Jun 2024 13:22:58 +0200 Subject: [PATCH 011/100] fix(kumascript): avoid ListSubpagesForSidebar error --- kumascript/macros/ListSubpagesForSidebar.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kumascript/macros/ListSubpagesForSidebar.ejs b/kumascript/macros/ListSubpagesForSidebar.ejs index 40b4867599da..3e498cc6c0d1 100644 --- a/kumascript/macros/ListSubpagesForSidebar.ejs +++ b/kumascript/macros/ListSubpagesForSidebar.ejs @@ -92,7 +92,7 @@ async function createLink(aPage) { } let output = '' -if(pages.length) { +if(pages?.length) { const linkArray = await Promise.all(pages.map(createLink)); const links = linkArray.join(''); output = `
      ${links}
    `; From 0d7d080b168c37793db23123d665c8e440fa64e0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 11 Jun 2024 13:23:33 +0200 Subject: [PATCH 012/100] tmp: use xyz-build --- .github/workflows/xyz-build.yml | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/xyz-build.yml b/.github/workflows/xyz-build.yml index 88d8b9a40b76..def0b47af887 100644 --- a/.github/workflows/xyz-build.yml +++ b/.github/workflows/xyz-build.yml @@ -1,4 +1,4 @@ -name: XYZ Build +name: Test-DE Build env: DEFAULT_NOTES: "" @@ -30,7 +30,7 @@ permissions: jobs: build: - environment: xyz + environment: test-de runs-on: ubuntu-latest # Only run the scheduled workflows on the main repo. @@ -67,10 +67,11 @@ jobs: - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: - repository: mdn/translated-content + repository: mdn/translated-content-de path: mdn/translated-content # See matching warning for mdn/content checkout step fetch-depth: 0 + token: ${{ secrets.MDN_MTC_PAT }} - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} @@ -109,14 +110,14 @@ jobs: CONTRIBUTOR_SPOTLIGHT_ROOT: ${{ github.workspace }}/mdn/mdn-contributor-spotlight/contributors BLOG_ROOT: ${{ github.workspace }}/mdn/mdn-studio/content/posts CURRICULUM_ROOT: ${{ github.workspace }}/mdn/curriculum - BASE_URL: "https://developer.allizom.xyz" + BASE_URL: "https://de.test.developer.allizom.org" # The default for this environment variable is geared for writers # (aka. local development). Usually defaults are supposed to be for # secure production but this is an exception and default # is not insecure. - BUILD_LIVE_SAMPLES_BASE_URL: https://live.mdnyalp.dev - BUILD_LEGACY_LIVE_SAMPLES_BASE_URL: https://live.mdnyalp.dev + BUILD_LIVE_SAMPLES_BASE_URL: https://live.de.test.mdnyalp.dev + BUILD_LEGACY_LIVE_SAMPLES_BASE_URL: https://live.de.test.mdnyalp.dev # Use the stage version of interactive examples. BUILD_INTERACTIVE_EXAMPLES_BASE_URL: https://interactive-examples.mdn.allizom.net @@ -145,7 +146,7 @@ jobs: # No surveys. # Telemetry. - REACT_APP_GLEAN_CHANNEL: xyz + REACT_APP_GLEAN_CHANNEL: test-de REACT_APP_GLEAN_ENABLED: true # Newsletter @@ -155,7 +156,7 @@ jobs: REACT_APP_PLACEMENT_ENABLED: false # Playground - REACT_APP_PLAYGROUND_BASE_HOST: mdnyalp.dev + REACT_APP_PLAYGROUND_BASE_HOST: play.de.test.mdn.allizom.net run: | # Info about which CONTENT_* environment variables were set and to what. @@ -166,11 +167,11 @@ jobs: yarn build:sw yarn build:prepare - yarn tool sync-translated-content + #yarn tool sync-translated-content # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. - for locale in en-us es fr ja ko pt-br ru zh-cn zh-tw; do + for locale in en-us de; do yarn build --locale $locale 2>&1 | sed "s/^/[$locale] /" & pids+=($!) done @@ -199,7 +200,7 @@ jobs: uses: google-github-actions/auth@v2 with: token_format: access_token - service_account: deploy-xyz-yari@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com + service_account: deploy-test-de-content@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com workload_identity_provider: projects/${{ secrets.WIP_PROJECT_ID }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions - name: Setup gcloud @@ -216,7 +217,7 @@ jobs: uses: google-github-actions/auth@v2 with: token_format: access_token - service_account: deploy-xyz-yari@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com + service_account: deploy-test-de-nonprod-mdn-ing@${{ secrets.GCP_PROJECT_NAME }}.iam.gserviceaccount.com workload_identity_provider: projects/${{ secrets.WIP_PROJECT_ID }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions - name: Setup gcloud @@ -238,8 +239,8 @@ jobs: - name: Deploy Function if: ${{ ! vars.SKIP_FUNCTION }} run: |- - for region in europe-west1 us-west1 asia-east1; do - gcloud beta functions deploy mdn-xyz-$region \ + for region in europe-west3; do + gcloud beta functions deploy mdn-nonprod-test-de-$region \ --gen2 \ --runtime=nodejs18 \ --region=$region \ @@ -252,13 +253,13 @@ jobs: --max-instances=100 \ --memory=2GB \ --timeout=120s \ - --set-env-vars="ORIGIN_MAIN=developer.allizom.xyz" \ - --set-env-vars="ORIGIN_LIVE_SAMPLES=live.mdnyalp.dev" \ - --set-env-vars="ORIGIN_PLAY=mdnyalp.dev" \ + --set-env-vars="ORIGIN_MAIN=de.test.developer.allizom.org" \ + --set-env-vars="ORIGIN_LIVE_SAMPLES=live.de.test.mdnyalp.dev" \ + --set-env-vars="ORIGIN_PLAY=de.test.mdnyalp.dev" \ --set-env-vars="SOURCE_CONTENT=https://storage.googleapis.com/${{ vars.GCP_BUCKET_NAME }}/main/" \ --set-env-vars="SOURCE_API=https://api.developer.allizom.org/" \ --set-env-vars="SENTRY_DSN=${{ secrets.SENTRY_DSN_CLOUD_FUNCTION }}" \ - --set-env-vars="SENTRY_ENVIRONMENT=xyz" \ + --set-env-vars="SENTRY_ENVIRONMENT=test-de" \ --set-env-vars="SENTRY_TRACES_SAMPLE_RATE=${{ vars.SENTRY_TRACES_SAMPLE_RATE }}" \ --set-env-vars="SENTRY_RELEASE=${{ github.sha }}" \ --set-secrets="KEVEL_SITE_ID=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-kevel-site-id/versions/latest" \ From a1e0f9b59462e1eefa5feb71874feae47c5f0fb9 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 12 Jun 2024 12:13:43 +0200 Subject: [PATCH 013/100] chore(kumascript): add German translations --- kumascript/macros/AccessibilitySidebar.ejs | 10 ++ kumascript/macros/AddonSidebar.ejs | 17 ++ kumascript/macros/AddonSidebarMain.ejs | 9 + kumascript/macros/AvailableInWorkers.ejs | 1 + kumascript/macros/CSSRef.ejs | 170 ++++++++++++++++++ kumascript/macros/Deprecated_Header.ejs | 2 + kumascript/macros/Deprecated_Inline.ejs | 2 + kumascript/macros/EmbedInteractiveExample.ejs | 3 + kumascript/macros/ExperimentalBadge.ejs | 2 + kumascript/macros/FirefoxSidebar.ejs | 9 + kumascript/macros/GamesSidebar.ejs | 55 ++++++ kumascript/macros/HTMLSidebar.ejs | 44 +++++ kumascript/macros/HTTPSidebar.ejs | 39 ++++ kumascript/macros/JsSidebar.ejs | 55 ++++++ kumascript/macros/LearnSidebar.ejs | 47 +++++ kumascript/macros/ListSubpagesForSidebar.ejs | 1 + kumascript/macros/MDNSidebar.ejs | 9 + kumascript/macros/MathMLElement.ejs | 1 + kumascript/macros/MathMLRef.ejs | 7 + kumascript/macros/Non-standard_Header.ejs | 2 + kumascript/macros/NonStandardBadge.ejs | 4 +- kumascript/macros/PreviousMenuNext.ejs | 2 + kumascript/macros/ReadOnlyInline.ejs | 2 + kumascript/macros/SVGAttr.ejs | 1 + kumascript/macros/SVGRef.ejs | 8 + kumascript/macros/SeeCompatTable.ejs | 2 + kumascript/macros/WebAssemblySidebar.ejs | 13 ++ kumascript/macros/js_property_attributes.ejs | 8 + kumascript/macros/no_tag_omission.ejs | 1 + kumascript/macros/secureContext_header.ejs | 2 + kumascript/macros/xref_csscomputed.ejs | 1 + kumascript/macros/xref_cssinherited.ejs | 1 + kumascript/macros/xref_cssinitial.ejs | 1 + 33 files changed, 530 insertions(+), 1 deletion(-) diff --git a/kumascript/macros/AccessibilitySidebar.ejs b/kumascript/macros/AccessibilitySidebar.ejs index 5f89952613b3..817a3e299318 100644 --- a/kumascript/macros/AccessibilitySidebar.ejs +++ b/kumascript/macros/AccessibilitySidebar.ejs @@ -4,6 +4,16 @@ const locale = env.locale; const baseURL = `/${locale}/docs/`; const l10nStrings = mdn.localStringMap({ + "de": { + "Accessibility" : "Barrierefreiheit", + "Guides" : "Anleitungen", + "Learn" : "Barrierefreiheit lernen", + "ARIA" : "ARIA", + "ARIA_guides" : "ARIA Anleitungen", + "ARIA states and properties": "ARIA-Zustände und Eigenschaften", + "ARIA roles": "ARIA-Rollen", + "WCAG": "WCAG", + }, "en-US": { "Accessibility" : "Accessibility", "Guides" : "Guides", diff --git a/kumascript/macros/AddonSidebar.ejs b/kumascript/macros/AddonSidebar.ejs index 186e497920bf..b102e8a223c7 100644 --- a/kumascript/macros/AddonSidebar.ejs +++ b/kumascript/macros/AddonSidebar.ejs @@ -4,6 +4,23 @@ var slug = env.slug; var baseURL = '/' + locale + '/docs/Mozilla/Add-ons/'; var text = mdn.localStringMap({ + 'de': { + 'WebExtensions#Getting_started': 'Erste Schritte', + 'WebExtensions#Concepts': 'Konzepte', + 'WebExtensions#User_Interface': 'Benutzeroberfläche', + 'WebExtensions#How_to': 'Anleitungen', + 'WebExtensions#API': 'JavaScript-APIs', + 'WebExtensions#manifest.json': 'Manifest-Schlüssel', + 'extension_workshop': 'Erweiterungs-Workshop', + 'extension_workshop_develop': 'Entwickeln', + 'extension_workshop_publish': 'Veröffentlichen', + 'extension_workshop_manage': 'Verwalten', + 'extension_workshop_enterprise': 'Unternehmen', + 'Channels': 'Kanäle', + 'Blog': 'Add-ons Blog', + 'Forums': 'Add-ons Forum', + 'Chat': 'Add-ons Chat', + }, 'en-US': { 'WebExtensions#Getting_started': 'Getting started', 'WebExtensions#Concepts': 'Concepts', diff --git a/kumascript/macros/AddonSidebarMain.ejs b/kumascript/macros/AddonSidebarMain.ejs index 21c796ff4f8f..b75797f536b3 100644 --- a/kumascript/macros/AddonSidebarMain.ejs +++ b/kumascript/macros/AddonSidebarMain.ejs @@ -3,6 +3,15 @@ var locale = env.locale; var baseURL = '/' + locale + '/docs/Mozilla/Add-ons/'; var text = mdn.localStringMap({ + 'de': { + 'WebExtensions': 'Browser-Erweiterungen', + 'Themes': 'Themen', + 'Community_and_Support': 'Community und Support', + 'Channels': 'Kanäle', + 'Blog': 'Add-ons Blog', + 'Forums': 'Add-ons Foren', + '#Contact_us': 'Kontaktieren Sie uns' + }, 'en-US': { 'WebExtensions': 'Browser extensions', 'Themes': 'Themes', diff --git a/kumascript/macros/AvailableInWorkers.ejs b/kumascript/macros/AvailableInWorkers.ejs index c357401454c1..7d41c60cfcbc 100644 --- a/kumascript/macros/AvailableInWorkers.ejs +++ b/kumascript/macros/AvailableInWorkers.ejs @@ -20,6 +20,7 @@ const locale = env.locale; const note = mdn.localString({ + "de": "Hinweis:", "en-US": "Note:", "es": "Nota:", "fr": "Note:", diff --git a/kumascript/macros/CSSRef.ejs b/kumascript/macros/CSSRef.ejs index 6293e588471f..eef7a89ab8ce 100644 --- a/kumascript/macros/CSSRef.ejs +++ b/kumascript/macros/CSSRef.ejs @@ -1,6 +1,176 @@ <% const text = mdn.localStringMap({ + 'de': { + 'Tutorials': 'Tutorials', + 'CSS_basics': 'CSS Grundlagen', + 'CSS_first_steps': 'CSS erste Schritte', + 'CSS_first_steps_overview': 'CSS erste Schritte Übersicht', + 'What_is_CSS': 'Was ist CSS?', + 'Getting_started_with_CSS': 'Erste Schritte mit CSS', + 'How_CSS_is_structured': 'Wie CSS aufgebaut ist', + 'How_CSS_works': 'Wie CSS funktioniert', + 'Assessment_Styling_a_biography_page': 'Bewertung: Gestaltung einer Biografie-Seite', + 'CSS_building_blocks': 'CSS Bausteine', + 'CSS_building_blocks_overview': 'CSS Bausteine Übersicht', + 'CSS_selectors': 'CSS Selektoren', + 'Type_Class_and_ID_Selectors': 'Typ-, Klassen- und ID-Selektoren', + 'Attribute_selectors': 'Attribut-Selektoren', + 'Pseudo-classes_and_pseudo-elements': 'Pseudo-Klassen und Pseudo-Elemente', + 'Combinators': 'Kombinatoren', + 'Cascade_and_inheritance': 'Kaskade und Vererbung', + 'Cascade_layers': 'Kaskadenschichten', + 'The_box_model': 'Das Box-Modell', + 'Backgrounds_and_borders': 'Hintergründe und Rahmen', + 'Handling_different_text_directions': 'Umgang mit verschiedenen Textrichtungen', + 'Overflowing_content': 'Überlaufender Inhalt', + 'CSS_values_and_units': 'CSS Werte und Einheiten', + 'Sizing_items_in_CSS': 'Größenanpassung von Elementen in CSS', + 'Images_media_and_form_elements': 'Bilder, Medien und Formularelemente', + 'Styling_tables': 'Tabellen gestalten', + 'Debugging_CSS': 'CSS debuggen', + 'Organizing_your_CSS': 'CSS organisieren', + 'Assessment_Fundamental_CSS_comprehension': 'Bewertung: Grundlegendes CSS-Verständnis', + 'Assessment_Creating_fancy_letterheaded_paper': 'Bewertung: Erstellung von schickem Briefpapier', + 'Assessment_A_cool_looking_box': 'Bewertung: Eine cool aussehende Box', + 'Styling_text': 'Textgestaltung', + 'Styling_text_overview': 'Textgestaltung Übersicht', + 'Fundamental_text_and_font_styling': 'Grundlegende Text- und Schriftgestaltung', + 'Styling_lists': 'Listen gestalten', + 'Styling_links': 'Links gestalten', + 'Web_fonts': 'Web-Schriftarten', + 'Assessment_Typesetting_a_community_school_homepage': 'Bewertung: Satz einer Community-Schulhomepage', + 'CSS_layout': 'CSS-Layout', + 'CSS_layout_overview': 'CSS-Layout Übersicht', + 'Introduction_to_CSS_layout' : 'Einführung in CSS-Layout', + 'Normal_Flow' : 'Normaler Fluss', + 'Flexbox': 'Flexbox', + 'Grids' : 'Raster', + 'Floats': 'Floats', + 'Positioning': 'Positionierung', + 'Multiple-column_layout': 'Mehrspalten-Layout', + 'Responsive_design': 'Responsives Design', + 'Beginners_guide_to_media_queries': 'Anfängerkurs zu Media-Queries', + 'Legacy_layout_methods': 'Veraltete Layout-Methoden', + 'Supporting_older_browsers': 'Unterstützung älterer Browser', + 'Assessment_Fundamental_Layout_Comprehension' : 'Bewertung: Grundlegendes Layout-Verständnis', + 'Reference': 'Referenz', + 'Guides': 'Leitfäden', + 'Animations': 'Animationen', + 'Using_CSS_animations': 'CSS-Animationen verwenden', + 'Backgrounds_and_Borders': 'Hintergründe und Rahmen', + 'Using_multiple_backgrounds': 'Mehrere Hintergründe verwenden', + 'Resizing_background_images': 'Hintergrundbilder skalieren', + 'Box_alignment': 'Box-Ausrichtung', + 'Box_alignment_in_block_layout': 'Box-Ausrichtung im Block-Layout', + 'Box_alignment_in_flexbox': 'Box-Ausrichtung in Flexbox', + 'Box_alignment_in_grid_layout': 'Box-Ausrichtung im Raster-Layout', + 'Box_alignment_in_multi-column_layout': 'Box-Ausrichtung im Mehrspalten-Layout', + 'Box_model': 'Box-Modell', + 'Introduction_to_the_CSS_basic_box_model': 'Einführung in das grundlegende CSS-Box-Modell', + 'Mastering_margin_collapsing': 'Beherrschung des Margin-Zusammenbruchs', + 'Colors': 'Farben', + 'Applying_color_to_HTML_elements_using_CSS': 'Farbe auf HTML-Elemente anwenden', + 'Web_Accessibility_Understanding_Colors_and_Luminance': 'Barrierefreiheit: Farben und Helligkeit verstehen', + 'Color_contrast': 'Barrierefreiheit: Farbkontrast', + 'Columns': 'Spalten', + 'Basic_concepts_of_Multicol': 'Grundlagen von Multicol', + 'Styling_columns': 'Spalten gestalten', + 'Spanning_and_balancing': 'Spannen und Ausgleichen', + 'Handling_overflow_in_Multicol': 'Überlauf in Multicol handhaben', + 'Content_breaks_in_Multicol': 'Inhaltsumbrüche in Multicol', + 'Conditional_rules': 'Bedingte Regeln', + 'Using_feature_queries': 'Feature-Abfragen verwenden', + 'Containment' : 'Eindämmung', + 'Using_CSS_containment' : 'CSS-Eindämmung verwenden', + 'Container_queries' : 'Container-Abfragen', + 'Container_size_and_style_queries' : 'Containergrößen- und Stilabfragen', + 'CSSOM_view': 'CSSOM-Ansicht', + 'Coordinate_systems': 'Koordinatensysteme', + 'Flexbox': 'Flexbox', + 'Basic_concepts_of_Flexbox': 'Grundlagen von Flexbox', + 'Comparison_with_other_layout_methods': 'Vergleich mit anderen Layout-Methoden', + 'Aligning_items_in_a_flex_container': 'Elemente in einem Flex-Container ausrichten', + 'Ordering_flex_items': 'Flex-Elemente anordnen', + 'Controlling_flex_item_ratios': 'Verhältnis von Flex-Elementen steuern', + 'Mastering_wrapping_of_flex_items': 'Beherrschung des Flex-Item-Wraps', + 'Typical_use_cases_of_Flexbox': 'Typische Anwendungsfälle von Flexbox', + 'Flow_layout': 'Fluss-Layout', + 'Block_and_Inline_layout_in_normal_flow': 'Block- und Inline-Layout im normalen Fluss', + 'In_flow_and_Out_of_flow': 'Im Fluss und außerhalb des Flusses', + 'Formatting_contexts_explained': 'Formatierungskontexte erklärt', + 'Flow_layout_and_writing_modes': 'Fluss-Layout und Schreibmodi', + 'Flow_layout_and_overflow': 'Fluss-Layout und Überlauf', + 'Fonts': 'Schriftarten', + 'OpenType_font_features_guide': 'Leitfaden zu OpenType-Schriftarten', + 'Variable_fonts_guide': 'Leitfaden zu variablen Schriftarten', + 'Grid': 'Raster', + 'Basics_concepts_of_grid_layout': 'Grundlagen des Raster-Layouts', + 'Relationship_to_other_layout_methods': 'Beziehung zu anderen Layout-Methoden', + 'Line-based_placement': 'Linienbasierte Platzierung', + 'Grid_template_areas': 'Raster-Template-Bereiche', + 'Layout_using_named_grid_lines': 'Layout mit benannten Raster-Linien', + 'Auto-placement_in_grid_layout': 'Automatische Platzierung im Raster-Layout', + 'Box_alignment_in_grid_layout': 'Box-Ausrichtung im Raster-Layout', + 'Grids_logical_values_and_writing_modes': 'Raster, logische Werte und Schreibmodi', + 'Grid_layout_and_accessibility': 'Raster-Layout und Barrierefreiheit', + 'Grid_Layout_and_progressive_enhancement': 'Raster-Layout und progressive Verbesserung', + 'Realizing_common_layouts_using_grids': 'Umsetzung häufiger Layouts mit Rastern', + 'Subgrid': 'Unter-Raster', + 'Masonry_layout': 'Masonry-Layout', + 'Images': 'Bilder', + 'Using_CSS_gradients': 'CSS-Verläufe verwenden', + 'Lists_and_counters': 'Listen und Zähler', + 'Using_CSS_counters': 'CSS-Zähler verwenden', + 'Consistent_list_indentation': 'Konsistente Listen-Einrückung', + 'Logical_properties': 'Logische Eigenschaften', + 'Basic_concepts': 'Grundkonzepte', + 'Floating_and_positioning': 'Floaten und Positionieren', + 'Margins_borders_and_padding': 'Ränder, Rahmen und Abstände', + 'Sizing': 'Größenanpassung', + 'Math_functions': 'Mathematische Funktionen', + 'Using_CSS_math_functions': 'CSS-Mathematikfunktionen verwenden', + 'Media_queries': 'Media-Abfragen', + 'Using_media_queries': 'Media-Abfragen verwenden', + 'Using_media_queries_for_accessibility': 'Media-Abfragen für Barrierefreiheit verwenden', + 'Testing_media_queries_programmatically': 'Media-Abfragen programmatisch testen', + 'Printing': 'Drucken', + 'Nesting': 'Verschachteln von Stilregeln', + 'Using_CSS_nesting': 'CSS-Verschachtelung verwenden', + 'Nesting_and_specificity': 'Verschachtelung und Spezifität', + 'Nesting_at-rules': 'Verschachtelung von @-Regeln', + 'Positioning': 'Positionierung', + 'Understanding_CSS_z-index': 'CSS z-Index verstehen', + 'Scroll_snap': 'Scroll-Snap', + 'Basic_concepts_of_scroll_snap': 'Grundkonzepte von Scroll-Snap', + 'Shapes': 'Formen', + 'Overview_of_shapes': 'Übersicht der Formen', + 'Shapes_from_box_values': 'Formen aus Box-Werten', + 'Basic_shapes': 'Grundlegende Formen', + 'Shapes_from_images': 'Formen aus Bildern', + 'Text': 'Text', + 'Wrapping_and_breaking_text': 'Text umfließen und umbrechen', + 'Transforms': 'Transformationen', + 'Using_transforms': 'Transformationen verwenden', + 'Transitions': 'Übergänge', + 'Using_transitions': 'Übergänge verwenden', + 'Layout_cookbook': 'Layout-Kochbuch', + 'Media_objects': 'Medienobjekte', + 'Columns': 'Spalten', + 'Center_an_element': 'Ein Element zentrieren', + 'Sticky_footers': 'Sticky-Footer', + 'Split_navigation': 'Geteilte Navigation', + 'Breadcrumb_navigation': 'Breadcrumb-Navigation', + 'List_group_with_badges': 'Listengruppe mit Abzeichen', + 'Pagination': 'Seitennummerierung', + 'Card': 'Karte', + 'Grid_wrapper': 'Raster-Wrapper', + 'Tools': 'Werkzeuge', + 'Color_picker_tool': 'Farbwähler', + 'Box-shadow_generator': 'Box-Schatten-Generator', + 'Border-radius_generator': 'Border-Radius-Generator', + 'Border-image_generator' : 'Border-Image-Generator', + }, 'en-US': { 'Tutorials': 'Tutorials', 'CSS_basics': 'CSS basics', diff --git a/kumascript/macros/Deprecated_Header.ejs b/kumascript/macros/Deprecated_Header.ejs index 86f6001194f1..d61c67eb791b 100644 --- a/kumascript/macros/Deprecated_Header.ejs +++ b/kumascript/macros/Deprecated_Header.ejs @@ -9,6 +9,7 @@ if (/^\d/.test($0)) { } const note = mdn.localString({ + "de": "Veraltet", "en-US": "Deprecated", "es": "Obsoleto", "fr": "Obsolète", @@ -20,6 +21,7 @@ const note = mdn.localString({ }); const desc = mdn.localString({ + "de": "Diese Funktion wird nicht mehr empfohlen. Obwohl einige Browser sie möglicherweise noch unterstützen, wurde sie möglicherweise bereits aus den relevanten Webstandards entfernt, befindet sich im Prozess der Entfernung oder wird nur aus Kompatibilitätsgründen beibehalten. Vermeiden Sie die Verwendung und aktualisieren Sie gegebenenfalls bestehenden Code; siehe die Kompatibilitätstabelle am Ende dieser Seite, um Ihre Entscheidung zu treffen. Beachten Sie, dass diese Funktion jederzeit nicht mehr funktionieren kann.", "en-US": "This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.", "es": "Esta característica ya no se recomienda. Aunque es posible que algunos navegadores aún lo admitan, probablemente ya se ha eliminado de los estándares web relevantes, está en proceso de eliminación o solo se conserva por motivos de compatibilidad. Evite usarlo y actualice el código existente si es posible; consulte la tabla de compatibilidad en la parte inferior de esta página para orientar su decisión. Tenga en cuenta que esta característica puede dejar de funcionar en cualquier momento.", "fr": "Cette fonctionnalité a été supprimée des standards du Web. Bien que quelques navigateurs puissent encore la supporter, elle est en cours d'éradication. Ne l'utilisez ni dans d'anciens projets, ni dans de nouveaux. Les pages et applications Web l'utilisant peuvent cesser de fonctionner à tout moment.", diff --git a/kumascript/macros/Deprecated_Inline.ejs b/kumascript/macros/Deprecated_Inline.ejs index d9d979bbeb61..f56472d295e7 100644 --- a/kumascript/macros/Deprecated_Inline.ejs +++ b/kumascript/macros/Deprecated_Inline.ejs @@ -9,6 +9,7 @@ // None const title = mdn.localString({ + "de": "Veraltet. Nicht für neue Websites verwenden.", "en-US": "Deprecated. Not for use in new websites.", "ko": "지원이 중단되었습니다. 새로운 웹사이트에서 사용하지 마세요.", "zh-CN": "已弃用。请不要在新的网站中使用。", @@ -16,6 +17,7 @@ const title = mdn.localString({ }); const abbreviation = mdn.localString({ + "de": "Veraltet", "en-US": "Deprecated", "es": "Obsoleto", "fr": "Obsolète", diff --git a/kumascript/macros/EmbedInteractiveExample.ejs b/kumascript/macros/EmbedInteractiveExample.ejs index d8a28c27037f..e40166b6e518 100644 --- a/kumascript/macros/EmbedInteractiveExample.ejs +++ b/kumascript/macros/EmbedInteractiveExample.ejs @@ -27,6 +27,9 @@ if ($1) { } const text = mdn.localStringMap({ + "de": { + "title": "Probieren Sie es aus", + }, "en-US": { "title": "Try it", }, diff --git a/kumascript/macros/ExperimentalBadge.ejs b/kumascript/macros/ExperimentalBadge.ejs index 874dbb020785..3297dee1f7fd 100644 --- a/kumascript/macros/ExperimentalBadge.ejs +++ b/kumascript/macros/ExperimentalBadge.ejs @@ -5,6 +5,7 @@ // Parameters: none const title = mdn.localString({ + "de": "Experimentell. Erwarten Sie, dass sich das Verhalten in Zukunft ändert.", "en-US": "Experimental. Expect behavior to change in the future.", "es": "Experimental. Espere que el comportamiento cambie en el futuro.", "fr": "Expérimental. Le comportement attendu pourrait évoluer à l'avenir.", @@ -17,6 +18,7 @@ const title = mdn.localString({ }); const abbreviation = mdn.localString({ + "de": "Experimentell", "en-US": "Experimental", "es": "Experimental", "fr": "Expérimental", diff --git a/kumascript/macros/FirefoxSidebar.ejs b/kumascript/macros/FirefoxSidebar.ejs index e17ab9531590..ff0116dfd2cb 100644 --- a/kumascript/macros/FirefoxSidebar.ejs +++ b/kumascript/macros/FirefoxSidebar.ejs @@ -6,6 +6,15 @@ const baseURL = `/${locale}/docs/Mozilla/`; const addonsURL = `/${locale}/Add-ons/`; const text = mdn.localStringMap({ + "de": { + "Firefox_releases": "Firefox-Veröffentlichungen", + "Firefox_release_notes_developer": "Firefox-Hinweise für Entwickler", + "Experimental_features_firefox": "Experimentelle Funktionen in Firefox", + "Add-ons": "Add-ons", + "Browser_extensions": "Browser-Erweiterungen", + "Themes": "Themen", + "Firefox_documentation": "Firefox-Dokumentation", + }, "en-US": { "Firefox_releases": "Firefox releases", "Firefox_release_notes_developer": "Firefox release notes for developers", diff --git a/kumascript/macros/GamesSidebar.ejs b/kumascript/macros/GamesSidebar.ejs index 7dba26432886..3c7dc6415159 100644 --- a/kumascript/macros/GamesSidebar.ejs +++ b/kumascript/macros/GamesSidebar.ejs @@ -7,6 +7,61 @@ const webURL = `/${locale}/docs/Web/`; const appsURL = `/${locale}/docs/Web/Apps/`; const text = mdn.localStringMap({ + "de": { + "Introduction": "Einführung", + "Anatomy": "Anatomie", + "APIs_for_game_development": "APIs für die Spieleentwicklung", + "Canvas": "Leinwand", + "CSS": "CSS", + "Full_Screen": "Vollbild", + "Gamepad": "Gamepad", + "IndexedDB": "IndexedDB", + "JavaScript": "JavaScript", + "Pointer_Lock": "Mauszeiger Sperre", + "SVG": "SVG", + "Typed_Arrays": "Typisierte Arrays", + "Web_Audio": "Web Audio", + "WebGL": "WebGL", + "WebRTC": "WebRTC", + "Web_Sockets": "WebSockets", + "WebVR": "WebVR", + "Web_Workers": "Web Workers", + "XMLHttpRequest": "XMLHttpRequest", + "Techniques": "Techniken", + "Using_async_scripts_for_asm.js": "Verwendung asynchroner Skripte für asm.js", + "Optimizing_startup_performance": "Optimierung der Startleistung", + "Using_WebRTC_peer-to-peer_data_channels": "Verwendung von WebRTC Peer-to-Peer-Datenkanälen", + "Efficient_animation_for_web_games": "Effiziente Animationen für Webspiele", + "Audio_for_Web_Games": "Audio für Webspiele", + "2D_collision_detection": "2D-Kollisionserkennung", + "Tiles_and_tilemaps_overview": "Überblick über Kacheln und Kachelkarten", + "3D_games_on_the_Web": "3D-Spiele im Web", + "3D_games_on_the_Web_overview": "Überblick über 3D-Spiele im Web", + "Explaining_basic_3D_theory": "Erklärung der grundlegenden 3D-Theorie", + "Building_up_a_basic_demo_with_A-Frame": "Aufbau einer einfachen Demo mit A-Frame", + "Building_up_a_basic_demo_with_Babylon.js": "Aufbau einer einfachen Demo mit Babylon.js", + "Building_up_a_basic_demo_with_PlayCanvas": "Aufbau einer einfachen Demo mit PlayCanvas", + "Building_up_a_basic_demo_with_Three.js": "Aufbau einer einfachen Demo mit Three.js", + "WebXR_guide": "WebXR-Anleitung", + "3D_collision_detection": "3D-Kollisionserkennung", + "Bounding_volume_collision_detection_with_THREE.js": "Erkennung von Kollisionen mit Begrenzungsvolumen mit THREE.js", + "Implementing_game_control_mechanisms": "Implementierung von Spielsteuerungsmechanismen", + "Control_mechanisms": "Steuermechanismen", + "Mobile_touch": "Mobile Berührung", + "Desktop_with_mouse_and_keyboard": "Desktop mit Maus und Tastatur", + "Desktop_with_gamepad": "Desktop mit Gamepad", + "Other": "Andere", + "Tutorials": "Anleitungen", + "2D_breakout_game_using_pure_JavaScript": "2D-Breakout-Spiel mit purem JavaScript", + "2D_breakout_game_using_Phaser": "2D-Breakout-Spiel mit Phaser", + "2D_maze_game_with_device_orientation": "2D-Labyrinth-Spiel mit Geräteorientierung", + "2D_platform_game_using_Phaser": "2D-Plattformspiel mit Phaser", + "Publishing_games": "Veröffentlichung von Spielen", + "Publishing_games_overview": "Überblick über die Veröffentlichung von Spielen", + "Game_distribution": "Spielverteilung", + "Game_promotion": "Spielwerbung", + "Game_monetization": "Spielmonetarisierung" + }, "en-US": { "Introduction": "Introduction", "Anatomy": "Anatomy", diff --git a/kumascript/macros/HTMLSidebar.ejs b/kumascript/macros/HTMLSidebar.ejs index 001486120be8..4b369cac3261 100644 --- a/kumascript/macros/HTMLSidebar.ejs +++ b/kumascript/macros/HTMLSidebar.ejs @@ -12,6 +12,50 @@ function state(section) { } var text = mdn.localStringMap({ + 'de': { + 'Tutorials': 'Tutorials', + 'HTML_basics': 'HTML-Grundlagen', + 'Introduction_to_HTML': 'Einführung in HTML', + 'Introduction_to_HTML_overview': 'Übersicht zur Einführung in HTML', + 'Getting_started_with_HTML': 'Erste Schritte mit HTML', + 'Whats_in_the_head_metadata_in_HTML': 'Was ist im Kopf? Metadaten in HTML', + 'HTML_text_fundamentals': 'Grundlagen der HTML-Texte', + 'Creating_hyperlinks': 'Erstellen von Hyperlinks', + 'Advanced_text_formatting': 'Erweiterte Textformatierung', + 'Document_and_website_structure': 'Dokument- und Webseitenstruktur', + 'Debugging_HTML': 'Debugging von HTML', + 'Assessment_Marking_up_a_letter': 'Bewertung: Briefauszeichnung', + 'Assessment_Structuring_a_page_of_content': 'Bewertung: Strukturierung einer Inhaltsseite', + 'Multimedia_and_embedding': 'Multimedia und Einbettung', + 'Multimedia_and_embedding_overview': 'Übersicht zu Multimedia und Einbettung', + 'Images_in_HTML': 'Bilder in HTML', + 'Video_and_audio_content': 'Video- und Audioinhalte', + 'From_object_to_iframe_other_embedding_technologies': 'Von Object zu Iframe - andere Einbettungstechnologien', + 'Adding_vector_graphics_to_the_web': 'Hinzufügen von Vektorgrafiken zum Web', + 'Responsive_images': 'Responsive Bilder', + 'Assessment_Mozilla_splash_page': 'Bewertung: Mozilla-Startseite', + 'HTML_tables' : 'HTML-Tabellen', + 'HTML_tables_overview' : 'Übersicht zu HTML-Tabellen', + 'HTML_table_basics' : 'Grundlagen der HTML-Tabellen', + 'HTML_table_advanced_features_and_accessibility' : 'Erweiterte Funktionen und Zugänglichkeit von HTML-Tabellen', + 'Assessment_Structuring_planet_data' : 'Bewertung: Strukturierung von Planetendaten', + 'Reference': 'Referenzen', + 'HTML_Elements': 'HTML-Elemente', + 'Global_attributes': 'Globale Attribute', + 'Attributes': 'Attribute', + 'Input_types': '<input>-Typen', + 'Guides': 'Anleitungen', + 'Content_categories': 'Inhaltskategorien', + 'Allowing_cross-origin_images_and_canvas': 'Erlauben der Cross-Origin-Nutzung von Bildern und Canvas', + 'Block-level_elements': 'Block-Elemente', + 'Inline_elements': 'Inline-Elemente', + 'Date_and_time_formats': 'Datums- und Zeitformate in HTML', + 'Constraint_validation': 'Einschränkungsvalidierung', + 'Microdata': 'Mikrodaten', + 'Microformats': 'Mikroformate', + 'Quirks_Mode_and_Standards_Mode': 'Quirks-Modus und Standards-Modus', + 'Viewport_meta_tag': 'Viewport-Meta-Tag', + }, 'en-US': { 'Tutorials': 'Tutorials', 'HTML_basics': 'HTML basics', diff --git a/kumascript/macros/HTTPSidebar.ejs b/kumascript/macros/HTTPSidebar.ejs index 1c2c268c0807..97a951b4d1f1 100644 --- a/kumascript/macros/HTTPSidebar.ejs +++ b/kumascript/macros/HTTPSidebar.ejs @@ -13,6 +13,45 @@ function state(section) { } var text = mdn.localStringMap({ + 'de': { + 'HTTP': 'HTTP', + 'HTTPGuide': 'HTTP-Leitfaden', + 'Basics': 'Grundlagen von HTTP', + 'Overview': 'Übersicht über HTTP', + 'Evolution': 'Entwicklung von HTTP', + 'ResourcesURI': 'Ressourcen und URIs', + 'Identifying': 'Identifizieren von Ressourcen im Web', + 'DataURLs': 'Daten-URLs', + 'MIMETypes': 'Einführung in MIME-Typen', + 'ListMIMETypes': 'Gängige MIME-Typen', + 'WWWorNotWWW': 'Auswahl zwischen www und nicht-www URLs', + 'Messages': 'HTTP-Nachrichten', + 'Session': 'Eine typische HTTP-Sitzung', + 'Connection1x': 'Verbindungsverwaltung in HTTP/1.x', + 'Ranges': 'HTTP-Bereichsanfragen', + 'Redirects': 'HTTP-Weiterleitungen', + 'Conditionals': 'HTTP-Bedingte Anfragen', + 'ContentNego': 'HTTP-Inhaltsverhandlung', + 'Headers': 'HTTP-Header', + 'Response_codes': 'Antwortcodes', + 'Compression': 'HTTP-Komprimierung', + 'Cookies': 'HTTP-Cookies', + 'Caching': 'HTTP-Caching', + 'CORS': 'HTTP-Zugriffskontrolle (CORS)', + 'Resources': 'HTTP-Spezifikationen', + 'Permissions_Policy': 'Berechtigungsrichtlinie', + 'Guides': 'Anleitungen', + 'Reference': 'Referenzen', + 'Methods': 'HTTP-Anfragemethoden', + 'Status': 'HTTP-Antwortstatuscodes', + 'CSPDirectives': 'CSP-Direktiven', + 'CORS_errors': 'CORS-Fehler', + 'PermissionsPolicyDirectives': 'Permissions-Policy Direktiven', + 'Security': 'HTTP-Sicherheit', + 'Authentication': 'HTTP-Authentifizierung', + 'ProtocolUpgradeMech': 'Protokoll-Upgrade-Mechanismus', + 'CSP': 'Content Security Policy (CSP)' + }, 'en-US': { 'HTTP': 'HTTP', 'HTTPGuide': 'HTTP guide', diff --git a/kumascript/macros/JsSidebar.ejs b/kumascript/macros/JsSidebar.ejs index 6602a53522c4..a95def271a3e 100644 --- a/kumascript/macros/JsSidebar.ejs +++ b/kumascript/macros/JsSidebar.ejs @@ -10,6 +10,61 @@ function state(section) { } var text = mdn.localStringMap({ + 'de': { + 'Overview': 'JavaScript-Technologieübersicht', + 'Tutorials': 'Tutorials', + 'Guide': 'JavaScript-Handbuch', + 'Guide_Introduction': 'Einführung', + 'Guide_Grammar': 'Grammatik und Typen', + 'Guide_Control_flow': 'Steuerfluss und Fehlerbehandlung', + 'Guide_Loops': 'Schleifen und Iteration', + 'Guide_Functions': 'Funktionen', + 'Guide_Expressions': 'Ausdrücke und Operatoren', + 'Guide_Numbers': 'Zahlen und Daten', + 'Guide_Text': 'Textformatierung', + 'Guide_RegExp': 'Reguläre Ausdrücke', + 'Guide_Indexed_collections': 'Indexierte Sammlungen', + 'Guide_keyed_collections': 'Gekennzeichnete Sammlungen', + 'Guide_Objects': 'Arbeiten mit Objekten', + 'Guide_Classes': 'Verwendung von Klassen', + 'Guide_Promises': 'Verwendung von Promises', + 'Guide_Typed_arrays': 'JavaScript-getypte Arrays', + 'Guide_Iterators_generators': 'Iteratoren und Generatoren', + 'Guide_Meta': 'Metaprogrammierung', + 'Guide_Modules': 'JavaScript-Module', + 'Complete_beginners': 'Komplette Anfänger', + 'Basics': 'JavaScript-Grundlagen', + 'First_steps': 'Erste Schritte in JavaScript', + 'Building_blocks': 'JavaScript-Bausteine', + 'Intermediate': 'Mittelstufe', + 'Introducing_objects': 'Einführung in JavaScript-Objekte', + 'Client-side_APIs': 'Client-seitige Web-APIs', + 'Frameworks': 'Client-seitige JavaScript-Frameworks', + 'Language_overview': 'Sprachübersicht', + 'Data_structures': 'JavaScript-Datenstrukturen', + 'Equality': 'Gleichheitsvergleiche und Gleichheit', + 'Closures': 'Closures', + 'Advanced': 'Fortgeschritten', + 'Inheritance': 'Vererbung und die Prototypenkette', + 'Strict_mode': 'Strenger Modus', + 'Memory_management': 'Speicherverwaltung', + 'Event_loop': 'Konkurrenzmodell und Ereignisschleife', + 'Reference': 'Referenzen', + 'Global_Objects': 'Eingebaute Objekte', + 'Operators': 'Ausdrücke und Operatoren', + 'Statements': 'Anweisungen und Deklarationen', + 'Functions': 'Funktionen', + 'Classes': 'Klassen', + 'Errors': 'Fehler', + 'More': 'Sonstiges', + 'Lexical_grammar': 'Lexikalische Grammatik', + 'Enumerability': 'Aufzählbarkeit und Eigentum von Eigenschaften', + 'Data_types': 'Datentypen und Datenstrukturen', + 'Iteration_protocols': 'Iterationsprotokolle', + 'Template_strings': 'Vorlagenliterale', + 'Trailing_commas': 'Abschließende Kommas', + 'Deprecated_features': 'Veraltete Funktionen', + }, 'en-US': { 'Overview': 'JavaScript technologies overview', 'Tutorials': 'Tutorials', diff --git a/kumascript/macros/LearnSidebar.ejs b/kumascript/macros/LearnSidebar.ejs index e7409dd9a244..9f0f02cb01e6 100644 --- a/kumascript/macros/LearnSidebar.ejs +++ b/kumascript/macros/LearnSidebar.ejs @@ -1,6 +1,53 @@ <% const l10nStrings = mdn.localStringMap({ + 'de': { + 'Complete_beginners_start_here': 'Komplette Anfänger beginnen hier!', + 'Getting_started_with_the_web': 'Erste Schritte mit dem Web', + 'HTML_Structuring_the_web': 'HTML — Strukturierung des Webs', + 'Introduction_to_HTML': 'Einführung in HTML', + 'Multimedia_and_embedding': 'Multimedia und Einbettung', + 'HTML_tables': 'HTML-Tabellen', + 'CSS_Styling_the_web': 'CSS — Gestaltung des Webs', + 'CSS_first_steps': 'CSS erste Schritte', + 'CSS_building_blocks': 'CSS-Bausteine', + 'Styling_text': 'Textgestaltung', + 'CSS_layout': 'CSS-Layout', + 'JavaScript_dynamic_client-side_scripting': 'JavaScript — Dynamisches clientseitiges Skripting', + 'JavaScript_first_steps': 'JavaScript erste Schritte', + 'JavaScript_building_blocks': 'JavaScript-Bausteine', + 'Introducing_JavaScript_objects': 'Einführung in JavaScript-Objekte', + 'Asynchronous_JavaScript': 'Asynchrones JavaScript', + 'Client-side_web_APIs': 'Client-seitige Web-APIs', + 'Web_forms': 'Webformulare — Arbeiten mit Benutzerdaten', + 'Web_forms_core': 'Grundlagen der Webformulare', + 'Web_forms_advanced': 'Erweiterte Techniken für Webformulare', + 'Accessibility_—_Make_the_web_usable_by_everyone': 'Barrierefreiheit — Das Web für alle nutzbar machen', + 'Accessibility_guides': 'Barrierefreiheitsleitfäden', + 'Accessibility_assessment': 'Barrierefreiheitseinschätzung', + 'Performance': 'Leistung — Websites schnell und reaktionsschnell machen', + 'Performance_guides': 'Leitfäden zur Leistung', + 'MathML_Writing_mathematics': 'MathML — Schreiben von Mathematik mit MathML', + 'MathML_first_steps': 'MathML erste Schritte', + 'Games_Developing_for_web': 'Spiele — Entwicklung von Spielen für das Web', + 'Guides_and_tutorials': 'Anleitungen und Tutorials', + 'Tools_and_testing': 'Werkzeuge und Tests', + 'Cross_browser_testing': 'Cross-Browser-Tests', + 'Git_and_GitHub': 'Git und GitHub', + 'Client-side_web_development_tools': 'Client-seitige Webentwicklungstools', + 'Introduction_to_client-side_frameworks': 'Einführung in client-seitige Frameworks', + 'React': 'React', + 'Ember': 'Ember', + 'Vue': 'Vue', + 'Svelte': 'Svelte', + 'Angular': 'Angular', + 'Server-side_website_programming': 'Server-seitige Webprogrammierung', + 'First_steps': 'Erste Schritte', + 'Django_web_framework_(Python)': 'Django Web-Framework (Python)', + 'Express_Web_Framework_(Node.js_JavaScript)': 'Express Web-Framework (Node.js/JavaScript)', + 'Further_resources': 'Weitere Ressourcen', + 'Common_questions': 'Häufige Fragen', + }, 'en-US': { 'Complete_beginners_start_here': 'Complete beginners start here!', 'Getting_started_with_the_web': 'Getting started with the web', diff --git a/kumascript/macros/ListSubpagesForSidebar.ejs b/kumascript/macros/ListSubpagesForSidebar.ejs index 3e498cc6c0d1..6f93e266699e 100644 --- a/kumascript/macros/ListSubpagesForSidebar.ejs +++ b/kumascript/macros/ListSubpagesForSidebar.ejs @@ -24,6 +24,7 @@ const startDelim = $3; const endDelim = $4; const overview = mdn.localString({ + 'de': 'Überblick', 'en-US': 'Overview', 'es': 'Generalidades', 'fr': 'Aperçu', diff --git a/kumascript/macros/MDNSidebar.ejs b/kumascript/macros/MDNSidebar.ejs index 87909bc188d3..ee5e35e7f4bd 100644 --- a/kumascript/macros/MDNSidebar.ejs +++ b/kumascript/macros/MDNSidebar.ejs @@ -1,6 +1,15 @@ <% const l10nStrings = mdn.localStringMap({ + "de": { + "history": "Geschichte", + "advisory_board": "Beirat", + "community_guidelines": "Gemeinschaftsrichtlinien", + "contributing_to_mdn_web_docs": "Beiträge zu MDN Web Docs", + "writing_guide": "Schreibanleitung", + "how_to_guides": "Anleitungen", + "page_structures": "Seitenstrukturen", + }, "en-US": { "history": "History", "advisory_board": "Advisory Board", diff --git a/kumascript/macros/MathMLElement.ejs b/kumascript/macros/MathMLElement.ejs index 2867cb1a0a11..8ec19843e534 100644 --- a/kumascript/macros/MathMLElement.ejs +++ b/kumascript/macros/MathMLElement.ejs @@ -2,6 +2,7 @@ var name = $0; var sectionname = mdn.localString({ + "de": "Element", "en-US": "Element", "es": "Elemento" }); diff --git a/kumascript/macros/MathMLRef.ejs b/kumascript/macros/MathMLRef.ejs index 7e589f77ee1f..27b0f9f758f4 100644 --- a/kumascript/macros/MathMLRef.ejs +++ b/kumascript/macros/MathMLRef.ejs @@ -2,6 +2,13 @@ const locale = env.locale; const text = mdn.localStringMap({ + 'de': { + 'Reference': 'Referenz', + 'Elements': 'Elemente', + 'Global attributes': 'Globale Attribute', + 'Guides': 'Anleitungen', + 'Examples': 'Beispiele', + }, 'en-US': { 'Reference': 'Reference', 'Elements': 'Elements', diff --git a/kumascript/macros/Non-standard_Header.ejs b/kumascript/macros/Non-standard_Header.ejs index f872203db3ed..65bf18302d0b 100644 --- a/kumascript/macros/Non-standard_Header.ejs +++ b/kumascript/macros/Non-standard_Header.ejs @@ -1,6 +1,7 @@ <% var title = mdn.localString({ + "de": "Kein Standard", "en-US": "Non-standard", "es": "No estándar", "fr": "Non standard", @@ -12,6 +13,7 @@ var title = mdn.localString({ }); var description = mdn.localString({ + "de": "Diese Funktion ist kein Standard und befindet sich nicht auf dem Weg zu einem Standard. Verwenden Sie sie nicht auf Produktionsseiten, die auf das Web ausgerichtet sind: Sie wird nicht für alle Benutzer funktionieren. Außerdem kann es große Inkompatibilitäten zwischen Implementierungen geben und das Verhalten kann sich in Zukunft ändern.", "en-US": "This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.", "es": "Esta característica no es parte de los estándares. No la uses en sitios Web en producción: no funcionará para todos los usuarios. Podrían haber también incompatibilidades considerables entre distintas implementaciones y el comportamiento podría cambiar en el futuro.", "fr": "Cette fonctionnalité n'est ni standard, ni en voie de standardisation. Ne l'utilisez pas pour des sites accessibles sur le Web : elle ne fonctionnera pas pour tout utilisateur. Il peut également y avoir d'importantes incompatibilités entre les implémentations et son comportement peut être modifié dans le futur.", diff --git a/kumascript/macros/NonStandardBadge.ejs b/kumascript/macros/NonStandardBadge.ejs index 6447ea095577..e5d95c94cd90 100644 --- a/kumascript/macros/NonStandardBadge.ejs +++ b/kumascript/macros/NonStandardBadge.ejs @@ -6,6 +6,7 @@ */ const title = mdn.localString({ + "de": "Kein Standard. Überprüfen Sie die Unterstützung in verschiedenen Browsern, bevor Sie es verwenden.", "en-US": "Non-standard. Check cross-browser support before using.", "ko": "비표준. 사용하기전에 다른 브라우저에서도 사용 가능한지 확인 해주세요.", "zh-CN": "非标准。请在使用前检查跨浏览器支持。", @@ -13,7 +14,8 @@ const title = mdn.localString({ }); const abbreviation = mdn.localString({ - "en-US": "Non-standard", + "de": "Nicht standardisiert", + "en-US": "Kein Standard", "ko": "비표준", "zh-CN": "非标准", "zh-TW": "非標準" diff --git a/kumascript/macros/PreviousMenuNext.ejs b/kumascript/macros/PreviousMenuNext.ejs index 820d55c32de1..eda4f3e63aa2 100644 --- a/kumascript/macros/PreviousMenuNext.ejs +++ b/kumascript/macros/PreviousMenuNext.ejs @@ -15,6 +15,7 @@ const nextPage = $1?.replace(/ /g, "_"); const mainMenu = $2?.replace(/ /g, "_"); const previousNextStr = mdn.localString({ + "de" : [" Vorherige ", " Nächste "], "en-US": [" Previous ", " Next "], "es" : [" Anterior ", " Siguiente "], "fr" : [" Précédent ", " Suivant "], @@ -27,6 +28,7 @@ const previousNextStr = mdn.localString({ }); const menuStr = mdn.localString({ + "de" : " Übersicht: ", "en-US": " Overview: ", "pt-BR": " Menu: ", "fr" : " Aperçu : ", diff --git a/kumascript/macros/ReadOnlyInline.ejs b/kumascript/macros/ReadOnlyInline.ejs index 3aa43a078e1b..5815d8ca2b14 100644 --- a/kumascript/macros/ReadOnlyInline.ejs +++ b/kumascript/macros/ReadOnlyInline.ejs @@ -1,5 +1,6 @@ <% var str = mdn.localString({ + "de": "Nur lesen ", "en-US": "Read only ", "fr": "Lecture seule ", "ja": "読取専用 ", @@ -10,6 +11,7 @@ var str = mdn.localString({ }); var title = mdn.localString({ + "de": "Dieser Wert kann nicht geändert werden.", "en-US": "This value may not be changed.", "fr": "Cette valeur ne peut pas être changée.", "ko": "이 값은 변경 할 수 없습니다.", diff --git a/kumascript/macros/SVGAttr.ejs b/kumascript/macros/SVGAttr.ejs index c8f0c8ca2432..eacc71a79bbd 100644 --- a/kumascript/macros/SVGAttr.ejs +++ b/kumascript/macros/SVGAttr.ejs @@ -1,6 +1,7 @@ <% /* one parameter: attribute name */ var slug = mdn.localString({ + "de": "Attribut", "en-US": "Attribute", }); diff --git a/kumascript/macros/SVGRef.ejs b/kumascript/macros/SVGRef.ejs index 10793a67c668..eb6356df88a6 100644 --- a/kumascript/macros/SVGRef.ejs +++ b/kumascript/macros/SVGRef.ejs @@ -2,6 +2,14 @@ const locale = env.locale; const text = mdn.localStringMap({ + 'de': { + 'Tutorials': 'Tutorials', + 'Reference': 'Referenz', + 'Elements': 'Elemente', + 'Attributes': 'Attribute', + 'Guides': 'Leitfäden', + 'Introducing SVG from scratch': 'SVG von Grund auf neu einführen' + }, 'en-US': { 'Tutorials': 'Tutorials', 'Reference': 'Reference', diff --git a/kumascript/macros/SeeCompatTable.ejs b/kumascript/macros/SeeCompatTable.ejs index 0cf7df921472..3ef886726af5 100644 --- a/kumascript/macros/SeeCompatTable.ejs +++ b/kumascript/macros/SeeCompatTable.ejs @@ -1,6 +1,7 @@ <% const str = mdn.localString({ + "de": "Dies ist eine experimentelle Technologie
    Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese in der Produktion verwenden.", "es": "Esta es una tecnología experimental
    Comprueba la Tabla de compabilidad de navegadores cuidadosamente antes de usarla en producción.", "fr": "Cette fonction est expérimentale
    Puisque cette fonction est toujours en développement dans certains navigateurs, veuillez consulter le tableau de compatibilité pour les préfixes à utiliser selon les navigateurs.
    Il convient de noter qu'une fonctionnalité expérimentale peut voir sa syntaxe ou son comportement modifié dans le futur en fonction des évolutions de la spécification.", "ja": "これは実験的な機能です。
    本番で使用する前にブラウザー互換性一覧表をチェックしてください。", @@ -13,6 +14,7 @@ const str = mdn.localString({ }); const title = mdn.localString({ + "de": "Experimentell", "en-US": "Experimental", "es": "Experimental", "fr": "Expérimental", diff --git a/kumascript/macros/WebAssemblySidebar.ejs b/kumascript/macros/WebAssemblySidebar.ejs index 878983856c9e..ed973960519c 100644 --- a/kumascript/macros/WebAssemblySidebar.ejs +++ b/kumascript/macros/WebAssemblySidebar.ejs @@ -3,6 +3,19 @@ var locale = env.locale; var baseURL = "/" + locale + "/docs/WebAssembly"; var text = mdn.localStringMap({ + 'de': { + 'WebAssembly_home_page' : 'WebAssembly-Startseite', + 'Tutorials' : 'Anleitungen', + 'WebAssembly_concepts' : 'WebAssembly-Konzepte', + 'Compiling_to_wasm' : 'Kompilierung von C/C++ zu WebAssembly', + 'Compiling_rust_to_wasm' : 'Kompilierung von Rust zu WebAssembly', + 'JavaScript_API' : 'Verwendung der WebAssembly JavaScript API', + 'Text_format' : 'Verständnis des WebAssembly-Textformats', + 'Text_format_to_wasm' : 'Umwandlung des WebAssembly-Textformats in wasm', + 'Loading_and_running' : 'Laden und Ausführen von WebAssembly-Code', + 'Exported_functions' : 'Exportierte WebAssembly-Funktionen', + 'JavaScript_interface' : 'JavaScript-Schnittstelle' + }, 'en-US': { 'WebAssembly_home_page' : 'WebAssembly home page', 'Tutorials' : 'Tutorials', diff --git a/kumascript/macros/js_property_attributes.ejs b/kumascript/macros/js_property_attributes.ejs index 20d46124ffa7..de5fd484e063 100644 --- a/kumascript/macros/js_property_attributes.ejs +++ b/kumascript/macros/js_property_attributes.ejs @@ -14,6 +14,14 @@ const isEnumerable = $1 == 1; const isConfigurable = $2 == 1; const text = mdn.localStringMap({ + "de": { + header: `Eigenschaftsattribute von ${env.title}`, + writableName: "Schreibbar", + enumerableName: "Aufzählbar", + configurableName: "Konfigurierbar", + yes: "ja", + no: "nein" + }, "en-US": { header: `Property attributes of ${env.title}`, writableName: "Writable", diff --git a/kumascript/macros/no_tag_omission.ejs b/kumascript/macros/no_tag_omission.ejs index 8b422882c0e9..7ccf9f727bb7 100644 --- a/kumascript/macros/no_tag_omission.ejs +++ b/kumascript/macros/no_tag_omission.ejs @@ -3,6 +3,7 @@ /* This string will be extended in the future as there are discussion to apply animation-delay to non-animatable property */ var str = mdn.localString({ + "de": "Keine, sowohl das Start- als auch das End-Tag sind obligatorisch.", "en-US": "None, both the starting and ending tag are mandatory.", "fr": "Aucune, la balise d'ouverture et la balise de fermeture sont obligatoires", "ja": "不可。開始と終了タグの両方が必要。", diff --git a/kumascript/macros/secureContext_header.ejs b/kumascript/macros/secureContext_header.ejs index 01180070a13d..813759272057 100644 --- a/kumascript/macros/secureContext_header.ejs +++ b/kumascript/macros/secureContext_header.ejs @@ -6,6 +6,7 @@ var lang = env.locale; var str_title = mdn.localString({ + "de" : "Sicherer Kontext", "en-US": "Secure context", "es" : "Contexto seguro", "fr" : "Contexte sécurisé", @@ -14,6 +15,7 @@ var str_title = mdn.localString({ }); var str_desc = mdn.localString({ + "de" : "Diese Funktion ist nur in sicheren Kontexten (HTTPS) in einigen oder allen unterstützenden Browsern verfügbar.", "en-US": "This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.", "es" : "Esta función está disponible solo en contextos seguros (HTTPS), en algunos o todos los navegadores que lo soportan.", "fr" : "Cette fonctionnalité est uniquement disponible dans des contextes sécurisés (HTTPS), pour certains navigateurs qui la prennent en charge.", diff --git a/kumascript/macros/xref_csscomputed.ejs b/kumascript/macros/xref_csscomputed.ejs index 69b01e029b41..8f0d87137ff4 100644 --- a/kumascript/macros/xref_csscomputed.ejs +++ b/kumascript/macros/xref_csscomputed.ejs @@ -1,5 +1,6 @@ <% let linkText = mdn.localString({ + "de" : "Berechneter Wert", "en-US": "Computed value", "es" : "Valor calculado", "fr" : "Valeur calculée", diff --git a/kumascript/macros/xref_cssinherited.ejs b/kumascript/macros/xref_cssinherited.ejs index 3abfeb1078e1..afe42f231a38 100644 --- a/kumascript/macros/xref_cssinherited.ejs +++ b/kumascript/macros/xref_cssinherited.ejs @@ -1,5 +1,6 @@ <% let linkText = mdn.localString({ + "de" : "Vererbt", "en-US": "Inherited", "es" : "Heredable", "fr" : "Héritée", diff --git a/kumascript/macros/xref_cssinitial.ejs b/kumascript/macros/xref_cssinitial.ejs index de693f2c0f27..bf24881827fc 100644 --- a/kumascript/macros/xref_cssinitial.ejs +++ b/kumascript/macros/xref_cssinitial.ejs @@ -1,5 +1,6 @@ <% let linkText = mdn.localString({ + "de" : "Initialer Wert", "en-US": "Initial value", "es" : "Valor inicial", "fr" : "Valeur initiale", From d96801a280ef2f67e2f679e9146f4954f4cec1de Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 12 Jun 2024 13:18:46 +0200 Subject: [PATCH 014/100] chore(client,libs): add German translation --- client/src/document/baseline-indicator.tsx | 1 + libs/l10n/l10n.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/client/src/document/baseline-indicator.tsx b/client/src/document/baseline-indicator.tsx index 6b5484c6e3c6..4cba50b7cfff 100644 --- a/client/src/document/baseline-indicator.tsx +++ b/client/src/document/baseline-indicator.tsx @@ -39,6 +39,7 @@ const ENGINES: { ]; const LOCALIZED_BCD_IDS = { + de: "browserkompatibilität", "en-US": "browser_compatibility", es: "compatibilidad_con_navegadores", fr: "compatibilité_des_navigateurs", diff --git a/libs/l10n/l10n.ts b/libs/l10n/l10n.ts index 8f5b656cf56d..33e440be4934 100644 --- a/libs/l10n/l10n.ts +++ b/libs/l10n/l10n.ts @@ -7,6 +7,7 @@ function localeString(strings: LocaleStringMap) { } export const ONLY_AVAILABLE_IN_ENGLISH = localeString({ + de: "Diese Seite ist derzeit nur auf Englisch verfügbar", "en-US": "This page is currently only available in English", es: "Esta página está disponible solo en inglés", fr: "Cette page est actuellement disponible uniquement en anglais", From 52883d1dfa844d6c54d2a67388f33b35e65b26e8 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 12 Jun 2024 13:19:03 +0200 Subject: [PATCH 015/100] chore(libs): add "de" to Locale type --- libs/types/core.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/types/core.ts b/libs/types/core.ts index 5ba1b2a5a985..bb72099f51eb 100644 --- a/libs/types/core.ts +++ b/libs/types/core.ts @@ -1,4 +1,5 @@ export type Locale = + | "de" | "en-US" | "es" | "fr" From 3a5c8d8c9e5db8c541501fe74c03e8a31fe606a1 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 12 Jun 2024 14:36:21 +0200 Subject: [PATCH 016/100] enhance(toc): localize "In this article" header --- client/src/document/organisms/toc/index.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/client/src/document/organisms/toc/index.tsx b/client/src/document/organisms/toc/index.tsx index dd2f7bb1dec2..2364af6da52f 100644 --- a/client/src/document/organisms/toc/index.tsx +++ b/client/src/document/organisms/toc/index.tsx @@ -5,8 +5,23 @@ import { Toc } from "../../../../../libs/types/document"; import { useFirstVisibleElement } from "../../hooks"; import { useGleanClick } from "../../../telemetry/glean-context"; import { TOC_CLICK } from "../../../telemetry/constants"; +import { useLocale } from "../../../hooks"; +import { DEFAULT_LOCALE } from "../../../../../libs/constants"; + +const DEFAULT_TITLE = { + "en-US": "In this article", + es: "En este artículo", + fr: "Dans cet article", + ja: "この記事では", + ko: "이 문서에서는", + "pt-BR": "Neste artigo", + ru: "В этой статье", + "zh-CN": "在本文中", + "zh-TW": "在本文中", +}; export function TOC({ toc, title }: { toc: Toc[]; title?: string }) { + const locale = useLocale(); const [currentViewedTocItem, setCurrentViewedTocItem] = useState(""); const observedElements = React.useCallback(() => { @@ -45,7 +60,7 @@ export function TOC({ toc, title }: { toc: Toc[]; title?: string }) {

    - {title || "In this article"} + {title || DEFAULT_TITLE[locale] || DEFAULT_TITLE[DEFAULT_LOCALE]}

      From 9e3d0694801d3f22fabc03c14e856be04539fbba Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 12 Jun 2024 15:34:16 +0200 Subject: [PATCH 017/100] fixup! enhance(toc): localize "In this article" header --- client/src/document/organisms/toc/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/document/organisms/toc/index.tsx b/client/src/document/organisms/toc/index.tsx index 2364af6da52f..ea6411afb956 100644 --- a/client/src/document/organisms/toc/index.tsx +++ b/client/src/document/organisms/toc/index.tsx @@ -9,6 +9,7 @@ import { useLocale } from "../../../hooks"; import { DEFAULT_LOCALE } from "../../../../../libs/constants"; const DEFAULT_TITLE = { + de: "In diesem Artikel", "en-US": "In this article", es: "En este artículo", fr: "Dans cet article", From d16827179f82c92953ca88f26cb1c09313c902cb Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 24 Jun 2024 11:04:22 +0200 Subject: [PATCH 018/100] fixup! Merge branch 'main' into test-de --- .github/workflows/test-de-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 880fca60a19e..0f96353c4735 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -186,7 +186,7 @@ jobs: # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. for locale in en-us de; do - yarn build:json --locale $locale 2>&1 | sed "s/^/[$locale] /" & + yarn build:docs --locale $locale 2>&1 | sed "s/^/[$locale] /" & pids+=($!) done From 7dd78b81184725896d77b5b227350b118276f31a Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 23 Jul 2024 11:54:48 +0200 Subject: [PATCH 019/100] chore(deps): add cookie-parser to /cloud-function --- cloud-function/package-lock.json | 34 ++++++++++++++++++++++++++++++++ cloud-function/package.json | 2 ++ cloud-function/src/app.ts | 2 ++ 3 files changed, 38 insertions(+) diff --git a/cloud-function/package-lock.json b/cloud-function/package-lock.json index 9ff4f349ac23..837bbd8d96cc 100644 --- a/cloud-function/package-lock.json +++ b/cloud-function/package-lock.json @@ -18,6 +18,7 @@ "@yari-internal/pong": "file:src/internal/pong", "@yari-internal/slug-utils": "file:src/internal/slug-utils", "accept-language-parser": "^1.5.0", + "cookie-parser": "^1.4.6", "dotenv": "^16.0.3", "express": "^4.19.2", "http-proxy-middleware": "^3.0.0", @@ -26,6 +27,7 @@ "devDependencies": { "@swc/core": "^1.3.38", "@types/accept-language-parser": "^1.5.3", + "@types/cookie-parser": "^1.4.7", "@types/http-proxy": "^1.17.10", "@types/http-server": "^0.12.1", "cross-env": "^7.0.3", @@ -541,6 +543,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -1024,6 +1036,28 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "license": "MIT", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/cloud-function/package.json b/cloud-function/package.json index 2ff6b8639f34..1c211ffa7ac6 100644 --- a/cloud-function/package.json +++ b/cloud-function/package.json @@ -36,6 +36,7 @@ "@yari-internal/pong": "file:src/internal/pong", "@yari-internal/slug-utils": "file:src/internal/slug-utils", "accept-language-parser": "^1.5.0", + "cookie-parser": "^1.4.6", "dotenv": "^16.0.3", "express": "^4.19.2", "http-proxy-middleware": "^3.0.0", @@ -44,6 +45,7 @@ "devDependencies": { "@swc/core": "^1.3.38", "@types/accept-language-parser": "^1.5.3", + "@types/cookie-parser": "^1.4.7", "@types/http-proxy": "^1.17.10", "@types/http-server": "^0.12.1", "cross-env": "^7.0.3", diff --git a/cloud-function/src/app.ts b/cloud-function/src/app.ts index 1f7221f70d4f..efa3bfe2f380 100644 --- a/cloud-function/src/app.ts +++ b/cloud-function/src/app.ts @@ -1,3 +1,4 @@ +import cookieParser from "cookie-parser"; import express, { Request, Response } from "express"; import { Router } from "express"; @@ -25,6 +26,7 @@ import { stripForwardedHostHeaders } from "./middlewares/stripForwardedHostHeade import { proxyPong } from "./handlers/proxy-pong.js"; const router = Router(); +router.use(cookieParser()); router.use(stripForwardedHostHeaders); router.use(redirectLeadingSlash); // MDN Plus plans. From 7b79f24fdc9015ae3834f0a832e08ca96a7bbc71 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 23 Jul 2024 11:56:24 +0200 Subject: [PATCH 020/100] refactor(cloud-function): extract canonicals module --- cloud-function/src/canonicals.ts | 5 +++++ .../src/middlewares/redirect-non-canonicals.ts | 11 ++++------- 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 cloud-function/src/canonicals.ts diff --git a/cloud-function/src/canonicals.ts b/cloud-function/src/canonicals.ts new file mode 100644 index 000000000000..92c5b7ec5ba8 --- /dev/null +++ b/cloud-function/src/canonicals.ts @@ -0,0 +1,5 @@ +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); + +export const CANONICALS = require("../canonicals.json"); diff --git a/cloud-function/src/middlewares/redirect-non-canonicals.ts b/cloud-function/src/middlewares/redirect-non-canonicals.ts index 0c8f1d3eb1b3..57390983bdad 100644 --- a/cloud-function/src/middlewares/redirect-non-canonicals.ts +++ b/cloud-function/src/middlewares/redirect-non-canonicals.ts @@ -1,12 +1,9 @@ -import { createRequire } from "node:module"; - import { NextFunction, Request, Response } from "express"; import { THIRTY_DAYS } from "../constants.js"; import { normalizePath, redirect } from "../utils.js"; +import { CANONICALS } from "../canonicals.js"; -const require = createRequire(import.meta.url); -const REDIRECTS = require("../../canonicals.json"); const REDIRECT_SUFFIXES = ["/index.json", "/bcd.json", ""]; export async function redirectNonCanonicals( @@ -31,10 +28,10 @@ export async function redirectNonCanonicals( ); const source = normalizePath(originalSource); if ( - typeof REDIRECTS[source] == "string" && - REDIRECTS[source] !== originalSource + typeof CANONICALS[source] == "string" && + CANONICALS[source] !== originalSource ) { - const target = joinPath(REDIRECTS[source], suffix) + parsedUrl.search; + const target = joinPath(CANONICALS[source], suffix) + parsedUrl.search; if (pathname !== target) { return redirect(res, target, { status: 301, From 91de9b8150b5f3e92cf117610f6698cce9a73cce Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 23 Jul 2024 11:57:16 +0200 Subject: [PATCH 021/100] feat(cloud-function): always redirect to preferred locale Unless the user manually switched the locale for the current page. --- cloud-function/src/app.ts | 2 + .../middlewares/redirect-preferred-locale.ts | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 cloud-function/src/middlewares/redirect-preferred-locale.ts diff --git a/cloud-function/src/app.ts b/cloud-function/src/app.ts index efa3bfe2f380..7cc61f1cd59c 100644 --- a/cloud-function/src/app.ts +++ b/cloud-function/src/app.ts @@ -17,6 +17,7 @@ import { redirectMovedPages } from "./middlewares/redirect-moved-pages.js"; import { redirectEnforceTrailingSlash } from "./middlewares/redirect-enforce-trailing-slash.js"; import { redirectFundamental } from "./middlewares/redirect-fundamental.js"; import { redirectLocale } from "./middlewares/redirect-locale.js"; +import { redirectPreferredLocale } from "./middlewares/redirect-preferred-locale.js"; import { redirectTrailingSlash } from "./middlewares/redirect-trailing-slash.js"; import { requireOrigin } from "./middlewares/require-origin.js"; import { notFound } from "./middlewares/not-found.js"; @@ -87,6 +88,7 @@ router.get( requireOrigin(Origin.main), redirectFundamental, redirectLocale, + redirectPreferredLocale, redirectTrailingSlash, redirectMovedPages, resolveIndexHTML, diff --git a/cloud-function/src/middlewares/redirect-preferred-locale.ts b/cloud-function/src/middlewares/redirect-preferred-locale.ts new file mode 100644 index 000000000000..54f974eb6b25 --- /dev/null +++ b/cloud-function/src/middlewares/redirect-preferred-locale.ts @@ -0,0 +1,60 @@ +import { NextFunction, Request, Response } from "express"; +import { normalizePath, redirect } from "../utils.js"; +import { CANONICALS } from "../canonicals.js"; + +export async function redirectPreferredLocale( + req: Request, + res: Response, + next: NextFunction +) { + // Check 1: Does the user prefer a locale? + + const preferredLocale = req.cookies["preferredlocale"]; + + if (!preferredLocale) { + next(); + return; + } + + // Check 2: Does the target have a different locale? + + const target = new URL(req.url, `${req.protocol}://${req.headers.host}`); + const targetPathname = target.pathname; + const [targetLocale, targetSlug] = localeAndSlugOf(target); + + if (targetLocale.toLowerCase() === preferredLocale.toLowerCase()) { + next(); + return; + } + + // Check 3: Did the user manually switch the locale? + + if (req.headers.referer) { + const source = new URL(req.headers.referer); + const [, sourceSlug] = localeAndSlugOf(source); + + if (targetSlug.toLowerCase() === sourceSlug.toLowerCase()) { + // User manually switched locale. + next(); + return; + } + } + + // Check 4: Does the target exist in the preferred locale? + + const preferredPathname = + CANONICALS[normalizePath(`/${preferredLocale}/${targetSlug}`)] ?? null; + if (preferredPathname && preferredPathname !== targetPathname) { + const location = preferredPathname + target.search; + return redirect(res, location); + } + + next(); +} + +function localeAndSlugOf(url: URL): [string, string] { + const locale = url.pathname.split("/").at(1) || ""; + const slug = url.pathname.split("/").slice(2).join("/"); + + return [locale, slug]; +} From f047dec87a5e9da4995833a486249f11f52be3e0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 23 Jul 2024 12:38:35 +0200 Subject: [PATCH 022/100] chore(cloud-function): ignore referer for preferred locale redirect --- .../src/middlewares/redirect-preferred-locale.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/cloud-function/src/middlewares/redirect-preferred-locale.ts b/cloud-function/src/middlewares/redirect-preferred-locale.ts index 54f974eb6b25..479c7be5b22a 100644 --- a/cloud-function/src/middlewares/redirect-preferred-locale.ts +++ b/cloud-function/src/middlewares/redirect-preferred-locale.ts @@ -27,20 +27,7 @@ export async function redirectPreferredLocale( return; } - // Check 3: Did the user manually switch the locale? - - if (req.headers.referer) { - const source = new URL(req.headers.referer); - const [, sourceSlug] = localeAndSlugOf(source); - - if (targetSlug.toLowerCase() === sourceSlug.toLowerCase()) { - // User manually switched locale. - next(); - return; - } - } - - // Check 4: Does the target exist in the preferred locale? + // Check 3: Does the target exist in the preferred locale? const preferredPathname = CANONICALS[normalizePath(`/${preferredLocale}/${targetSlug}`)] ?? null; From 213869f2568576a35e0cd3095989ecd61a2fcbdb Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 13:08:48 +0200 Subject: [PATCH 023/100] refactor(language-menu): extract {get,set}CookieValue() --- .../article-actions/language-menu/index.tsx | 23 ++++----------- client/src/utils.ts | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 755527d8f77b..20ddf2694d77 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -10,6 +10,7 @@ import "./index.scss"; import { DropdownMenu, DropdownMenuWrapper } from "../../../molecules/dropdown"; import { useLocale } from "../../../../hooks"; import { LANGUAGE } from "../../../../telemetry/constants"; +import { getCookieValue, setCookieValue } from "../../../../utils"; // This needs to match what's set in 'libs/constants.js' on the server/builder! const PREFERRED_LOCALE_COOKIE_NAME = "preferredlocale"; @@ -33,27 +34,13 @@ export function LanguageMenu({ // The default is the current locale itself. If that's what's chosen, // don't bother redirecting. if (preferredLocale !== locale) { - let cookieValueBefore = document.cookie - .split("; ") - .find((row) => row.startsWith(`${PREFERRED_LOCALE_COOKIE_NAME}=`)); - if (cookieValueBefore && cookieValueBefore.includes("=")) { - cookieValueBefore = cookieValueBefore.split("=")[1]; - } + const cookieValueBefore = getCookieValue(PREFERRED_LOCALE_COOKIE_NAME); for (const translation of translations) { if (translation.locale === preferredLocale) { - let cookieValue = `${PREFERRED_LOCALE_COOKIE_NAME}=${ - translation.locale - };max-age=${60 * 60 * 24 * 365 * 3};path=/`; - if ( - !( - document.location.hostname === "localhost" || - document.location.hostname === "localhost.org" - ) - ) { - cookieValue += ";secure"; - } - document.cookie = cookieValue; + setCookieValue(PREFERRED_LOCALE_COOKIE_NAME, translation.locale, { + maxAge: 60 * 60 * 24 * 365 * 3, + }); } } diff --git a/client/src/utils.ts b/client/src/utils.ts index 1105e08da90f..058b18d340a7 100644 --- a/client/src/utils.ts +++ b/client/src/utils.ts @@ -136,3 +136,32 @@ export function splitQuery(term: string): string[] { return term.split(/[ ,.]+/); } } + +export function getCookieValue(name: string) { + let value = document.cookie + .split("; ") + .find((row) => row.startsWith(`${name}=`)); + + if (value && value.includes("=")) { + value = value.split("=")[1]; + } + + return value; +} + +export function setCookieValue( + name: string, + value: string, + { maxAge, path = "/" }: { maxAge: number; path?: string } +) { + const cookieValue = [ + `${name}=${value}`, + maxAge && `max-age=${maxAge}`, + `path=${path}`, + document.location.hostname !== "localhost" && "secure", + ] + .filter(Boolean) + .join(";"); + + document.cookie = cookieValue; +} From 1cc7ef7f81c04acccf484199daae309bfc578764 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 13:46:03 +0200 Subject: [PATCH 024/100] fix(switch): use em instead of rem This makes the Switch appear as big as surrounding text. --- client/src/ui/atoms/switch/index.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/ui/atoms/switch/index.scss b/client/src/ui/atoms/switch/index.scss index 04021cdbecfe..e0194700b6c1 100644 --- a/client/src/ui/atoms/switch/index.scss +++ b/client/src/ui/atoms/switch/index.scss @@ -29,10 +29,10 @@ background-color: var(--text-secondary); border-radius: 1.5em; cursor: pointer; - height: 1.5rem; + height: 1.5em; position: absolute; transition: 0.4s; - width: 3rem; + width: 3em; &:before { background-color: var(--background-primary); From dcfcaa05b97ce0735110fd48b311f015ca858b5b Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 13:46:56 +0200 Subject: [PATCH 025/100] feat(language-menu): add automatic redirect setting --- .../article-actions/language-menu/index.scss | 11 ++++ .../article-actions/language-menu/index.tsx | 66 ++++++++++++++++--- client/src/utils.ts | 11 +++- .../middlewares/redirect-preferred-locale.ts | 5 +- 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index 8454294cade8..2c794fc9bd77 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -14,4 +14,15 @@ @media (min-width: $screen-md) { right: 0; } + + .locale-redirect-setting { + border: 1px solid var(--border-secondary); + border-radius: 1rem; + font-size: var(--type-tiny-font-size); + padding: 0.25em 0.5em; + + .glean-thumbs { + margin-top: 0.25em; + } + } } diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 20ddf2694d77..8fb5a1a2ff26 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -10,10 +10,17 @@ import "./index.scss"; import { DropdownMenu, DropdownMenuWrapper } from "../../../molecules/dropdown"; import { useLocale } from "../../../../hooks"; import { LANGUAGE } from "../../../../telemetry/constants"; -import { getCookieValue, setCookieValue } from "../../../../utils"; +import { + deleteCookie, + getCookieValue, + setCookieValue, +} from "../../../../utils"; +import { GleanThumbs } from "../../../atoms/thumbs"; +import { Switch } from "../../../atoms/switch"; // This needs to match what's set in 'libs/constants.js' on the server/builder! const PREFERRED_LOCALE_COOKIE_NAME = "preferredlocale"; +const REDIRECT_LOCALE_COOKIE_NAME = "redirectlocale"; export function LanguageMenu({ onClose, @@ -52,15 +59,20 @@ export function LanguageMenu({ const menuEntry = { label: "Languages", id: menuId, - items: translations.map((translation) => ({ - component: () => ( - - ), - })), + items: [ + { + component: () => , + }, + ...translations.map((translation) => ({ + component: () => ( + + ), + })), + ], }; return ( @@ -114,3 +126,37 @@ function LanguageMenuItem({ ); } + +function LocaleRedirectSetting() { + const TRUE_VALUE = "true"; + const [value, setValue] = useState( + () => getCookieValue(REDIRECT_LOCALE_COOKIE_NAME) === TRUE_VALUE + ); + + function toggle(event) { + const newValue = event.target.checked; + if (newValue) { + setCookieValue(REDIRECT_LOCALE_COOKIE_NAME, TRUE_VALUE, { + maxAge: 60 * 60 * 24 * 365 * 3, + }); + } else { + deleteCookie(REDIRECT_LOCALE_COOKIE_NAME); + } + setValue(event.target.checked); + } + + return ( +
      + + + + ); +} diff --git a/client/src/utils.ts b/client/src/utils.ts index 058b18d340a7..c1917d58d406 100644 --- a/client/src/utils.ts +++ b/client/src/utils.ts @@ -152,10 +152,15 @@ export function getCookieValue(name: string) { export function setCookieValue( name: string, value: string, - { maxAge, path = "/" }: { maxAge: number; path?: string } + { + expires, + maxAge, + path = "/", + }: { expires?: Date; maxAge?: number; path?: string } ) { const cookieValue = [ `${name}=${value}`, + expires && `expires=${expires.toUTCString()}`, maxAge && `max-age=${maxAge}`, `path=${path}`, document.location.hostname !== "localhost" && "secure", @@ -165,3 +170,7 @@ export function setCookieValue( document.cookie = cookieValue; } + +export function deleteCookie(name: string) { + setCookieValue(name, "", { expires: new Date(0) }); +} diff --git a/cloud-function/src/middlewares/redirect-preferred-locale.ts b/cloud-function/src/middlewares/redirect-preferred-locale.ts index 479c7be5b22a..763a20fe02b7 100644 --- a/cloud-function/src/middlewares/redirect-preferred-locale.ts +++ b/cloud-function/src/middlewares/redirect-preferred-locale.ts @@ -7,11 +7,12 @@ export async function redirectPreferredLocale( res: Response, next: NextFunction ) { - // Check 1: Does the user prefer a locale? + // Check 1: Does the user prefer a locale and has redirect enabled? const preferredLocale = req.cookies["preferredlocale"]; + const redirectLocale = req.cookies["redirectLocale"]; - if (!preferredLocale) { + if (!preferredLocale || !redirectLocale) { next(); return; } From c5a4c6211f745ae4ac00c5f81ea9d8e5e6bea194 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 13:49:23 +0200 Subject: [PATCH 026/100] chore(language-menu): reduce item padding --- .../ui/organisms/article-actions/language-menu/index.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index 2c794fc9bd77..ffd49a3e5353 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -8,7 +8,7 @@ .language-menu { .submenu-item { - padding: 0.5rem; + padding: 0.5rem !important; } @media (min-width: $screen-md) { @@ -16,8 +16,7 @@ } .locale-redirect-setting { - border: 1px solid var(--border-secondary); - border-radius: 1rem; + border-bottom: 1px solid var(--border-secondary); font-size: var(--type-tiny-font-size); padding: 0.25em 0.5em; From 3c011611d0ef6c775aa5dc9636c49b8c11f43360 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 13:52:50 +0200 Subject: [PATCH 027/100] chore(language-menu): add slight padding between items --- .../organisms/article-actions/language-menu/index.scss | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index ffd49a3e5353..f709080cfb5e 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -7,6 +7,16 @@ } .language-menu { + li { + &:not(:first-child) { + padding-top: 1px; + } + + &:not(:last-child) { + padding-bottom: 1px; + } + } + .submenu-item { padding: 0.5rem !important; } From b72d27e221e95cd1c8381b6e5d38c8183570a10e Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 13:58:38 +0200 Subject: [PATCH 028/100] chore(language-menu): revisit thumbs confirmationg --- client/src/ui/organisms/article-actions/language-menu/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 8fb5a1a2ff26..db4ffa7abf96 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -155,7 +155,7 @@ function LocaleRedirectSetting() { ); From 256fc317df4e19474c9f007bd15005dc94b49bba Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 14:41:12 +0200 Subject: [PATCH 029/100] fix(language-menu): avoid document.cookie access on SSR --- .../organisms/article-actions/language-menu/index.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index db4ffa7abf96..f5f0a839fe7f 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; import { useGleanClick } from "../../../../telemetry/glean-context"; @@ -129,9 +129,11 @@ function LanguageMenuItem({ function LocaleRedirectSetting() { const TRUE_VALUE = "true"; - const [value, setValue] = useState( - () => getCookieValue(REDIRECT_LOCALE_COOKIE_NAME) === TRUE_VALUE - ); + const [value, setValue] = useState(false); + + useEffect(() => { + setValue(getCookieValue(REDIRECT_LOCALE_COOKIE_NAME) === TRUE_VALUE); + }, [setValue]); function toggle(event) { const newValue = event.target.checked; From f95cb9314f0bf9333d428f1625614fa2113c81fe Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 15:04:40 +0200 Subject: [PATCH 030/100] fix(language-menu): set preferredlocale on redirect --- .../ui/organisms/article-actions/language-menu/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index f5f0a839fe7f..30a3d690a163 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -129,6 +129,7 @@ function LanguageMenuItem({ function LocaleRedirectSetting() { const TRUE_VALUE = "true"; + const locale = useLocale(); const [value, setValue] = useState(false); useEffect(() => { @@ -138,6 +139,11 @@ function LocaleRedirectSetting() { function toggle(event) { const newValue = event.target.checked; if (newValue) { + if (!getCookieValue(PREFERRED_LOCALE_COOKIE_NAME)) { + setCookieValue(PREFERRED_LOCALE_COOKIE_NAME, locale, { + maxAge: 60 * 60 * 24 * 365 * 3, + }); + } setCookieValue(REDIRECT_LOCALE_COOKIE_NAME, TRUE_VALUE, { maxAge: 60 * 60 * 24 * 365 * 3, }); From a3be882d7314bab78a43052550c8e793641435e5 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 15:05:26 +0200 Subject: [PATCH 031/100] fix(language-menu): measure locale-redirct clicks --- client/src/telemetry/constants.ts | 1 + .../src/ui/organisms/article-actions/language-menu/index.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/telemetry/constants.ts b/client/src/telemetry/constants.ts index 18042dcd2cd4..afef3515a706 100644 --- a/client/src/telemetry/constants.ts +++ b/client/src/telemetry/constants.ts @@ -82,5 +82,6 @@ export const BASELINE = Object.freeze({ export const CLIENT_SIDE_NAVIGATION = "client_side_nav"; export const LANGUAGE = "language"; +export const LANGUAGE_REDIRECT = "language_redirect"; export const THEME_SWITCHER = "theme_switcher"; export const SURVEY = "survey"; diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 30a3d690a163..e4fec10d9cf8 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -9,7 +9,7 @@ import { Submenu } from "../../../molecules/submenu"; import "./index.scss"; import { DropdownMenu, DropdownMenuWrapper } from "../../../molecules/dropdown"; import { useLocale } from "../../../../hooks"; -import { LANGUAGE } from "../../../../telemetry/constants"; +import { LANGUAGE, LANGUAGE_REDIRECT } from "../../../../telemetry/constants"; import { deleteCookie, getCookieValue, @@ -129,6 +129,7 @@ function LanguageMenuItem({ function LocaleRedirectSetting() { const TRUE_VALUE = "true"; + const gleanClick = useGleanClick(); const locale = useLocale(); const [value, setValue] = useState(false); @@ -151,6 +152,7 @@ function LocaleRedirectSetting() { deleteCookie(REDIRECT_LOCALE_COOKIE_NAME); } setValue(event.target.checked); + gleanClick(`${LANGUAGE_REDIRECT}: ${locale} -> ${Number(newValue)}`); } return ( From 3de136b86ecafb5e5e89885bc6c0ce62953730fa Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 15:43:02 +0200 Subject: [PATCH 032/100] fix(switch): remove margin-left from label --- client/src/ui/atoms/switch/index.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/src/ui/atoms/switch/index.scss b/client/src/ui/atoms/switch/index.scss index e0194700b6c1..6bd97ac0492b 100644 --- a/client/src/ui/atoms/switch/index.scss +++ b/client/src/ui/atoms/switch/index.scss @@ -46,8 +46,4 @@ transition: 0.4s; } } - - .label { - margin-left: 0.5rem; - } } From f5b9badaab769a8884f185f6929092d9076652ff Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 17:51:07 +0200 Subject: [PATCH 033/100] fixup! fix(switch): use em instead of rem --- client/src/ui/atoms/switch/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/atoms/switch/index.scss b/client/src/ui/atoms/switch/index.scss index 6bd97ac0492b..108941fa98ed 100644 --- a/client/src/ui/atoms/switch/index.scss +++ b/client/src/ui/atoms/switch/index.scss @@ -7,7 +7,7 @@ height: 0; margin: 0; opacity: 0; - width: 3rem; + width: 4em; &:checked + .slider { background-color: var(--text-link); From dd6a4f16ec57dae08ee2ade3c0509fafd9d92c58 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 18:32:29 +0200 Subject: [PATCH 034/100] chore(language-menu): improve remember design --- .../article-actions/language-menu/index.scss | 30 ++++++++++++------- .../article-actions/language-menu/index.tsx | 16 ++++------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index f709080cfb5e..6968a5897f78 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -19,19 +19,29 @@ .submenu-item { padding: 0.5rem !important; - } - @media (min-width: $screen-md) { - right: 0; - } + &.locale-redirect-setting { + &:hover { + background-color: unset; + } + + border-bottom: 1px solid var(--border-secondary) !important; + border-radius: 0 !important; + font-size: var(--type-tiny-font-size); + display: block; + padding: 0.25em 0.5em; - .locale-redirect-setting { - border-bottom: 1px solid var(--border-secondary); - font-size: var(--type-tiny-font-size); - padding: 0.25em 0.5em; + .switch { + display: flex; + } - .glean-thumbs { - margin-top: 0.25em; + .glean-thumbs { + margin-top: 0.25em; + } } } + + @media (min-width: $screen-md) { + right: 0; + } } diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index e4fec10d9cf8..59b912c63e72 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -156,17 +156,11 @@ function LocaleRedirectSetting() { } return ( -
      - - + + + Remember language + + ); } From a3986b2bcc8e32890d2806d1443975073e827f2b Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 22:43:51 +0200 Subject: [PATCH 035/100] fix(switch): correct lengths --- client/src/ui/atoms/switch/index.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/ui/atoms/switch/index.scss b/client/src/ui/atoms/switch/index.scss index 108941fa98ed..c6d9392fbb62 100644 --- a/client/src/ui/atoms/switch/index.scss +++ b/client/src/ui/atoms/switch/index.scss @@ -7,7 +7,7 @@ height: 0; margin: 0; opacity: 0; - width: 4em; + width: 3em; &:checked + .slider { background-color: var(--text-link); @@ -46,4 +46,8 @@ transition: 0.4s; } } + + .label { + margin-left: 0.5em; + } } From dc9293ed95c84d627ba92d9dbb58b353ba34b32c Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 22:46:32 +0200 Subject: [PATCH 036/100] fix(thumbs): use em over rem Allows them to scale with local font-size. --- client/src/ui/atoms/thumbs/index.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/ui/atoms/thumbs/index.scss b/client/src/ui/atoms/thumbs/index.scss index a6c72f55312c..21ee53029669 100644 --- a/client/src/ui/atoms/thumbs/index.scss +++ b/client/src/ui/atoms/thumbs/index.scss @@ -2,8 +2,8 @@ align-items: center; display: flex; flex-direction: row; - gap: 0.5rem; - height: 1.5rem; + gap: 0.5em; + height: 1.5em; .confirmation { animation-duration: 2.5s; From dbf93925a94a52a742709543c2d582d3635bc9b0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 22:48:05 +0200 Subject: [PATCH 037/100] fix(language-menu): correct spacing --- .../article-actions/language-menu/index.scss | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index 6968a5897f78..67d1ce560cea 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -21,22 +21,26 @@ padding: 0.5rem !important; &.locale-redirect-setting { - &:hover { - background-color: unset; - } - border-bottom: 1px solid var(--border-secondary) !important; border-radius: 0 !important; - font-size: var(--type-tiny-font-size); display: block; + font-size: var(--type-tiny-font-size); padding: 0.25em 0.5em; + &:hover { + background-color: unset; + } + .switch { display: flex; } .glean-thumbs { - margin-top: 0.25em; + margin-top: 0.5em; + + .icon { + margin-right: unset; + } } } } From 60feaeefa3dcee7a69a45743773fdf86b61b5f80 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 12 Sep 2024 22:50:39 +0200 Subject: [PATCH 038/100] chore(language-menu): make thumbs text italic --- .../src/ui/organisms/article-actions/language-menu/index.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index 67d1ce560cea..8a9c211ef96a 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -36,6 +36,8 @@ } .glean-thumbs { + font-style: italic; + font-variation-settings: "slnt" -10; margin-top: 0.5em; .icon { From 258738d80f92b5e0cc9a1c6f69a7e1c31fda7b3f Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 13:51:58 +0200 Subject: [PATCH 039/100] feat(language-menu): reuse preferredlocale, make opt-in --- .../article-actions/language-menu/index.tsx | 28 ++++++++----------- .../middlewares/redirect-preferred-locale.ts | 3 +- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 59b912c63e72..dd13d4c70edd 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -20,7 +20,6 @@ import { Switch } from "../../../atoms/switch"; // This needs to match what's set in 'libs/constants.js' on the server/builder! const PREFERRED_LOCALE_COOKIE_NAME = "preferredlocale"; -const REDIRECT_LOCALE_COOKIE_NAME = "redirectlocale"; export function LanguageMenu({ onClose, @@ -37,22 +36,23 @@ export function LanguageMenu({ const [isOpen, setIsOpen] = useState(false); const changeLocale: React.MouseEventHandler = (event) => { - const preferredLocale = event.currentTarget.dataset.locale; + const newLocale = event.currentTarget.dataset.locale; // The default is the current locale itself. If that's what's chosen, // don't bother redirecting. - if (preferredLocale !== locale) { + if (newLocale !== locale) { const cookieValueBefore = getCookieValue(PREFERRED_LOCALE_COOKIE_NAME); - for (const translation of translations) { - if (translation.locale === preferredLocale) { - setCookieValue(PREFERRED_LOCALE_COOKIE_NAME, translation.locale, { - maxAge: 60 * 60 * 24 * 365 * 3, - }); + if (cookieValueBefore === locale) { + for (const translation of translations) { + if (translation.locale === newLocale) { + setCookieValue(PREFERRED_LOCALE_COOKIE_NAME, translation.locale, { + maxAge: 60 * 60 * 24 * 365 * 3, + }); + } } } - const oldValue = cookieValueBefore || "none"; - gleanClick(`${LANGUAGE}: ${oldValue} -> ${preferredLocale}`); + gleanClick(`${LANGUAGE}: ${locale} -> ${newLocale}`); } }; @@ -128,13 +128,12 @@ function LanguageMenuItem({ } function LocaleRedirectSetting() { - const TRUE_VALUE = "true"; const gleanClick = useGleanClick(); const locale = useLocale(); const [value, setValue] = useState(false); useEffect(() => { - setValue(getCookieValue(REDIRECT_LOCALE_COOKIE_NAME) === TRUE_VALUE); + setValue(!!getCookieValue(PREFERRED_LOCALE_COOKIE_NAME)); }, [setValue]); function toggle(event) { @@ -145,11 +144,8 @@ function LocaleRedirectSetting() { maxAge: 60 * 60 * 24 * 365 * 3, }); } - setCookieValue(REDIRECT_LOCALE_COOKIE_NAME, TRUE_VALUE, { - maxAge: 60 * 60 * 24 * 365 * 3, - }); } else { - deleteCookie(REDIRECT_LOCALE_COOKIE_NAME); + deleteCookie(PREFERRED_LOCALE_COOKIE_NAME); } setValue(event.target.checked); gleanClick(`${LANGUAGE_REDIRECT}: ${locale} -> ${Number(newValue)}`); diff --git a/cloud-function/src/middlewares/redirect-preferred-locale.ts b/cloud-function/src/middlewares/redirect-preferred-locale.ts index 763a20fe02b7..40a236014cae 100644 --- a/cloud-function/src/middlewares/redirect-preferred-locale.ts +++ b/cloud-function/src/middlewares/redirect-preferred-locale.ts @@ -10,9 +10,8 @@ export async function redirectPreferredLocale( // Check 1: Does the user prefer a locale and has redirect enabled? const preferredLocale = req.cookies["preferredlocale"]; - const redirectLocale = req.cookies["redirectLocale"]; - if (!preferredLocale || !redirectLocale) { + if (!preferredLocale) { next(); return; } From 2fe4fc703a71c80dec516b92431fafe2e37e52ed Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 21:13:12 +0200 Subject: [PATCH 040/100] refactor(main-menu): move {Reference,Guides,Plus,Tools}Menu --- client/src/ui/molecules/guides-menu/index.tsx | 66 ----- .../index.scss => main-menu/guides.scss} | 0 client/src/ui/molecules/main-menu/index.tsx | 279 +++++++++++++++++- .../index.scss => main-menu/plus.scss} | 0 .../index.scss => main-menu/references.scss} | 0 .../index.scss => main-menu/tools.scss} | 0 client/src/ui/molecules/plus-menu/index.tsx | 94 ------ .../src/ui/molecules/reference-menu/index.tsx | 84 ------ client/src/ui/molecules/tools-menu/index.tsx | 40 --- 9 files changed, 275 insertions(+), 288 deletions(-) delete mode 100644 client/src/ui/molecules/guides-menu/index.tsx rename client/src/ui/molecules/{guides-menu/index.scss => main-menu/guides.scss} (100%) rename client/src/ui/molecules/{plus-menu/index.scss => main-menu/plus.scss} (100%) rename client/src/ui/molecules/{reference-menu/index.scss => main-menu/references.scss} (100%) rename client/src/ui/molecules/{tools-menu/index.scss => main-menu/tools.scss} (100%) delete mode 100644 client/src/ui/molecules/plus-menu/index.tsx delete mode 100644 client/src/ui/molecules/reference-menu/index.tsx delete mode 100644 client/src/ui/molecules/tools-menu/index.tsx diff --git a/client/src/ui/molecules/guides-menu/index.tsx b/client/src/ui/molecules/guides-menu/index.tsx deleted file mode 100644 index 402f11600180..000000000000 --- a/client/src/ui/molecules/guides-menu/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useLocale } from "../../../hooks"; -import { Menu } from "../menu"; - -import "./index.scss"; - -export const GuidesMenu = ({ visibleSubMenuId, toggleMenu }) => { - const locale = useLocale(); - - const menu = { - id: "guides", - label: "Guides", - to: `/${locale}/docs/Learn`, - items: [ - { - description: "Learn web development", - hasIcon: true, - extraClasses: "apis-link-container mobile-only", - iconClasses: "submenu-icon learn", - label: "Overview / MDN Learning Area", - url: `/${locale}/docs/Learn`, - }, - { - description: "Learn web development", - extraClasses: "apis-link-container desktop-only", - hasIcon: true, - iconClasses: "submenu-icon learn", - label: "MDN Learning Area", - url: `/${locale}/docs/Learn`, - }, - { - description: "Learn to structure web content with HTML", - extraClasses: "html-link-container", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "HTML", - url: `/${locale}/docs/Learn/HTML`, - }, - { - description: "Learn to style content using CSS", - extraClasses: "css-link-container", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "CSS", - url: `/${locale}/docs/Learn/CSS`, - }, - { - description: "Learn to run scripts in the browser", - extraClasses: "javascript-link-container", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "JavaScript", - url: `/${locale}/docs/Learn/JavaScript`, - }, - { - description: "Learn to make the web accessible to all", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Accessibility", - url: `/${locale}/docs/Web/Accessibility`, - }, - ], - }; - const isOpen = visibleSubMenuId === menu.id; - - return ; -}; diff --git a/client/src/ui/molecules/guides-menu/index.scss b/client/src/ui/molecules/main-menu/guides.scss similarity index 100% rename from client/src/ui/molecules/guides-menu/index.scss rename to client/src/ui/molecules/main-menu/guides.scss diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index a61dc3e1e28d..dc5d100a4eb8 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -1,15 +1,23 @@ import { useEffect, useRef, useState } from "react"; -import { GuidesMenu } from "../guides-menu"; -import { ReferenceMenu } from "../reference-menu"; -import { PlusMenu } from "../plus-menu"; +import { Menu } from "../menu"; import "./index.scss"; +import "./references.scss"; +import "./guides.scss"; +import "./plus.scss"; +import "./tools.scss"; + import { PLUS_IS_ENABLED } from "../../../env"; import { useGleanClick } from "../../../telemetry/glean-context"; import { MENU } from "../../../telemetry/constants"; import { useLocation } from "react-router"; -import { ToolsMenu } from "../tools-menu"; +import { useIsServer, useLocale, useViewedState } from "../../../hooks"; +import { usePlusUrl } from "../../../plus/utils"; +import { MenuEntry } from "../submenu"; +import { FeatureId } from "../../../constants"; +import { useUserData } from "../../../user-context"; +import { OBSERVATORY_TITLE } from "../../../../../libs/constants"; export default function MainMenu({ isOpenOnMobile }) { const previousActiveElement = useRef(null); @@ -116,3 +124,266 @@ function TopLevelMenuLink({ ); } + +function ReferenceMenu({ visibleSubMenuId, toggleMenu }) { + const locale = useLocale(); + + const menu = { + id: "references", + label: "References", + to: `/${locale}/docs/Web`, + items: [ + { + description: "Web technology reference for developers", + hasIcon: true, + extraClasses: "apis-link-container mobile-only", + iconClasses: "submenu-icon", + label: "Overview / Web Technology", + url: `/${locale}/docs/Web`, + }, + { + description: "Structure of content on the web", + extraClasses: "html-link-container", + hasIcon: true, + iconClasses: "submenu-icon html", + label: "HTML", + url: `/${locale}/docs/Web/HTML`, + }, + { + description: "Code used to describe document style", + extraClasses: "css-link-container", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "CSS", + url: `/${locale}/docs/Web/CSS`, + }, + { + description: "General-purpose scripting language", + extraClasses: "javascript-link-container", + hasIcon: true, + iconClasses: "submenu-icon javascript", + label: "JavaScript", + url: `/${locale}/docs/Web/JavaScript`, + }, + { + description: "Protocol for transmitting web resources", + extraClasses: "http-link-container", + hasIcon: true, + iconClasses: "submenu-icon http", + label: "HTTP", + url: `/${locale}/docs/Web/HTTP`, + }, + { + description: "Interfaces for building web applications", + extraClasses: "apis-link-container", + hasIcon: true, + iconClasses: "submenu-icon apis", + label: "Web APIs", + url: `/${locale}/docs/Web/API`, + }, + { + description: "Developing extensions for web browsers", + extraClasses: "apis-link-container", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Web Extensions", + url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, + }, + { + description: "Web technology reference for developers", + extraClasses: "apis-link-container desktop-only", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Web Technology", + url: `/${locale}/docs/Web`, + }, + ], + }; + + const isOpen = visibleSubMenuId === menu.id; + + return ; +} + +function GuidesMenu({ visibleSubMenuId, toggleMenu }) { + const locale = useLocale(); + + const menu = { + id: "guides", + label: "Guides", + to: `/${locale}/docs/Learn`, + items: [ + { + description: "Learn web development", + hasIcon: true, + extraClasses: "apis-link-container mobile-only", + iconClasses: "submenu-icon learn", + label: "Overview / MDN Learning Area", + url: `/${locale}/docs/Learn`, + }, + { + description: "Learn web development", + extraClasses: "apis-link-container desktop-only", + hasIcon: true, + iconClasses: "submenu-icon learn", + label: "MDN Learning Area", + url: `/${locale}/docs/Learn`, + }, + { + description: "Learn to structure web content with HTML", + extraClasses: "html-link-container", + hasIcon: true, + iconClasses: "submenu-icon html", + label: "HTML", + url: `/${locale}/docs/Learn/HTML`, + }, + { + description: "Learn to style content using CSS", + extraClasses: "css-link-container", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "CSS", + url: `/${locale}/docs/Learn/CSS`, + }, + { + description: "Learn to run scripts in the browser", + extraClasses: "javascript-link-container", + hasIcon: true, + iconClasses: "submenu-icon javascript", + label: "JavaScript", + url: `/${locale}/docs/Learn/JavaScript`, + }, + { + description: "Learn to make the web accessible to all", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Accessibility", + url: `/${locale}/docs/Web/Accessibility`, + }, + ], + }; + const isOpen = visibleSubMenuId === menu.id; + + return ; +} + +function PlusMenu({ visibleSubMenuId, toggleMenu }) { + const plusUrl = usePlusUrl(); + const locale = useLocale(); + const isServer = useIsServer(); + const userData = useUserData(); + const isAuthenticated = userData && userData.isAuthenticated; + + const { isViewed } = useViewedState(); + + // Avoid that "Plus" and "AI Help" are both active. + const { pathname } = useLocation(); + const aiHelpUrl = `/${locale}/plus/ai-help`; + const isActive = + pathname.startsWith(plusUrl.split("#", 2)[0]) && + !pathname.startsWith(aiHelpUrl); + + const plusMenu: MenuEntry = { + label: "Plus", + id: "mdn-plus", + to: plusUrl, + items: [ + { + description: "A customized MDN experience", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Overview", + url: plusUrl, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon", + label: "AI Help", + url: aiHelpUrl, + }, + ...(!isServer && isAuthenticated + ? [ + { + description: "Your saved articles from across MDN", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Collections", + url: `/${locale}/plus/collections`, + }, + ] + : []), + { + description: "All browser compatibility updates at a glance", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Updates", + dot: + Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime() + !isViewed(FeatureId.PLUS_UPDATES_V2) + ? "New feature" + : undefined, + url: `/${locale}/plus/updates`, + }, + { + description: "Learn how to use MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Documentation", + url: `/en-US/plus/docs/features/overview`, + }, + { + description: "Frequently asked questions about MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon", + label: "FAQ", + url: `/en-US/plus/docs/faq`, + }, + ], + }; + const isOpen = visibleSubMenuId === plusMenu.id; + + return ( + + ); +} + +function ToolsMenu({ visibleSubMenuId, toggleMenu }) { + const locale = useLocale(); + + const menu = { + id: "tools", + label: "Tools", + items: [ + { + description: "Write, test and share your code", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Playground", + url: `/${locale}/play`, + }, + { + description: "Scan a website for free", + hasIcon: true, + iconClasses: "submenu-icon", + label: OBSERVATORY_TITLE, + url: `/en-US/observatory`, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon", + label: "AI Help", + url: `/en-US/plus/ai-help`, + }, + ], + }; + const isOpen = visibleSubMenuId === menu.id; + + return ; +} diff --git a/client/src/ui/molecules/plus-menu/index.scss b/client/src/ui/molecules/main-menu/plus.scss similarity index 100% rename from client/src/ui/molecules/plus-menu/index.scss rename to client/src/ui/molecules/main-menu/plus.scss diff --git a/client/src/ui/molecules/reference-menu/index.scss b/client/src/ui/molecules/main-menu/references.scss similarity index 100% rename from client/src/ui/molecules/reference-menu/index.scss rename to client/src/ui/molecules/main-menu/references.scss diff --git a/client/src/ui/molecules/tools-menu/index.scss b/client/src/ui/molecules/main-menu/tools.scss similarity index 100% rename from client/src/ui/molecules/tools-menu/index.scss rename to client/src/ui/molecules/main-menu/tools.scss diff --git a/client/src/ui/molecules/plus-menu/index.tsx b/client/src/ui/molecules/plus-menu/index.tsx deleted file mode 100644 index 5aa5138688c7..000000000000 --- a/client/src/ui/molecules/plus-menu/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import "./index.scss"; -import { usePlusUrl } from "../../../plus/utils"; -import { Menu } from "../menu"; -import { useIsServer, useLocale, useViewedState } from "../../../hooks"; -import { useUserData } from "../../../user-context"; -import { MenuEntry } from "../submenu"; -import { FeatureId } from "../../../constants"; -import { useLocation } from "react-router"; - -export const PlusMenu = ({ visibleSubMenuId, toggleMenu }) => { - const plusUrl = usePlusUrl(); - const locale = useLocale(); - const isServer = useIsServer(); - const userData = useUserData(); - const isAuthenticated = userData && userData.isAuthenticated; - - const { isViewed } = useViewedState(); - - // Avoid that "Plus" and "AI Help" are both active. - const { pathname } = useLocation(); - const aiHelpUrl = `/${locale}/plus/ai-help`; - const isActive = - pathname.startsWith(plusUrl.split("#", 2)[0]) && - !pathname.startsWith(aiHelpUrl); - - const plusMenu: MenuEntry = { - label: "Plus", - id: "mdn-plus", - to: plusUrl, - items: [ - { - description: "A customized MDN experience", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Overview", - url: plusUrl, - }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon", - label: "AI Help", - url: aiHelpUrl, - }, - ...(!isServer && isAuthenticated - ? [ - { - description: "Your saved articles from across MDN", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Collections", - url: `/${locale}/plus/collections`, - }, - ] - : []), - { - description: "All browser compatibility updates at a glance", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Updates", - dot: - Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime() - !isViewed(FeatureId.PLUS_UPDATES_V2) - ? "New feature" - : undefined, - url: `/${locale}/plus/updates`, - }, - { - description: "Learn how to use MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Documentation", - url: `/en-US/plus/docs/features/overview`, - }, - { - description: "Frequently asked questions about MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon", - label: "FAQ", - url: `/en-US/plus/docs/faq`, - }, - ], - }; - const isOpen = visibleSubMenuId === plusMenu.id; - - return ( - - ); -}; diff --git a/client/src/ui/molecules/reference-menu/index.tsx b/client/src/ui/molecules/reference-menu/index.tsx deleted file mode 100644 index 35c550dfa9e3..000000000000 --- a/client/src/ui/molecules/reference-menu/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useLocale } from "../../../hooks"; -import { Menu } from "../menu"; - -import "./index.scss"; - -export const ReferenceMenu = ({ visibleSubMenuId, toggleMenu }) => { - const locale = useLocale(); - - const menu = { - id: "references", - label: "References", - to: `/${locale}/docs/Web`, - items: [ - { - description: "Web technology reference for developers", - hasIcon: true, - extraClasses: "apis-link-container mobile-only", - iconClasses: "submenu-icon", - label: "Overview / Web Technology", - url: `/${locale}/docs/Web`, - }, - { - description: "Structure of content on the web", - extraClasses: "html-link-container", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "HTML", - url: `/${locale}/docs/Web/HTML`, - }, - { - description: "Code used to describe document style", - extraClasses: "css-link-container", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "CSS", - url: `/${locale}/docs/Web/CSS`, - }, - { - description: "General-purpose scripting language", - extraClasses: "javascript-link-container", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "JavaScript", - url: `/${locale}/docs/Web/JavaScript`, - }, - { - description: "Protocol for transmitting web resources", - extraClasses: "http-link-container", - hasIcon: true, - iconClasses: "submenu-icon http", - label: "HTTP", - url: `/${locale}/docs/Web/HTTP`, - }, - { - description: "Interfaces for building web applications", - extraClasses: "apis-link-container", - hasIcon: true, - iconClasses: "submenu-icon apis", - label: "Web APIs", - url: `/${locale}/docs/Web/API`, - }, - { - description: "Developing extensions for web browsers", - extraClasses: "apis-link-container", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Web Extensions", - url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, - }, - { - description: "Web technology reference for developers", - extraClasses: "apis-link-container desktop-only", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Web Technology", - url: `/${locale}/docs/Web`, - }, - ], - }; - - const isOpen = visibleSubMenuId === menu.id; - - return ; -}; diff --git a/client/src/ui/molecules/tools-menu/index.tsx b/client/src/ui/molecules/tools-menu/index.tsx deleted file mode 100644 index ce1a83b316e2..000000000000 --- a/client/src/ui/molecules/tools-menu/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { OBSERVATORY_TITLE } from "../../../../../libs/constants"; -import { useLocale } from "../../../hooks"; -import { Menu } from "../menu"; - -import "./index.scss"; - -export const ToolsMenu = ({ visibleSubMenuId, toggleMenu }) => { - const locale = useLocale(); - - const menu = { - id: "tools", - label: "Tools", - items: [ - { - description: "Write, test and share your code", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Playground", - url: `/${locale}/play`, - }, - { - description: "Scan a website for free", - hasIcon: true, - iconClasses: "submenu-icon", - label: OBSERVATORY_TITLE, - url: `/en-US/observatory`, - }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon", - label: "AI Help", - url: `/en-US/plus/ai-help`, - }, - ], - }; - const isOpen = visibleSubMenuId === menu.id; - - return ; -}; From acc7f2af0a6a7a99872ea2bfa5965e421629d088 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 21:21:00 +0200 Subject: [PATCH 041/100] refactor(main-menu): inline {Reference,Guides,Plus,Tools}Menu --- client/src/ui/molecules/main-menu/index.tsx | 151 +++++++++----------- 1 file changed, 65 insertions(+), 86 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index dc5d100a4eb8..5450bace4ff5 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -72,63 +72,11 @@ export default function MainMenu({ isOpenOnMobile }) { } } - return ( - - ); -} - -function TopLevelMenuLink({ - to, - children, -}: { - to: string; - children: React.ReactNode; -}) { - const { pathname } = useLocation(); - const gleanClick = useGleanClick(); - - const isActive = pathname.startsWith(to.split("#", 2)[0]); - - return ( -
    • - gleanClick(`${MENU.CLICK_LINK}: top-level -> ${to}`)} - > - {children} - -
    • - ); -} - -function ReferenceMenu({ visibleSubMenuId, toggleMenu }) { const locale = useLocale(); - const menu = { + // References. + + const referencesMenu = { id: "references", label: "References", to: `/${locale}/docs/Web`, @@ -200,15 +148,9 @@ function ReferenceMenu({ visibleSubMenuId, toggleMenu }) { ], }; - const isOpen = visibleSubMenuId === menu.id; + // Guides. - return ; -} - -function GuidesMenu({ visibleSubMenuId, toggleMenu }) { - const locale = useLocale(); - - const menu = { + const guidesMenu = { id: "guides", label: "Guides", to: `/${locale}/docs/Learn`, @@ -262,14 +204,9 @@ function GuidesMenu({ visibleSubMenuId, toggleMenu }) { }, ], }; - const isOpen = visibleSubMenuId === menu.id; - return ; -} - -function PlusMenu({ visibleSubMenuId, toggleMenu }) { + // Plus menu. const plusUrl = usePlusUrl(); - const locale = useLocale(); const isServer = useIsServer(); const userData = useUserData(); const isAuthenticated = userData && userData.isAuthenticated; @@ -279,7 +216,7 @@ function PlusMenu({ visibleSubMenuId, toggleMenu }) { // Avoid that "Plus" and "AI Help" are both active. const { pathname } = useLocation(); const aiHelpUrl = `/${locale}/plus/ai-help`; - const isActive = + const isPlusActive = pathname.startsWith(plusUrl.split("#", 2)[0]) && !pathname.startsWith(aiHelpUrl); @@ -341,22 +278,10 @@ function PlusMenu({ visibleSubMenuId, toggleMenu }) { }, ], }; - const isOpen = visibleSubMenuId === plusMenu.id; - - return ( - - ); -} -function ToolsMenu({ visibleSubMenuId, toggleMenu }) { - const locale = useLocale(); + // Tools. - const menu = { + const toolsMenu = { id: "tools", label: "Tools", items: [ @@ -383,7 +308,61 @@ function ToolsMenu({ visibleSubMenuId, toggleMenu }) { }, ], }; - const isOpen = visibleSubMenuId === menu.id; - return ; + return ( + + ); +} + +function TopLevelMenuLink({ + to, + children, +}: { + to: string; + children: React.ReactNode; +}) { + const { pathname } = useLocation(); + const gleanClick = useGleanClick(); + + const isActive = pathname.startsWith(to.split("#", 2)[0]); + + return ( +
    • + gleanClick(`${MENU.CLICK_LINK}: top-level -> ${to}`)} + > + {children} + +
    • + ); } From ae512bbaecd21321d6767e92030ea99e47a2397a Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 21:31:13 +0200 Subject: [PATCH 042/100] refactor(main-menu): define menu in single array --- client/src/ui/molecules/main-menu/index.tsx | 473 ++++++++++---------- client/src/ui/molecules/submenu/index.tsx | 1 + 2 files changed, 231 insertions(+), 243 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index 5450bace4ff5..d95042f380d1 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -74,137 +74,6 @@ export default function MainMenu({ isOpenOnMobile }) { const locale = useLocale(); - // References. - - const referencesMenu = { - id: "references", - label: "References", - to: `/${locale}/docs/Web`, - items: [ - { - description: "Web technology reference for developers", - hasIcon: true, - extraClasses: "apis-link-container mobile-only", - iconClasses: "submenu-icon", - label: "Overview / Web Technology", - url: `/${locale}/docs/Web`, - }, - { - description: "Structure of content on the web", - extraClasses: "html-link-container", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "HTML", - url: `/${locale}/docs/Web/HTML`, - }, - { - description: "Code used to describe document style", - extraClasses: "css-link-container", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "CSS", - url: `/${locale}/docs/Web/CSS`, - }, - { - description: "General-purpose scripting language", - extraClasses: "javascript-link-container", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "JavaScript", - url: `/${locale}/docs/Web/JavaScript`, - }, - { - description: "Protocol for transmitting web resources", - extraClasses: "http-link-container", - hasIcon: true, - iconClasses: "submenu-icon http", - label: "HTTP", - url: `/${locale}/docs/Web/HTTP`, - }, - { - description: "Interfaces for building web applications", - extraClasses: "apis-link-container", - hasIcon: true, - iconClasses: "submenu-icon apis", - label: "Web APIs", - url: `/${locale}/docs/Web/API`, - }, - { - description: "Developing extensions for web browsers", - extraClasses: "apis-link-container", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Web Extensions", - url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, - }, - { - description: "Web technology reference for developers", - extraClasses: "apis-link-container desktop-only", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Web Technology", - url: `/${locale}/docs/Web`, - }, - ], - }; - - // Guides. - - const guidesMenu = { - id: "guides", - label: "Guides", - to: `/${locale}/docs/Learn`, - items: [ - { - description: "Learn web development", - hasIcon: true, - extraClasses: "apis-link-container mobile-only", - iconClasses: "submenu-icon learn", - label: "Overview / MDN Learning Area", - url: `/${locale}/docs/Learn`, - }, - { - description: "Learn web development", - extraClasses: "apis-link-container desktop-only", - hasIcon: true, - iconClasses: "submenu-icon learn", - label: "MDN Learning Area", - url: `/${locale}/docs/Learn`, - }, - { - description: "Learn to structure web content with HTML", - extraClasses: "html-link-container", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "HTML", - url: `/${locale}/docs/Learn/HTML`, - }, - { - description: "Learn to style content using CSS", - extraClasses: "css-link-container", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "CSS", - url: `/${locale}/docs/Learn/CSS`, - }, - { - description: "Learn to run scripts in the browser", - extraClasses: "javascript-link-container", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "JavaScript", - url: `/${locale}/docs/Learn/JavaScript`, - }, - { - description: "Learn to make the web accessible to all", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Accessibility", - url: `/${locale}/docs/Web/Accessibility`, - }, - ], - }; - // Plus menu. const plusUrl = usePlusUrl(); const isServer = useIsServer(); @@ -220,128 +89,246 @@ export default function MainMenu({ isOpenOnMobile }) { pathname.startsWith(plusUrl.split("#", 2)[0]) && !pathname.startsWith(aiHelpUrl); - const plusMenu: MenuEntry = { - label: "Plus", - id: "mdn-plus", - to: plusUrl, - items: [ - { - description: "A customized MDN experience", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Overview", - url: plusUrl, - }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon", - label: "AI Help", - url: aiHelpUrl, - }, - ...(!isServer && isAuthenticated - ? [ - { - description: "Your saved articles from across MDN", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Collections", - url: `/${locale}/plus/collections`, - }, - ] - : []), - { - description: "All browser compatibility updates at a glance", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Updates", - dot: - Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime() - !isViewed(FeatureId.PLUS_UPDATES_V2) - ? "New feature" - : undefined, - url: `/${locale}/plus/updates`, - }, - { - description: "Learn how to use MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Documentation", - url: `/en-US/plus/docs/features/overview`, - }, - { - description: "Frequently asked questions about MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon", - label: "FAQ", - url: `/en-US/plus/docs/faq`, - }, - ], - }; - - // Tools. - - const toolsMenu = { - id: "tools", - label: "Tools", - items: [ - { - description: "Write, test and share your code", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Playground", - url: `/${locale}/play`, - }, - { - description: "Scan a website for free", - hasIcon: true, - iconClasses: "submenu-icon", - label: OBSERVATORY_TITLE, - url: `/en-US/observatory`, - }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon", - label: "AI Help", - url: `/en-US/plus/ai-help`, - }, - ], - }; + const menus = [ + { + id: "references", + label: "References", + to: `/${locale}/docs/Web`, + items: [ + { + description: "Web technology reference for developers", + hasIcon: true, + extraClasses: "apis-link-container mobile-only", + iconClasses: "submenu-icon", + label: "Overview / Web Technology", + url: `/${locale}/docs/Web`, + }, + { + description: "Structure of content on the web", + extraClasses: "html-link-container", + hasIcon: true, + iconClasses: "submenu-icon html", + label: "HTML", + url: `/${locale}/docs/Web/HTML`, + }, + { + description: "Code used to describe document style", + extraClasses: "css-link-container", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "CSS", + url: `/${locale}/docs/Web/CSS`, + }, + { + description: "General-purpose scripting language", + extraClasses: "javascript-link-container", + hasIcon: true, + iconClasses: "submenu-icon javascript", + label: "JavaScript", + url: `/${locale}/docs/Web/JavaScript`, + }, + { + description: "Protocol for transmitting web resources", + extraClasses: "http-link-container", + hasIcon: true, + iconClasses: "submenu-icon http", + label: "HTTP", + url: `/${locale}/docs/Web/HTTP`, + }, + { + description: "Interfaces for building web applications", + extraClasses: "apis-link-container", + hasIcon: true, + iconClasses: "submenu-icon apis", + label: "Web APIs", + url: `/${locale}/docs/Web/API`, + }, + { + description: "Developing extensions for web browsers", + extraClasses: "apis-link-container", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Web Extensions", + url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, + }, + { + description: "Web technology reference for developers", + extraClasses: "apis-link-container desktop-only", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Web Technology", + url: `/${locale}/docs/Web`, + }, + ], + }, + { + id: "guides", + label: "Guides", + to: `/${locale}/docs/Learn`, + items: [ + { + description: "Learn web development", + hasIcon: true, + extraClasses: "apis-link-container mobile-only", + iconClasses: "submenu-icon learn", + label: "Overview / MDN Learning Area", + url: `/${locale}/docs/Learn`, + }, + { + description: "Learn web development", + extraClasses: "apis-link-container desktop-only", + hasIcon: true, + iconClasses: "submenu-icon learn", + label: "MDN Learning Area", + url: `/${locale}/docs/Learn`, + }, + { + description: "Learn to structure web content with HTML", + extraClasses: "html-link-container", + hasIcon: true, + iconClasses: "submenu-icon html", + label: "HTML", + url: `/${locale}/docs/Learn/HTML`, + }, + { + description: "Learn to style content using CSS", + extraClasses: "css-link-container", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "CSS", + url: `/${locale}/docs/Learn/CSS`, + }, + { + description: "Learn to run scripts in the browser", + extraClasses: "javascript-link-container", + hasIcon: true, + iconClasses: "submenu-icon javascript", + label: "JavaScript", + url: `/${locale}/docs/Learn/JavaScript`, + }, + { + description: "Learn to make the web accessible to all", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Accessibility", + url: `/${locale}/docs/Web/Accessibility`, + }, + ], + }, + PLUS_IS_ENABLED && { + label: "Plus", + id: "mdn-plus", + isActive: isPlusActive, + to: plusUrl, + items: [ + { + description: "A customized MDN experience", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Overview", + url: plusUrl, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon", + label: "AI Help", + url: aiHelpUrl, + }, + ...(!isServer && isAuthenticated + ? [ + { + description: "Your saved articles from across MDN", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Collections", + url: `/${locale}/plus/collections`, + }, + ] + : []), + { + description: "All browser compatibility updates at a glance", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Updates", + dot: + Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime() + !isViewed(FeatureId.PLUS_UPDATES_V2) + ? "New feature" + : undefined, + url: `/${locale}/plus/updates`, + }, + { + description: "Learn how to use MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Documentation", + url: `/en-US/plus/docs/features/overview`, + }, + { + description: "Frequently asked questions about MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon", + label: "FAQ", + url: `/en-US/plus/docs/faq`, + }, + ], + }, + Curriculum, + Blog, + { + id: "tools", + label: "Tools", + items: [ + { + description: "Write, test and share your code", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Playground", + url: `/${locale}/play`, + }, + { + description: "Scan a website for free", + hasIcon: true, + iconClasses: "submenu-icon", + label: OBSERVATORY_TITLE, + url: `/en-US/observatory`, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon", + label: "AI Help", + url: `/en-US/plus/ai-help`, + }, + ], + }, + ].filter(Boolean) as (MenuEntry | React.ReactElement)[]; return ( ); } +function isMenuEntry(menu: any): menu is MenuEntry { + return typeof menu.id === "string"; +} + function TopLevelMenuLink({ to, children, diff --git a/client/src/ui/molecules/submenu/index.tsx b/client/src/ui/molecules/submenu/index.tsx index 1931e98db100..caf480f0f7c8 100644 --- a/client/src/ui/molecules/submenu/index.tsx +++ b/client/src/ui/molecules/submenu/index.tsx @@ -17,6 +17,7 @@ export type SubmenuItem = { export type MenuEntry = { id: string; + isActive?: boolean; items: SubmenuItem[]; label: string | ReactNode; to?: string; From 0880b73686581f589d16e45a2f935aa2bef0d821 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 21:32:46 +0200 Subject: [PATCH 043/100] chore(main-menu): remove Updates dot --- client/src/ui/molecules/main-menu/index.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index d95042f380d1..3a393c9b69a6 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -12,10 +12,9 @@ import { PLUS_IS_ENABLED } from "../../../env"; import { useGleanClick } from "../../../telemetry/glean-context"; import { MENU } from "../../../telemetry/constants"; import { useLocation } from "react-router"; -import { useIsServer, useLocale, useViewedState } from "../../../hooks"; +import { useIsServer, useLocale } from "../../../hooks"; import { usePlusUrl } from "../../../plus/utils"; import { MenuEntry } from "../submenu"; -import { FeatureId } from "../../../constants"; import { useUserData } from "../../../user-context"; import { OBSERVATORY_TITLE } from "../../../../../libs/constants"; @@ -80,8 +79,6 @@ export default function MainMenu({ isOpenOnMobile }) { const userData = useUserData(); const isAuthenticated = userData && userData.isAuthenticated; - const { isViewed } = useViewedState(); - // Avoid that "Plus" and "AI Help" are both active. const { pathname } = useLocation(); const aiHelpUrl = `/${locale}/plus/ai-help`; @@ -251,11 +248,6 @@ export default function MainMenu({ isOpenOnMobile }) { hasIcon: true, iconClasses: "submenu-icon", label: "Updates", - dot: - Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime() - !isViewed(FeatureId.PLUS_UPDATES_V2) - ? "New feature" - : undefined, url: `/${locale}/plus/updates`, }, { From e74d869b8067fb902bbee05b36f67bed9fa06e01 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 21:39:41 +0200 Subject: [PATCH 044/100] feat(main-menu): merge Plus into Tools menu --- client/src/ui/molecules/main-menu/index.tsx | 118 +++++++++----------- 1 file changed, 50 insertions(+), 68 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index 3a393c9b69a6..825d7bb6e5d3 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -79,13 +79,6 @@ export default function MainMenu({ isOpenOnMobile }) { const userData = useUserData(); const isAuthenticated = userData && userData.isAuthenticated; - // Avoid that "Plus" and "AI Help" are both active. - const { pathname } = useLocation(); - const aiHelpUrl = `/${locale}/plus/ai-help`; - const isPlusActive = - pathname.startsWith(plusUrl.split("#", 2)[0]) && - !pathname.startsWith(aiHelpUrl); - const menus = [ { id: "references", @@ -212,60 +205,6 @@ export default function MainMenu({ isOpenOnMobile }) { }, ], }, - PLUS_IS_ENABLED && { - label: "Plus", - id: "mdn-plus", - isActive: isPlusActive, - to: plusUrl, - items: [ - { - description: "A customized MDN experience", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Overview", - url: plusUrl, - }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon", - label: "AI Help", - url: aiHelpUrl, - }, - ...(!isServer && isAuthenticated - ? [ - { - description: "Your saved articles from across MDN", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Collections", - url: `/${locale}/plus/collections`, - }, - ] - : []), - { - description: "All browser compatibility updates at a glance", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Updates", - url: `/${locale}/plus/updates`, - }, - { - description: "Learn how to use MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Documentation", - url: `/en-US/plus/docs/features/overview`, - }, - { - description: "Frequently asked questions about MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon", - label: "FAQ", - url: `/en-US/plus/docs/faq`, - }, - ], - }, Curriculum, Blog, { @@ -286,13 +225,56 @@ export default function MainMenu({ isOpenOnMobile }) { label: OBSERVATORY_TITLE, url: `/en-US/observatory`, }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon", - label: "AI Help", - url: `/en-US/plus/ai-help`, - }, + ...(PLUS_IS_ENABLED + ? [ + { + description: "A customized MDN experience", + hasIcon: true, + iconClasses: "submenu-icon plus", + label: "MDN Plus", + url: plusUrl, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon plus", + label: "AI Help", + url: `/en-US/plus/ai-help`, + }, + ...(!isServer && isAuthenticated + ? [ + { + description: "Your saved articles from across MDN", + hasIcon: true, + iconClasses: "submenu-icon plus", + label: "Collections", + url: `/${locale}/plus/collections`, + }, + ] + : []), + { + description: "All browser compatibility updates at a glance", + hasIcon: true, + iconClasses: "submenu-icon plus", + label: "Updates", + url: `/${locale}/plus/updates`, + }, + { + description: "Learn how to use MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon plus", + label: "MDN Plus Docs", + url: `/en-US/plus/docs/features/overview`, + }, + { + description: "Frequently asked questions about MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon plus", + label: "MDN Plus FAQ", + url: `/en-US/plus/docs/faq`, + }, + ] + : []), ], }, ].filter(Boolean) as (MenuEntry | React.ReactElement)[]; From ea6e6488744ee7efc989a670ab7ae57cfc6789a0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 22:08:36 +0200 Subject: [PATCH 045/100] chore(signup-link): change "Sign up for free" to "Sign up" --- client/src/ui/atoms/signup-link/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/atoms/signup-link/index.tsx b/client/src/ui/atoms/signup-link/index.tsx index 51a6a3bcf372..b01b87349bc1 100644 --- a/client/src/ui/atoms/signup-link/index.tsx +++ b/client/src/ui/atoms/signup-link/index.tsx @@ -10,7 +10,7 @@ export const SignUpLink = ({ toPlans = false, gleanContext = "" }) => { const loginUrl = useLoginUrl(); const href = toPlans ? plansUrl : loginUrl; - const label = toPlans ? "Upgrade Now" : "Sign up for free"; + const label = toPlans ? "Upgrade Now" : "Sign up"; const rel = toPlans ? undefined : "nofollow"; return ( From efa86ab6f0948a9f2b8dfef5738b9ad7a1e3e673 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 22:08:58 +0200 Subject: [PATCH 046/100] chore(theme-switcher): hide "Theme" visually --- client/src/ui/molecules/theme-switcher/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/molecules/theme-switcher/index.tsx b/client/src/ui/molecules/theme-switcher/index.tsx index 91e143ccb6f0..add18e2e3627 100644 --- a/client/src/ui/molecules/theme-switcher/index.tsx +++ b/client/src/ui/molecules/theme-switcher/index.tsx @@ -82,7 +82,7 @@ export const ThemeSwitcher = () => { setIsOpen(!isOpen); }} > - Theme + Theme From afbf152262ad1e5721e3cb06b8c0c3e9fe72df04 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 22:09:15 +0200 Subject: [PATCH 047/100] feat(main-menu): migrate to topical structure --- client/src/ui/molecules/main-menu/guides.scss | 23 --- client/src/ui/molecules/main-menu/index.scss | 62 ++++++ client/src/ui/molecules/main-menu/index.tsx | 192 +++++++++++------- client/src/ui/molecules/main-menu/plus.scss | 23 --- .../ui/molecules/main-menu/references.scss | 110 ---------- 5 files changed, 179 insertions(+), 231 deletions(-) delete mode 100644 client/src/ui/molecules/main-menu/guides.scss delete mode 100644 client/src/ui/molecules/main-menu/plus.scss delete mode 100644 client/src/ui/molecules/main-menu/references.scss diff --git a/client/src/ui/molecules/main-menu/guides.scss b/client/src/ui/molecules/main-menu/guides.scss deleted file mode 100644 index 42a234d2f98a..000000000000 --- a/client/src/ui/molecules/main-menu/guides.scss +++ /dev/null @@ -1,23 +0,0 @@ -@use "sass:color"; -@use "../../vars" as *; - -.guides { - .submenu .submenu-item-heading { - font-size: var(--type-smaller-font-size); - font-weight: initial; - } - - .desktop-only { - display: none; - } - - @media (min-width: $screen-lg) { - .desktop-only { - display: inherit; - } - - .mobile-only { - display: none; - } - } -} diff --git a/client/src/ui/molecules/main-menu/index.scss b/client/src/ui/molecules/main-menu/index.scss index 29913f4dbe63..f8b346588934 100644 --- a/client/src/ui/molecules/main-menu/index.scss +++ b/client/src/ui/molecules/main-menu/index.scss @@ -40,8 +40,22 @@ ul.main-menu { } } + .top-level-entry .long { + display: none; + } + @media (min-width: ($screen-xl)) { gap: 1rem; + + .top-level-entry { + .long { + display: unset; + } + + .short { + display: none; + } + } } } @@ -78,3 +92,51 @@ ul.main-menu { } } } + +@media screen and (min-width: $screen-lg) { + #more-button { + display: flex; + + &::after { + display: none; + } + } +} + +.submenu-icon { + &.html { + background-color: var(--html-accent-color); + } + + &.css { + background-color: var(--css-accent-color); + } + + &.javascript { + background-color: var(--js-accent-color); + } + + &.http { + background-color: var(--http-accent-color); + } + + &.apis { + background-color: var(--apis-accent-color); + } + + &.learn { + background-color: var(--learn-accent-color); + } + + &.plus { + background-color: var(--plus-accent-color); + } + + &.curriculum { + background-color: var(--curriculum-color); + } + + &.blog { + background-color: var(--apis-accent-color); + } +} diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index 825d7bb6e5d3..b96dffb291a2 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -3,9 +3,6 @@ import { useEffect, useRef, useState } from "react"; import { Menu } from "../menu"; import "./index.scss"; -import "./references.scss"; -import "./guides.scss"; -import "./plus.scss"; import "./tools.scss"; import { PLUS_IS_ENABLED } from "../../../env"; @@ -78,135 +75,180 @@ export default function MainMenu({ isOpenOnMobile }) { const isServer = useIsServer(); const userData = useUserData(); const isAuthenticated = userData && userData.isAuthenticated; + const { pathname } = useLocation(); const menus = [ { - id: "references", - label: "References", - to: `/${locale}/docs/Web`, + id: "html", + label: "HTML", + to: `/${locale}/docs/Web/HTML`, + isActive: + pathname.startsWith(`/${locale}/docs/Learn/HTML`) || + pathname.startsWith(`/${locale}/docs/Web/HTML`), items: [ { - description: "Web technology reference for developers", + description: "Learn to structure web content with HTML", hasIcon: true, - extraClasses: "apis-link-container mobile-only", - iconClasses: "submenu-icon", - label: "Overview / Web Technology", - url: `/${locale}/docs/Web`, + iconClasses: "submenu-icon html", + label: "Learn HTML", + url: `/${locale}/docs/Learn/HTML`, }, { - description: "Structure of content on the web", - extraClasses: "html-link-container", + description: "Look up elements, attributes, and more", hasIcon: true, iconClasses: "submenu-icon html", - label: "HTML", + label: "HTML references", url: `/${locale}/docs/Web/HTML`, }, + ], + }, + { + id: "css", + label: "CSS", + to: `/${locale}/docs/Web/CSS`, + isActive: + pathname.startsWith(`/${locale}/docs/Learn/CSS`) || + pathname.startsWith(`/${locale}/docs/Web/CSS`), + items: [ + { + description: "Learn to style content using CSS", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "Learn CSS", + url: `/${locale}/docs/Learn/CSS`, + }, { - description: "Code used to describe document style", - extraClasses: "css-link-container", + description: "Look up properties, selectors, and more", hasIcon: true, iconClasses: "submenu-icon css", - label: "CSS", + label: "CSS references", url: `/${locale}/docs/Web/CSS`, }, + ], + }, + { + id: "js", + label: ( + <> + JS + JavaScript + + ), + to: `/${locale}/docs/Web/JavaScript`, + isActive: + pathname.startsWith(`/${locale}/docs/Learn/JavaScript`) || + pathname.startsWith(`/${locale}/docs/Web/JavaScript`), + items: [ { - description: "General-purpose scripting language", - extraClasses: "javascript-link-container", + description: "Learn to run scripts in the browser", hasIcon: true, iconClasses: "submenu-icon javascript", - label: "JavaScript", - url: `/${locale}/docs/Web/JavaScript`, + label: "Learn JavaScript", + url: `/${locale}/docs/Learn/JavaScript`, }, { - description: "Protocol for transmitting web resources", - extraClasses: "http-link-container", + description: "Look up objects, expressions, and more", hasIcon: true, - iconClasses: "submenu-icon http", - label: "HTTP", - url: `/${locale}/docs/Web/HTTP`, + iconClasses: "submenu-icon javascript", + label: "JavaScript references", + url: `/${locale}/docs/Web/JavaScript`, }, + ], + }, + { + id: "apis", + label: ( + <> + APIs + Web APIs + + ), + to: `/${locale}/docs/Web/API`, + isActive: pathname.startsWith(`/${locale}/docs/Web/API`), + items: [ { - description: "Interfaces for building web applications", - extraClasses: "apis-link-container", + description: "Look up all the APIs and interfaces", hasIcon: true, iconClasses: "submenu-icon apis", - label: "Web APIs", + label: "Web API references", url: `/${locale}/docs/Web/API`, }, + ], + }, + { + id: "http", + label: "HTTP", + to: `/${locale}/docs/Web/HTTP`, + isActive: pathname.startsWith(`/${locale}/docs/Web/HTTP`), + items: [ { - description: "Developing extensions for web browsers", - extraClasses: "apis-link-container", + description: "Look up status codes, headers, and more", + hasIcon: true, + iconClasses: "submenu-icon http", + label: "HTTP references", + url: `/${locale}/docs/Web/HTTP`, + }, + ], + }, + { + id: "more", + label: "More", + items: [ + { + description: "Learn to make the web accessible to all", hasIcon: true, iconClasses: "submenu-icon", - label: "Web Extensions", - url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, + label: "Accessibility", + url: `/${locale}/docs/Web/Accessibility`, }, { - description: "Web technology reference for developers", - extraClasses: "apis-link-container desktop-only", + description: "Develop extensions for web browsers", hasIcon: true, iconClasses: "submenu-icon", - label: "Web Technology", - url: `/${locale}/docs/Web`, + label: "Web Extensions", + url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, }, ], }, { - id: "guides", - label: "Guides", + id: "learn", + label: "Learn", to: `/${locale}/docs/Learn`, + isActive: + pathname.startsWith("/en-US/curriculum/") || + pathname.startsWith(`/${locale}/docs/Learn`), items: [ { - description: "Learn web development", + description: "Essential skills for front-end developers", hasIcon: true, - extraClasses: "apis-link-container mobile-only", - iconClasses: "submenu-icon learn", - label: "Overview / MDN Learning Area", - url: `/${locale}/docs/Learn`, + iconClasses: "submenu-icon curriculum", + label: "MDN Curriculum", + url: "/en-US/curriculum/", }, { description: "Learn web development", - extraClasses: "apis-link-container desktop-only", hasIcon: true, iconClasses: "submenu-icon learn", label: "MDN Learning Area", url: `/${locale}/docs/Learn`, }, + ], + }, + { + id: "blog", + label: "Blog", + to: "/en-US/blog/", + isActive: pathname.startsWith("/en-US/blog/"), + items: [ { - description: "Learn to structure web content with HTML", - extraClasses: "html-link-container", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "HTML", - url: `/${locale}/docs/Learn/HTML`, - }, - { - description: "Learn to style content using CSS", - extraClasses: "css-link-container", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "CSS", - url: `/${locale}/docs/Learn/CSS`, - }, - { - description: "Learn to run scripts in the browser", - extraClasses: "javascript-link-container", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "JavaScript", - url: `/${locale}/docs/Learn/JavaScript`, - }, - { - description: "Learn to make the web accessible to all", + description: "Learn about web features, and MDN", hasIcon: true, - iconClasses: "submenu-icon", - label: "Accessibility", - url: `/${locale}/docs/Web/Accessibility`, + iconClasses: "submenu-icon blog", + label: "MDN Blog", + url: "/en-US/blog/", }, ], }, - Curriculum, - Blog, { id: "tools", label: "Tools", diff --git a/client/src/ui/molecules/main-menu/plus.scss b/client/src/ui/molecules/main-menu/plus.scss deleted file mode 100644 index bdc55163ef3c..000000000000 --- a/client/src/ui/molecules/main-menu/plus.scss +++ /dev/null @@ -1,23 +0,0 @@ -@use "sass:color"; -@use "../../vars" as *; - -.mdn-plus { - .submenu-icon { - background-color: var(--plus-accent-color); - } - - .note { - background-color: var(--background-information); - - .submenu-item-description { - display: block; - margin: 0.125rem; - } - } - - @media (min-width: $screen-lg) { - .mobile-only { - display: none; - } - } -} diff --git a/client/src/ui/molecules/main-menu/references.scss b/client/src/ui/molecules/main-menu/references.scss deleted file mode 100644 index 1420a142333f..000000000000 --- a/client/src/ui/molecules/main-menu/references.scss +++ /dev/null @@ -1,110 +0,0 @@ -@use "sass:color"; -@use "../../vars" as *; - -.references { - .desktop-only { - display: none; - } - - @media (min-width: $screen-lg) { - .desktop-only { - display: inherit; - } - - .mobile-only { - display: none; - } - } -} - -.html-link-container { - a:hover, - a:focus { - .submenu-icon { - &.html { - background: var(--html-accent-color) !important; - } - } - } -} - -.css-link-container { - a:hover, - a:focus { - .submenu-icon { - &.css { - background-color: var(--css-accent-color) !important; - } - } - } -} - -.javascript-link-container { - a:hover, - a:focus { - .submenu-icon { - &.javascript { - background-color: var(--js-accent-color) !important; - } - } - } -} - -.http-link-container { - a:hover, - a:focus { - .submenu-icon { - &.http { - background-color: var(--http-accent-color) !important; - } - } - } -} - -.apis-link-container { - a:hover, - a:focus { - .submenu-icon { - &.apis { - background-color: var(--apis-accent-color) !important; - } - } - } -} - -.learn-link-container { - a:hover, - a:focus { - .submenu-icon { - &.learn { - background-color: var(--learn-accent-color) !important; - } - } - } -} - -.submenu-icon { - &.html { - background-color: var(--html-accent-engage); - } - - &.css { - background-color: var(--css-accent-engage); - } - - &.javascript { - background-color: var(--js-accent-engage); - } - - &.http { - background-color: var(--http-accent-engage); - } - - &.apis { - background-color: var(--apis-accent-engage); - } - - &.learn { - background-color: var(--learn-accent-engage); - } -} From 505f1281a6f429fae68b90f67f48b403d88872e8 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 22:24:45 +0200 Subject: [PATCH 048/100] chore(main-menu): remove Plus {Docs,FAQ} --- client/src/ui/molecules/main-menu/index.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index b96dffb291a2..ce1d4933e918 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -301,20 +301,6 @@ export default function MainMenu({ isOpenOnMobile }) { label: "Updates", url: `/${locale}/plus/updates`, }, - { - description: "Learn how to use MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon plus", - label: "MDN Plus Docs", - url: `/en-US/plus/docs/features/overview`, - }, - { - description: "Frequently asked questions about MDN Plus", - hasIcon: true, - iconClasses: "submenu-icon plus", - label: "MDN Plus FAQ", - url: `/en-US/plus/docs/faq`, - }, ] : []), ], From 651425722c7a057716a90ffcb7ca97e6becc24f6 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 22:27:58 +0200 Subject: [PATCH 049/100] chore(main-menu): use Observatory accent color --- client/src/ui/molecules/main-menu/index.scss | 4 ++++ client/src/ui/molecules/main-menu/index.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/ui/molecules/main-menu/index.scss b/client/src/ui/molecules/main-menu/index.scss index f8b346588934..459e5e18ec30 100644 --- a/client/src/ui/molecules/main-menu/index.scss +++ b/client/src/ui/molecules/main-menu/index.scss @@ -139,4 +139,8 @@ ul.main-menu { &.blog { background-color: var(--apis-accent-color); } + + &.observatory { + background-color: var(--observatory-accent); + } } diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index ce1d4933e918..25a4ca9c9254 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -263,7 +263,7 @@ export default function MainMenu({ isOpenOnMobile }) { { description: "Scan a website for free", hasIcon: true, - iconClasses: "submenu-icon", + iconClasses: "submenu-icon observatory", label: OBSERVATORY_TITLE, url: `/en-US/observatory`, }, From b4693352c924d58a7ed3e7d51ad89fa2dca898cf Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 16 Sep 2024 23:00:03 +0200 Subject: [PATCH 050/100] chore(main-menu): remove unused component --- client/src/ui/molecules/main-menu/index.tsx | 27 --------------------- 1 file changed, 27 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index 25a4ca9c9254..7d600dec9d4a 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -6,8 +6,6 @@ import "./index.scss"; import "./tools.scss"; import { PLUS_IS_ENABLED } from "../../../env"; -import { useGleanClick } from "../../../telemetry/glean-context"; -import { MENU } from "../../../telemetry/constants"; import { useLocation } from "react-router"; import { useIsServer, useLocale } from "../../../hooks"; import { usePlusUrl } from "../../../plus/utils"; @@ -330,28 +328,3 @@ export default function MainMenu({ isOpenOnMobile }) { function isMenuEntry(menu: any): menu is MenuEntry { return typeof menu.id === "string"; } - -function TopLevelMenuLink({ - to, - children, -}: { - to: string; - children: React.ReactNode; -}) { - const { pathname } = useLocation(); - const gleanClick = useGleanClick(); - - const isActive = pathname.startsWith(to.split("#", 2)[0]); - - return ( -
    • - gleanClick(`${MENU.CLICK_LINK}: top-level -> ${to}`)} - > - {children} - -
    • - ); -} From 262a6ef6d2ba8b87ba2ebfac8b9ea2ff3e43e323 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 17 Sep 2024 12:56:20 +0200 Subject: [PATCH 051/100] chore(test-de): build translated-content with de --- .github/workflows/test-de-build.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 0f96353c4735..5d0a1488a70e 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -81,12 +81,25 @@ jobs: - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: - repository: mdn/translated-content-de + repository: mdn/translated-content path: mdn/translated-content # See matching warning for mdn/content checkout step fetch-depth: 0 + + - uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} + with: + repository: mdn/translated-content-de + path: mdn/translated-content-de + # See matching warning for mdn/content checkout step + fetch-depth: 0 token: ${{ secrets.MDN_MTC_PAT }} + - name: Move de into translated-content + run: | + mv mdn/translated-content-de/files/de mdn/translated-content/files + rm -rf mdn/translated-content-de + - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} with: From 4f52a00644295c7e3cc2cdf760e8dcb4fb9e7e7f Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 17 Sep 2024 13:15:59 +0200 Subject: [PATCH 052/100] chore(main-menu): derive isActive from item urls --- client/src/ui/molecules/main-menu/index.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index 7d600dec9d4a..fad6d2d38b0f 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -80,9 +80,6 @@ export default function MainMenu({ isOpenOnMobile }) { id: "html", label: "HTML", to: `/${locale}/docs/Web/HTML`, - isActive: - pathname.startsWith(`/${locale}/docs/Learn/HTML`) || - pathname.startsWith(`/${locale}/docs/Web/HTML`), items: [ { description: "Learn to structure web content with HTML", @@ -104,9 +101,6 @@ export default function MainMenu({ isOpenOnMobile }) { id: "css", label: "CSS", to: `/${locale}/docs/Web/CSS`, - isActive: - pathname.startsWith(`/${locale}/docs/Learn/CSS`) || - pathname.startsWith(`/${locale}/docs/Web/CSS`), items: [ { description: "Learn to style content using CSS", @@ -133,9 +127,6 @@ export default function MainMenu({ isOpenOnMobile }) { ), to: `/${locale}/docs/Web/JavaScript`, - isActive: - pathname.startsWith(`/${locale}/docs/Learn/JavaScript`) || - pathname.startsWith(`/${locale}/docs/Web/JavaScript`), items: [ { description: "Learn to run scripts in the browser", @@ -162,7 +153,6 @@ export default function MainMenu({ isOpenOnMobile }) { ), to: `/${locale}/docs/Web/API`, - isActive: pathname.startsWith(`/${locale}/docs/Web/API`), items: [ { description: "Look up all the APIs and interfaces", @@ -177,7 +167,6 @@ export default function MainMenu({ isOpenOnMobile }) { id: "http", label: "HTTP", to: `/${locale}/docs/Web/HTTP`, - isActive: pathname.startsWith(`/${locale}/docs/Web/HTTP`), items: [ { description: "Look up status codes, headers, and more", @@ -212,9 +201,6 @@ export default function MainMenu({ isOpenOnMobile }) { id: "learn", label: "Learn", to: `/${locale}/docs/Learn`, - isActive: - pathname.startsWith("/en-US/curriculum/") || - pathname.startsWith(`/${locale}/docs/Learn`), items: [ { description: "Essential skills for front-end developers", @@ -236,7 +222,6 @@ export default function MainMenu({ isOpenOnMobile }) { id: "blog", label: "Blog", to: "/en-US/blog/", - isActive: pathname.startsWith("/en-US/blog/"), items: [ { description: "Learn about web features, and MDN", @@ -312,7 +297,9 @@ export default function MainMenu({ isOpenOnMobile }) { isMenuEntry(menu) ? ( url && pathname.startsWith(url) + )} isOpen={visibleSubMenuId === menu.id} toggle={toggleMenu} /> From df7005c2fdc671ea61adca589bc01246c1836138 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 17 Sep 2024 22:48:59 +0200 Subject: [PATCH 053/100] chore(test-de-build): enable flaws --- .github/workflows/test-de-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 5d0a1488a70e..074cdb06dcd0 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -150,7 +150,7 @@ jobs: BUILD_INTERACTIVE_EXAMPLES_BASE_URL: https://interactive-examples.mdn.allizom.net # Now is not the time to worry about flaws. - BUILD_FLAW_LEVELS: "*:ignore" + #BUILD_FLAW_LEVELS: "*:ignore" # This enables the Plus call-to-action banner and the Plus landing page REACT_APP_ENABLE_PLUS: true From dfb866a0a238fd3988b28928a59785f5c1d61fe9 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 18 Sep 2024 10:22:13 +0200 Subject: [PATCH 054/100] fix(test-de-build): build canonicals + enable pipefail --- .github/workflows/test-de-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 074cdb06dcd0..b24e4b79d470 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -265,10 +265,13 @@ jobs: run: | npm ci npm run build-redirects + npm run build-canonicals - name: Deploy Function if: ${{ ! vars.SKIP_FUNCTION }} run: |- + set -eo pipefail + for region in europe-west3; do gcloud beta functions deploy mdn-nonprod-test-de-$region \ --gen2 \ From 7a3f0f34899ea8ca9be53087b1313a0f01e9fe1b Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 18 Sep 2024 10:23:26 +0200 Subject: [PATCH 055/100] chore(test-de-build): enable writer mode --- .github/workflows/test-de-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index b24e4b79d470..ff3c1af5e58b 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -151,6 +151,7 @@ jobs: # Now is not the time to worry about flaws. #BUILD_FLAW_LEVELS: "*:ignore" + REACT_APP_WRITER_MODE: true # This enables the Plus call-to-action banner and the Plus landing page REACT_APP_ENABLE_PLUS: true From dee587bf7b5d99beaebd925ca4458105ffa07d6f Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 18 Sep 2024 13:27:09 +0200 Subject: [PATCH 056/100] chore(content): resolve symlinks in findAll() --- content/document.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/content/document.ts b/content/document.ts index 3ebb11d99120..fb1f41bdcf67 100644 --- a/content/document.ts +++ b/content/document.ts @@ -474,6 +474,7 @@ export async function findAll({ for (const root of roots) { const api = new fdir() .withFullPaths() + .withSymlinks({ resolvePaths: false }) .withErrors() .filter((filePath) => { // Exit early if it's not a sane kind of file we expect From a4c4d95f282d1dd365dc66aa6cfe92d23e4b3b50 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 18 Sep 2024 14:54:06 +0200 Subject: [PATCH 057/100] fixup! fix(test-de-build): build canonicals + enable pipefail --- .github/workflows/test-de-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index ff3c1af5e58b..398f892f9c69 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -186,6 +186,7 @@ jobs: # Playground REACT_APP_PLAYGROUND_BASE_HOST: play.de.test.mdn.allizom.net run: | + set -eo pipefail # Info about which CONTENT_* environment variables were set and to what. echo "CONTENT_ROOT=$CONTENT_ROOT" From e87167ec1df600b773d7d87b057b1bc9d19ac01f Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 18 Sep 2024 17:02:48 +0200 Subject: [PATCH 058/100] Revert "Merge branch 'topical-main-menu' into test-de" This reverts commit b44cda6c78bbc6d16adc62f601bcbcb059e4bd4d, reversing changes made to 262a6ef6d2ba8b87ba2ebfac8b9ea2ff3e43e323. --- client/src/ui/atoms/signup-link/index.tsx | 2 +- .../src/ui/molecules/guides-menu/index.scss | 23 ++ client/src/ui/molecules/guides-menu/index.tsx | 66 ++++ client/src/ui/molecules/main-menu/index.scss | 66 ---- client/src/ui/molecules/main-menu/index.tsx | 295 +++--------------- client/src/ui/molecules/plus-menu/index.scss | 23 ++ client/src/ui/molecules/plus-menu/index.tsx | 94 ++++++ .../ui/molecules/reference-menu/index.scss | 110 +++++++ .../src/ui/molecules/reference-menu/index.tsx | 84 +++++ client/src/ui/molecules/submenu/index.tsx | 1 - .../src/ui/molecules/theme-switcher/index.tsx | 2 +- .../tools.scss => tools-menu/index.scss} | 0 client/src/ui/molecules/tools-menu/index.tsx | 40 +++ 13 files changed, 490 insertions(+), 316 deletions(-) create mode 100644 client/src/ui/molecules/guides-menu/index.scss create mode 100644 client/src/ui/molecules/guides-menu/index.tsx create mode 100644 client/src/ui/molecules/plus-menu/index.scss create mode 100644 client/src/ui/molecules/plus-menu/index.tsx create mode 100644 client/src/ui/molecules/reference-menu/index.scss create mode 100644 client/src/ui/molecules/reference-menu/index.tsx rename client/src/ui/molecules/{main-menu/tools.scss => tools-menu/index.scss} (100%) create mode 100644 client/src/ui/molecules/tools-menu/index.tsx diff --git a/client/src/ui/atoms/signup-link/index.tsx b/client/src/ui/atoms/signup-link/index.tsx index b01b87349bc1..51a6a3bcf372 100644 --- a/client/src/ui/atoms/signup-link/index.tsx +++ b/client/src/ui/atoms/signup-link/index.tsx @@ -10,7 +10,7 @@ export const SignUpLink = ({ toPlans = false, gleanContext = "" }) => { const loginUrl = useLoginUrl(); const href = toPlans ? plansUrl : loginUrl; - const label = toPlans ? "Upgrade Now" : "Sign up"; + const label = toPlans ? "Upgrade Now" : "Sign up for free"; const rel = toPlans ? undefined : "nofollow"; return ( diff --git a/client/src/ui/molecules/guides-menu/index.scss b/client/src/ui/molecules/guides-menu/index.scss new file mode 100644 index 000000000000..42a234d2f98a --- /dev/null +++ b/client/src/ui/molecules/guides-menu/index.scss @@ -0,0 +1,23 @@ +@use "sass:color"; +@use "../../vars" as *; + +.guides { + .submenu .submenu-item-heading { + font-size: var(--type-smaller-font-size); + font-weight: initial; + } + + .desktop-only { + display: none; + } + + @media (min-width: $screen-lg) { + .desktop-only { + display: inherit; + } + + .mobile-only { + display: none; + } + } +} diff --git a/client/src/ui/molecules/guides-menu/index.tsx b/client/src/ui/molecules/guides-menu/index.tsx new file mode 100644 index 000000000000..402f11600180 --- /dev/null +++ b/client/src/ui/molecules/guides-menu/index.tsx @@ -0,0 +1,66 @@ +import { useLocale } from "../../../hooks"; +import { Menu } from "../menu"; + +import "./index.scss"; + +export const GuidesMenu = ({ visibleSubMenuId, toggleMenu }) => { + const locale = useLocale(); + + const menu = { + id: "guides", + label: "Guides", + to: `/${locale}/docs/Learn`, + items: [ + { + description: "Learn web development", + hasIcon: true, + extraClasses: "apis-link-container mobile-only", + iconClasses: "submenu-icon learn", + label: "Overview / MDN Learning Area", + url: `/${locale}/docs/Learn`, + }, + { + description: "Learn web development", + extraClasses: "apis-link-container desktop-only", + hasIcon: true, + iconClasses: "submenu-icon learn", + label: "MDN Learning Area", + url: `/${locale}/docs/Learn`, + }, + { + description: "Learn to structure web content with HTML", + extraClasses: "html-link-container", + hasIcon: true, + iconClasses: "submenu-icon html", + label: "HTML", + url: `/${locale}/docs/Learn/HTML`, + }, + { + description: "Learn to style content using CSS", + extraClasses: "css-link-container", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "CSS", + url: `/${locale}/docs/Learn/CSS`, + }, + { + description: "Learn to run scripts in the browser", + extraClasses: "javascript-link-container", + hasIcon: true, + iconClasses: "submenu-icon javascript", + label: "JavaScript", + url: `/${locale}/docs/Learn/JavaScript`, + }, + { + description: "Learn to make the web accessible to all", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Accessibility", + url: `/${locale}/docs/Web/Accessibility`, + }, + ], + }; + const isOpen = visibleSubMenuId === menu.id; + + return ; +}; diff --git a/client/src/ui/molecules/main-menu/index.scss b/client/src/ui/molecules/main-menu/index.scss index 459e5e18ec30..29913f4dbe63 100644 --- a/client/src/ui/molecules/main-menu/index.scss +++ b/client/src/ui/molecules/main-menu/index.scss @@ -40,22 +40,8 @@ ul.main-menu { } } - .top-level-entry .long { - display: none; - } - @media (min-width: ($screen-xl)) { gap: 1rem; - - .top-level-entry { - .long { - display: unset; - } - - .short { - display: none; - } - } } } @@ -92,55 +78,3 @@ ul.main-menu { } } } - -@media screen and (min-width: $screen-lg) { - #more-button { - display: flex; - - &::after { - display: none; - } - } -} - -.submenu-icon { - &.html { - background-color: var(--html-accent-color); - } - - &.css { - background-color: var(--css-accent-color); - } - - &.javascript { - background-color: var(--js-accent-color); - } - - &.http { - background-color: var(--http-accent-color); - } - - &.apis { - background-color: var(--apis-accent-color); - } - - &.learn { - background-color: var(--learn-accent-color); - } - - &.plus { - background-color: var(--plus-accent-color); - } - - &.curriculum { - background-color: var(--curriculum-color); - } - - &.blog { - background-color: var(--apis-accent-color); - } - - &.observatory { - background-color: var(--observatory-accent); - } -} diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx index fad6d2d38b0f..a61dc3e1e28d 100644 --- a/client/src/ui/molecules/main-menu/index.tsx +++ b/client/src/ui/molecules/main-menu/index.tsx @@ -1,17 +1,15 @@ import { useEffect, useRef, useState } from "react"; -import { Menu } from "../menu"; +import { GuidesMenu } from "../guides-menu"; +import { ReferenceMenu } from "../reference-menu"; +import { PlusMenu } from "../plus-menu"; import "./index.scss"; -import "./tools.scss"; - import { PLUS_IS_ENABLED } from "../../../env"; +import { useGleanClick } from "../../../telemetry/glean-context"; +import { MENU } from "../../../telemetry/constants"; import { useLocation } from "react-router"; -import { useIsServer, useLocale } from "../../../hooks"; -import { usePlusUrl } from "../../../plus/utils"; -import { MenuEntry } from "../submenu"; -import { useUserData } from "../../../user-context"; -import { OBSERVATORY_TITLE } from "../../../../../libs/constants"; +import { ToolsMenu } from "../tools-menu"; export default function MainMenu({ isOpenOnMobile }) { const previousActiveElement = useRef(null); @@ -66,252 +64,55 @@ export default function MainMenu({ isOpenOnMobile }) { } } - const locale = useLocale(); - - // Plus menu. - const plusUrl = usePlusUrl(); - const isServer = useIsServer(); - const userData = useUserData(); - const isAuthenticated = userData && userData.isAuthenticated; - const { pathname } = useLocation(); - - const menus = [ - { - id: "html", - label: "HTML", - to: `/${locale}/docs/Web/HTML`, - items: [ - { - description: "Learn to structure web content with HTML", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "Learn HTML", - url: `/${locale}/docs/Learn/HTML`, - }, - { - description: "Look up elements, attributes, and more", - hasIcon: true, - iconClasses: "submenu-icon html", - label: "HTML references", - url: `/${locale}/docs/Web/HTML`, - }, - ], - }, - { - id: "css", - label: "CSS", - to: `/${locale}/docs/Web/CSS`, - items: [ - { - description: "Learn to style content using CSS", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "Learn CSS", - url: `/${locale}/docs/Learn/CSS`, - }, - { - description: "Look up properties, selectors, and more", - hasIcon: true, - iconClasses: "submenu-icon css", - label: "CSS references", - url: `/${locale}/docs/Web/CSS`, - }, - ], - }, - { - id: "js", - label: ( - <> - JS - JavaScript - - ), - to: `/${locale}/docs/Web/JavaScript`, - items: [ - { - description: "Learn to run scripts in the browser", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "Learn JavaScript", - url: `/${locale}/docs/Learn/JavaScript`, - }, - { - description: "Look up objects, expressions, and more", - hasIcon: true, - iconClasses: "submenu-icon javascript", - label: "JavaScript references", - url: `/${locale}/docs/Web/JavaScript`, - }, - ], - }, - { - id: "apis", - label: ( - <> - APIs - Web APIs - - ), - to: `/${locale}/docs/Web/API`, - items: [ - { - description: "Look up all the APIs and interfaces", - hasIcon: true, - iconClasses: "submenu-icon apis", - label: "Web API references", - url: `/${locale}/docs/Web/API`, - }, - ], - }, - { - id: "http", - label: "HTTP", - to: `/${locale}/docs/Web/HTTP`, - items: [ - { - description: "Look up status codes, headers, and more", - hasIcon: true, - iconClasses: "submenu-icon http", - label: "HTTP references", - url: `/${locale}/docs/Web/HTTP`, - }, - ], - }, - { - id: "more", - label: "More", - items: [ - { - description: "Learn to make the web accessible to all", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Accessibility", - url: `/${locale}/docs/Web/Accessibility`, - }, - { - description: "Develop extensions for web browsers", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Web Extensions", - url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, - }, - ], - }, - { - id: "learn", - label: "Learn", - to: `/${locale}/docs/Learn`, - items: [ - { - description: "Essential skills for front-end developers", - hasIcon: true, - iconClasses: "submenu-icon curriculum", - label: "MDN Curriculum", - url: "/en-US/curriculum/", - }, - { - description: "Learn web development", - hasIcon: true, - iconClasses: "submenu-icon learn", - label: "MDN Learning Area", - url: `/${locale}/docs/Learn`, - }, - ], - }, - { - id: "blog", - label: "Blog", - to: "/en-US/blog/", - items: [ - { - description: "Learn about web features, and MDN", - hasIcon: true, - iconClasses: "submenu-icon blog", - label: "MDN Blog", - url: "/en-US/blog/", - }, - ], - }, - { - id: "tools", - label: "Tools", - items: [ - { - description: "Write, test and share your code", - hasIcon: true, - iconClasses: "submenu-icon", - label: "Playground", - url: `/${locale}/play`, - }, - { - description: "Scan a website for free", - hasIcon: true, - iconClasses: "submenu-icon observatory", - label: OBSERVATORY_TITLE, - url: `/en-US/observatory`, - }, - ...(PLUS_IS_ENABLED - ? [ - { - description: "A customized MDN experience", - hasIcon: true, - iconClasses: "submenu-icon plus", - label: "MDN Plus", - url: plusUrl, - }, - { - description: "Get real-time assistance and support", - hasIcon: true, - iconClasses: "submenu-icon plus", - label: "AI Help", - url: `/en-US/plus/ai-help`, - }, - ...(!isServer && isAuthenticated - ? [ - { - description: "Your saved articles from across MDN", - hasIcon: true, - iconClasses: "submenu-icon plus", - label: "Collections", - url: `/${locale}/plus/collections`, - }, - ] - : []), - { - description: "All browser compatibility updates at a glance", - hasIcon: true, - iconClasses: "submenu-icon plus", - label: "Updates", - url: `/${locale}/plus/updates`, - }, - ] - : []), - ], - }, - ].filter(Boolean) as (MenuEntry | React.ReactElement)[]; - return ( ); } -function isMenuEntry(menu: any): menu is MenuEntry { - return typeof menu.id === "string"; +function TopLevelMenuLink({ + to, + children, +}: { + to: string; + children: React.ReactNode; +}) { + const { pathname } = useLocation(); + const gleanClick = useGleanClick(); + + const isActive = pathname.startsWith(to.split("#", 2)[0]); + + return ( +
    • + gleanClick(`${MENU.CLICK_LINK}: top-level -> ${to}`)} + > + {children} + +
    • + ); } diff --git a/client/src/ui/molecules/plus-menu/index.scss b/client/src/ui/molecules/plus-menu/index.scss new file mode 100644 index 000000000000..bdc55163ef3c --- /dev/null +++ b/client/src/ui/molecules/plus-menu/index.scss @@ -0,0 +1,23 @@ +@use "sass:color"; +@use "../../vars" as *; + +.mdn-plus { + .submenu-icon { + background-color: var(--plus-accent-color); + } + + .note { + background-color: var(--background-information); + + .submenu-item-description { + display: block; + margin: 0.125rem; + } + } + + @media (min-width: $screen-lg) { + .mobile-only { + display: none; + } + } +} diff --git a/client/src/ui/molecules/plus-menu/index.tsx b/client/src/ui/molecules/plus-menu/index.tsx new file mode 100644 index 000000000000..5aa5138688c7 --- /dev/null +++ b/client/src/ui/molecules/plus-menu/index.tsx @@ -0,0 +1,94 @@ +import "./index.scss"; +import { usePlusUrl } from "../../../plus/utils"; +import { Menu } from "../menu"; +import { useIsServer, useLocale, useViewedState } from "../../../hooks"; +import { useUserData } from "../../../user-context"; +import { MenuEntry } from "../submenu"; +import { FeatureId } from "../../../constants"; +import { useLocation } from "react-router"; + +export const PlusMenu = ({ visibleSubMenuId, toggleMenu }) => { + const plusUrl = usePlusUrl(); + const locale = useLocale(); + const isServer = useIsServer(); + const userData = useUserData(); + const isAuthenticated = userData && userData.isAuthenticated; + + const { isViewed } = useViewedState(); + + // Avoid that "Plus" and "AI Help" are both active. + const { pathname } = useLocation(); + const aiHelpUrl = `/${locale}/plus/ai-help`; + const isActive = + pathname.startsWith(plusUrl.split("#", 2)[0]) && + !pathname.startsWith(aiHelpUrl); + + const plusMenu: MenuEntry = { + label: "Plus", + id: "mdn-plus", + to: plusUrl, + items: [ + { + description: "A customized MDN experience", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Overview", + url: plusUrl, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon", + label: "AI Help", + url: aiHelpUrl, + }, + ...(!isServer && isAuthenticated + ? [ + { + description: "Your saved articles from across MDN", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Collections", + url: `/${locale}/plus/collections`, + }, + ] + : []), + { + description: "All browser compatibility updates at a glance", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Updates", + dot: + Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime() + !isViewed(FeatureId.PLUS_UPDATES_V2) + ? "New feature" + : undefined, + url: `/${locale}/plus/updates`, + }, + { + description: "Learn how to use MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Documentation", + url: `/en-US/plus/docs/features/overview`, + }, + { + description: "Frequently asked questions about MDN Plus", + hasIcon: true, + iconClasses: "submenu-icon", + label: "FAQ", + url: `/en-US/plus/docs/faq`, + }, + ], + }; + const isOpen = visibleSubMenuId === plusMenu.id; + + return ( + + ); +}; diff --git a/client/src/ui/molecules/reference-menu/index.scss b/client/src/ui/molecules/reference-menu/index.scss new file mode 100644 index 000000000000..1420a142333f --- /dev/null +++ b/client/src/ui/molecules/reference-menu/index.scss @@ -0,0 +1,110 @@ +@use "sass:color"; +@use "../../vars" as *; + +.references { + .desktop-only { + display: none; + } + + @media (min-width: $screen-lg) { + .desktop-only { + display: inherit; + } + + .mobile-only { + display: none; + } + } +} + +.html-link-container { + a:hover, + a:focus { + .submenu-icon { + &.html { + background: var(--html-accent-color) !important; + } + } + } +} + +.css-link-container { + a:hover, + a:focus { + .submenu-icon { + &.css { + background-color: var(--css-accent-color) !important; + } + } + } +} + +.javascript-link-container { + a:hover, + a:focus { + .submenu-icon { + &.javascript { + background-color: var(--js-accent-color) !important; + } + } + } +} + +.http-link-container { + a:hover, + a:focus { + .submenu-icon { + &.http { + background-color: var(--http-accent-color) !important; + } + } + } +} + +.apis-link-container { + a:hover, + a:focus { + .submenu-icon { + &.apis { + background-color: var(--apis-accent-color) !important; + } + } + } +} + +.learn-link-container { + a:hover, + a:focus { + .submenu-icon { + &.learn { + background-color: var(--learn-accent-color) !important; + } + } + } +} + +.submenu-icon { + &.html { + background-color: var(--html-accent-engage); + } + + &.css { + background-color: var(--css-accent-engage); + } + + &.javascript { + background-color: var(--js-accent-engage); + } + + &.http { + background-color: var(--http-accent-engage); + } + + &.apis { + background-color: var(--apis-accent-engage); + } + + &.learn { + background-color: var(--learn-accent-engage); + } +} diff --git a/client/src/ui/molecules/reference-menu/index.tsx b/client/src/ui/molecules/reference-menu/index.tsx new file mode 100644 index 000000000000..35c550dfa9e3 --- /dev/null +++ b/client/src/ui/molecules/reference-menu/index.tsx @@ -0,0 +1,84 @@ +import { useLocale } from "../../../hooks"; +import { Menu } from "../menu"; + +import "./index.scss"; + +export const ReferenceMenu = ({ visibleSubMenuId, toggleMenu }) => { + const locale = useLocale(); + + const menu = { + id: "references", + label: "References", + to: `/${locale}/docs/Web`, + items: [ + { + description: "Web technology reference for developers", + hasIcon: true, + extraClasses: "apis-link-container mobile-only", + iconClasses: "submenu-icon", + label: "Overview / Web Technology", + url: `/${locale}/docs/Web`, + }, + { + description: "Structure of content on the web", + extraClasses: "html-link-container", + hasIcon: true, + iconClasses: "submenu-icon html", + label: "HTML", + url: `/${locale}/docs/Web/HTML`, + }, + { + description: "Code used to describe document style", + extraClasses: "css-link-container", + hasIcon: true, + iconClasses: "submenu-icon css", + label: "CSS", + url: `/${locale}/docs/Web/CSS`, + }, + { + description: "General-purpose scripting language", + extraClasses: "javascript-link-container", + hasIcon: true, + iconClasses: "submenu-icon javascript", + label: "JavaScript", + url: `/${locale}/docs/Web/JavaScript`, + }, + { + description: "Protocol for transmitting web resources", + extraClasses: "http-link-container", + hasIcon: true, + iconClasses: "submenu-icon http", + label: "HTTP", + url: `/${locale}/docs/Web/HTTP`, + }, + { + description: "Interfaces for building web applications", + extraClasses: "apis-link-container", + hasIcon: true, + iconClasses: "submenu-icon apis", + label: "Web APIs", + url: `/${locale}/docs/Web/API`, + }, + { + description: "Developing extensions for web browsers", + extraClasses: "apis-link-container", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Web Extensions", + url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`, + }, + { + description: "Web technology reference for developers", + extraClasses: "apis-link-container desktop-only", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Web Technology", + url: `/${locale}/docs/Web`, + }, + ], + }; + + const isOpen = visibleSubMenuId === menu.id; + + return ; +}; diff --git a/client/src/ui/molecules/submenu/index.tsx b/client/src/ui/molecules/submenu/index.tsx index caf480f0f7c8..1931e98db100 100644 --- a/client/src/ui/molecules/submenu/index.tsx +++ b/client/src/ui/molecules/submenu/index.tsx @@ -17,7 +17,6 @@ export type SubmenuItem = { export type MenuEntry = { id: string; - isActive?: boolean; items: SubmenuItem[]; label: string | ReactNode; to?: string; diff --git a/client/src/ui/molecules/theme-switcher/index.tsx b/client/src/ui/molecules/theme-switcher/index.tsx index add18e2e3627..91e143ccb6f0 100644 --- a/client/src/ui/molecules/theme-switcher/index.tsx +++ b/client/src/ui/molecules/theme-switcher/index.tsx @@ -82,7 +82,7 @@ export const ThemeSwitcher = () => { setIsOpen(!isOpen); }} > - Theme + Theme diff --git a/client/src/ui/molecules/main-menu/tools.scss b/client/src/ui/molecules/tools-menu/index.scss similarity index 100% rename from client/src/ui/molecules/main-menu/tools.scss rename to client/src/ui/molecules/tools-menu/index.scss diff --git a/client/src/ui/molecules/tools-menu/index.tsx b/client/src/ui/molecules/tools-menu/index.tsx new file mode 100644 index 000000000000..ce1a83b316e2 --- /dev/null +++ b/client/src/ui/molecules/tools-menu/index.tsx @@ -0,0 +1,40 @@ +import { OBSERVATORY_TITLE } from "../../../../../libs/constants"; +import { useLocale } from "../../../hooks"; +import { Menu } from "../menu"; + +import "./index.scss"; + +export const ToolsMenu = ({ visibleSubMenuId, toggleMenu }) => { + const locale = useLocale(); + + const menu = { + id: "tools", + label: "Tools", + items: [ + { + description: "Write, test and share your code", + hasIcon: true, + iconClasses: "submenu-icon", + label: "Playground", + url: `/${locale}/play`, + }, + { + description: "Scan a website for free", + hasIcon: true, + iconClasses: "submenu-icon", + label: OBSERVATORY_TITLE, + url: `/en-US/observatory`, + }, + { + description: "Get real-time assistance and support", + hasIcon: true, + iconClasses: "submenu-icon", + label: "AI Help", + url: `/en-US/plus/ai-help`, + }, + ], + }; + const isOpen = visibleSubMenuId === menu.id; + + return ; +}; From 42ce509b98244d28e855be88450d48e3b8dbd776 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 18 Sep 2024 19:05:10 +0200 Subject: [PATCH 059/100] feat(build/cli): add {sort,from} options Allows continuing builds from the last failed path. --- build/cli.ts | 16 ++++++++++++++-- content/document.ts | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/build/cli.ts b/build/cli.ts index 25d4507951ef..302204e5018b 100644 --- a/build/cli.ts +++ b/build/cli.ts @@ -140,13 +140,16 @@ async function buildDocuments( quiet = false, interactive = false, noHTML = false, - locales: Map = new Map() + locales: Map = new Map(), + { sort = false, from = null }: { sort?: boolean; from?: string } ): Promise { // If a list of files was set, it came from the CLI. // Override whatever was in the build options. const findAllOptions = { ...options, locales, + sort, + from, }; if (files) { findAllOptions.files = new Set(files); @@ -460,7 +463,9 @@ interface BuildArgsAndOptions { nohtml?: boolean; locale?: string[]; notLocale?: string[]; + sort?: boolean; sitemapIndex?: boolean; + from?: string; }; } @@ -484,9 +489,15 @@ program default: [], validator: [...VALID_LOCALES.keys()], }) + .option("--sort", "Build pages in alphabetical order", { + default: false, + }) .option("--sitemap-index", "Build a sitemap index file", { default: false, }) + .option("--from ", "Build from the first path matching this needle", { + default: null, + }) .argument("[files...]", "specific files to build") .action(async ({ args, options }: BuildArgsAndOptions) => { try { @@ -566,7 +577,8 @@ program Boolean(options.quiet), Boolean(options.interactive), Boolean(options.nohtml), - locales + locales, + { sort: options.sort ?? false, from: options.from ?? null } ); const t1 = new Date(); const count = Object.values(slugPerLocale).reduce( diff --git a/content/document.ts b/content/document.ts index fb1f41bdcf67..f76980c1eaa9 100644 --- a/content/document.ts +++ b/content/document.ts @@ -456,6 +456,8 @@ export async function findAll({ files = new Set(), folderSearch = null, locales = new Map(), + sort = false, + from = null, } = {}) { if (!(files instanceof Set)) { throw new TypeError("'files' not a Set"); @@ -527,6 +529,22 @@ export async function findAll({ const output: PathsOutput = await api.withPromise(); filePaths.push(...output); } + + if (sort) { + filePaths.sort(); + } + + if (from) { + const fromPath = filePaths + .slice() + .sort() + .find((path) => path.includes(from)); + console.log({ after: from, lastItem: fromPath }); + filePaths + .filter((path) => path.localeCompare(fromPath) < 0) + .forEach((path) => filePaths.splice(filePaths.indexOf(path), 1)); + } + return { count: filePaths.length, *iterPaths() { From 33c43b21bbdfc009a66f386838bf1b4694be83e0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 20 Sep 2024 23:34:15 +0200 Subject: [PATCH 060/100] Revert "chore(test-de-build): enable writer mode" This reverts commit 7a3f0f34899ea8ca9be53087b1313a0f01e9fe1b. --- .github/workflows/test-de-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 398f892f9c69..da6ebbee3daa 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -151,7 +151,6 @@ jobs: # Now is not the time to worry about flaws. #BUILD_FLAW_LEVELS: "*:ignore" - REACT_APP_WRITER_MODE: true # This enables the Plus call-to-action banner and the Plus landing page REACT_APP_ENABLE_PLUS: true From 85216789429280b08d3688f3c6a8d8f1b07c4bc5 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 20 Sep 2024 23:39:45 +0200 Subject: [PATCH 061/100] chore(de): link to correct repo (workaround) --- build/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/index.ts b/build/index.ts index ccacb3b2b8ef..724bd2cbca52 100644 --- a/build/index.ts +++ b/build/index.ts @@ -158,6 +158,12 @@ function injectSource(doc, document, metadata) { last_commit_url: getLastCommitURL(root, metadata.hash), filename, }; + if (doc.locale === "de") { + doc.source.github_url = doc.source.github_url.replace( + "translated-content", + "translated-content-de" + ); + } } export interface BuiltDocument { From 3f0fe622aeeeda1f9aee5507b24a55d2eb352b9f Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 23 Sep 2024 11:29:51 +0200 Subject: [PATCH 062/100] chore(language-menu): clarify padding --- .../src/ui/organisms/article-actions/language-menu/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index 8a9c211ef96a..e36fad565684 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -18,6 +18,7 @@ } .submenu-item { + // Reduce padding compared to other menus. padding: 0.5rem !important; &.locale-redirect-setting { @@ -25,7 +26,6 @@ border-radius: 0 !important; display: block; font-size: var(--type-tiny-font-size); - padding: 0.25em 0.5em; &:hover { background-color: unset; From ddeaf6d7fe6228f613b50f2b15090beaf60ce35c Mon Sep 17 00:00:00 2001 From: Claas Augner <495429+caugner@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:36:34 +0200 Subject: [PATCH 063/100] Apply suggestions from code review Co-authored-by: Leo McArdle --- .../ui/organisms/article-actions/language-menu/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index dd13d4c70edd..103c1ae47076 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -133,8 +133,8 @@ function LocaleRedirectSetting() { const [value, setValue] = useState(false); useEffect(() => { - setValue(!!getCookieValue(PREFERRED_LOCALE_COOKIE_NAME)); - }, [setValue]); + setValue(Boolean(getCookieValue(PREFERRED_LOCALE_COOKIE_NAME))); + }, []); function toggle(event) { const newValue = event.target.checked; @@ -147,7 +147,7 @@ function LocaleRedirectSetting() { } else { deleteCookie(PREFERRED_LOCALE_COOKIE_NAME); } - setValue(event.target.checked); + setValue(newValue); gleanClick(`${LANGUAGE_REDIRECT}: ${locale} -> ${Number(newValue)}`); } From 2f348f7e5862910e532dbddfaedce14ffbc57f05 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 23 Sep 2024 11:39:26 +0200 Subject: [PATCH 064/100] refactor(language-menu): extract cookie max-age to constant --- .../src/ui/organisms/article-actions/language-menu/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 103c1ae47076..2ccd736d19e9 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -20,6 +20,7 @@ import { Switch } from "../../../atoms/switch"; // This needs to match what's set in 'libs/constants.js' on the server/builder! const PREFERRED_LOCALE_COOKIE_NAME = "preferredlocale"; +const PREFERRED_LOCALE_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 3; // 3 years. export function LanguageMenu({ onClose, @@ -46,7 +47,7 @@ export function LanguageMenu({ for (const translation of translations) { if (translation.locale === newLocale) { setCookieValue(PREFERRED_LOCALE_COOKIE_NAME, translation.locale, { - maxAge: 60 * 60 * 24 * 365 * 3, + maxAge: PREFERRED_LOCALE_COOKIE_MAX_AGE, }); } } From da1d442c6b26fd86aaaed319e831ee313bf7ece2 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 24 Sep 2024 13:58:35 +0200 Subject: [PATCH 065/100] Revert "chore(test-de): build translated-content with de" This reverts commit 262a6ef6d2ba8b87ba2ebfac8b9ea2ff3e43e323. --- .github/workflows/test-de-build.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index da6ebbee3daa..cddbbf79cbcc 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -78,28 +78,15 @@ jobs: repository: mdn/curriculum path: mdn/curriculum - - uses: actions/checkout@v4 - if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} - with: - repository: mdn/translated-content - path: mdn/translated-content - # See matching warning for mdn/content checkout step - fetch-depth: 0 - - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: repository: mdn/translated-content-de - path: mdn/translated-content-de + path: mdn/translated-content # See matching warning for mdn/content checkout step fetch-depth: 0 token: ${{ secrets.MDN_MTC_PAT }} - - name: Move de into translated-content - run: | - mv mdn/translated-content-de/files/de mdn/translated-content/files - rm -rf mdn/translated-content-de - - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} with: From 3fad7b0d9c66da0a919ce74b4b85aaddb83f2366 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 24 Sep 2024 14:00:27 +0200 Subject: [PATCH 066/100] chore(test-de): use mdn/translated-content-de --- .github/workflows/test-de-build.yml | 6 +++--- build/index.ts | 6 ------ build/spas.ts | 2 +- .../src/document/molecules/localized-content-note/index.tsx | 2 +- client/src/document/on-github.tsx | 2 +- docs/envvars.md | 2 +- libs/env/index.js | 2 +- 7 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index cddbbf79cbcc..fbff5b0e3662 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -82,7 +82,7 @@ jobs: if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: repository: mdn/translated-content-de - path: mdn/translated-content + path: mdn/translated-content-de # See matching warning for mdn/content checkout step fetch-depth: 0 token: ${{ secrets.MDN_MTC_PAT }} @@ -120,7 +120,7 @@ jobs: # Remember, the mdn/content repo got cloned into `pwd` into a # sub-folder called "mdn/content" CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files - CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files + CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content-de/files CONTRIBUTOR_SPOTLIGHT_ROOT: ${{ github.workspace }}/mdn/mdn-contributor-spotlight/contributors BLOG_ROOT: ${{ github.workspace }}/mdn/mdn-studio/content/posts CURRICULUM_ROOT: ${{ github.workspace }}/mdn/curriculum @@ -249,7 +249,7 @@ jobs: working-directory: cloud-function env: CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files - CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files + CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content-de/files run: | npm ci npm run build-redirects diff --git a/build/index.ts b/build/index.ts index 724bd2cbca52..ccacb3b2b8ef 100644 --- a/build/index.ts +++ b/build/index.ts @@ -158,12 +158,6 @@ function injectSource(doc, document, metadata) { last_commit_url: getLastCommitURL(root, metadata.hash), filename, }; - if (doc.locale === "de") { - doc.source.github_url = doc.source.github_url.replace( - "translated-content", - "translated-content-de" - ); - } } export interface BuiltDocument { diff --git a/build/spas.ts b/build/spas.ts index 70218b1f75c2..df5b51c1bf78 100644 --- a/build/spas.ts +++ b/build/spas.ts @@ -507,7 +507,7 @@ async function fetchGitHubPRs(repo, count = 5) { } async function fetchRecentContributions() { - const repos = ["mdn/content", "mdn/translated-content"]; + const repos = ["mdn/content", "mdn/translated-content-de"]; const countPerRepo = 5; const pullRequests = ( await Promise.all( diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index b7879912e4a5..89301205d268 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -71,7 +71,7 @@ export function LocalizedContentNote({ const url = isActive ? activeLocaleNoteContent[locale]?.url || activeLocaleNoteContent["en-US"].url - : "https://github.com/mdn/translated-content/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; + : "https://github.com/mdn/translated-content-de/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; const type = isActive ? "neutral" : "warning"; return ; diff --git a/client/src/document/on-github.tsx b/client/src/document/on-github.tsx index f73b59ab8a3f..fa32810d024c 100644 --- a/client/src/document/on-github.tsx +++ b/client/src/document/on-github.tsx @@ -55,7 +55,7 @@ function NewIssueOnGitHubLink({ url.pathname = locale !== "en-US" - ? "/mdn/translated-content/issues/new" + ? "/mdn/translated-content-de/issues/new" : "/mdn/content/issues/new"; sp.set( "template", diff --git a/docs/envvars.md b/docs/envvars.md index 0d70412bb960..c66b48db3722 100644 --- a/docs/envvars.md +++ b/docs/envvars.md @@ -35,7 +35,7 @@ Path to the content files, cloned from https://github.com/mdn/content. **Default: `../translated-content/files`** Path to the translated content files, cloned from -https://github.com/mdn/translated-content. +https://github.com/mdn/translated-content-de. ### `CONTRIBUTOR_SPOTLIGHT_ROOT` diff --git a/libs/env/index.js b/libs/env/index.js index f6a71a7ad79e..9b16e91923b2 100644 --- a/libs/env/index.js +++ b/libs/env/index.js @@ -104,7 +104,7 @@ export const REPOSITORY_URLS = { export const ROOTS = [CONTENT_ROOT]; if (CONTENT_TRANSLATED_ROOT) { ROOTS.push(CONTENT_TRANSLATED_ROOT); - REPOSITORY_URLS[CONTENT_TRANSLATED_ROOT] = "mdn/translated-content"; + REPOSITORY_URLS[CONTENT_TRANSLATED_ROOT] = "mdn/translated-content-de"; } function correctPathFromEnv(envVarName) { From 6358e189727e0213f184d513dc838c8d449d12c7 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 2 Oct 2024 11:50:35 +0200 Subject: [PATCH 067/100] refactor(note-banner): pass link text as children --- .../document/molecules/localized-content-note/index.tsx | 6 +++++- client/src/document/molecules/note-banner/index.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index 89301205d268..986cba465edb 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -74,5 +74,9 @@ export function LocalizedContentNote({ : "https://github.com/mdn/translated-content-de/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; const type = isActive ? "neutral" : "warning"; - return ; + return ( + + {linkText} + + ); } diff --git a/client/src/document/molecules/note-banner/index.tsx b/client/src/document/molecules/note-banner/index.tsx index f4147383cd16..8430a3954cc4 100644 --- a/client/src/document/molecules/note-banner/index.tsx +++ b/client/src/document/molecules/note-banner/index.tsx @@ -1,19 +1,21 @@ +import * as React from "react"; + import NoteCard from "../../../ui/molecules/notecards"; export function NoteBanner({ - linkText, url, type, + children, }: { - linkText: string; url: string; type: "neutral" | "warning"; + children: React.ReactNode; }) { return (

      - {linkText} + {children}

      From 034b664e3262728657319afea498e978cd46cfde Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 2 Oct 2024 11:55:51 +0200 Subject: [PATCH 068/100] chore(localized-content-note): add disclaimer + link to preview announcement --- .../molecules/localized-content-note/index.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index 986cba465edb..34597016a7fb 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -9,8 +9,15 @@ export function LocalizedContentNote({ }) { const activeLocaleNoteContent = { de: { - linkText: "Diese Seite wurde automatisch aus dem Englischen übersetzt.", - url: "/en-US/docs/MDN/Community/Contributing/Translated_content#active_locales", + linkText: ( + <> + Diese Seite wurde mit GPT-4o aus dem Englischen übersetzt. +
      + Disclaimer: Dies ist eine Vorschau. Übersetzungen + können unvollständig oder fehlerhaft sein. + + ), + url: "https://github.com/orgs/mdn/discussions/741", }, "en-US": { linkText: From a91dba820365a52542282fd5d60ec0741710e753 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 7 Oct 2024 19:24:59 +0200 Subject: [PATCH 069/100] chore(deps): bump cookie from 0.5.0 to 0.7.2 in /cloud-function --- cloud-function/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cloud-function/package-lock.json b/cloud-function/package-lock.json index 86e818501942..9b58470c7311 100644 --- a/cloud-function/package-lock.json +++ b/cloud-function/package-lock.json @@ -1476,9 +1476,9 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -4434,7 +4434,7 @@ "license": "MPL-2.0", "dependencies": { "accept-language-parser": "^1.5.0", - "cookie": "^0.5.0" + "cookie": "^0.7.0" } }, "src/internal/pong": { From 43a2350c6acfd92ce65067bde69546bcbec1c0d4 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 14 Oct 2024 11:39:18 +0200 Subject: [PATCH 070/100] chore(test-de): sync translated content --- .github/workflows/test-de-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index fbff5b0e3662..7fc5bdfa2a24 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -182,7 +182,7 @@ jobs: yarn build:sw yarn build:prepare - #yarn tool sync-translated-content + yarn tool sync-translated-content # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. From e63a13b282edd733ce18d9faba400ca4bb63fd7a Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 16 Oct 2024 10:22:43 +0200 Subject: [PATCH 071/100] fix(test-de): only sync de --- .github/workflows/test-de-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 7fc5bdfa2a24..31db0444bc23 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -182,7 +182,7 @@ jobs: yarn build:sw yarn build:prepare - yarn tool sync-translated-content + yarn tool sync-translated-content de # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. From 2139009cc0ccdec32dd10c06d48aefef41b4fef5 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 16 Oct 2024 11:55:34 +0200 Subject: [PATCH 072/100] chore(test-de): rename steps --- .github/workflows/test-de-build.yml | 37 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 31db0444bc23..4fe75a253407 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -51,9 +51,11 @@ jobs: if: github.repository == 'mdn/yari' && github.ref_name == 'test-de' steps: - - uses: actions/checkout@v4 + - name: Checkout (yari) + uses: actions/checkout@v4 - - uses: actions/checkout@v4 + - name: Checkout (content) + uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: repository: mdn/content @@ -64,7 +66,8 @@ jobs: # so we can figure out each document's last-modified date. fetch-depth: 0 - - uses: actions/checkout@v4 + - name: Checkout (mdn-studio) + uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} with: repository: mdn/mdn-studio @@ -72,13 +75,15 @@ jobs: lfs: true token: ${{ secrets.MDN_STUDIO_PAT }} - - uses: actions/checkout@v4 + - name: Checkout (curriculum) + uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} with: repository: mdn/curriculum path: mdn/curriculum - - uses: actions/checkout@v4 + - name: Checkout (translated-content-de) + uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: repository: mdn/translated-content-de @@ -87,34 +92,36 @@ jobs: fetch-depth: 0 token: ${{ secrets.MDN_MTC_PAT }} - - uses: actions/checkout@v4 + - name: Checkout (contributor-spotlight) + uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} with: repository: mdn/mdn-contributor-spotlight path: mdn/mdn-contributor-spotlight - - name: Setup Node.js environment + - name: Setup Node.js if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" cache: yarn - - name: Install all yarn packages + - name: Install if: ${{ ! vars.SKIP_BUILD }} run: yarn --frozen-lockfile env: # https://github.com/microsoft/vscode-ripgrep#github-api-limit-note GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Print information about build + - name: Print notes + if: github.event.inputs.notes run: | - echo "notes: ${{ github.event.inputs.notes || env.DEFAULT_NOTES }}" + echo "notes: ${{ github.event.inputs.notes }}" - - name: Print information about CPU + - name: Print CPU info run: cat /proc/cpuinfo - - name: Build everything + - name: Build if: ${{ ! vars.SKIP_BUILD }} env: # Remember, the mdn/content repo got cloned into `pwd` into a @@ -214,7 +221,7 @@ jobs: yarn tool whatsdeployed $CONTENT_ROOT --output client/build/_whatsdeployed/content.json yarn tool whatsdeployed $CONTENT_TRANSLATED_ROOT --output client/build/_whatsdeployed/translated-content.json - - name: Authenticate with GCP + - name: Auth (Cloud Storage) uses: google-github-actions/auth@v2 with: token_format: access_token @@ -230,7 +237,7 @@ jobs: gsutil -q -m -h "Cache-Control: public, max-age=3600" cp -r client/build/static gs://${{ vars.GCP_BUCKET_NAME }}/main/ gsutil -q -m -h "Cache-Control: public, max-age=3600" rsync -cdrj html,json,txt -y "^static/" client/build gs://${{ vars.GCP_BUCKET_NAME }}/main - - name: Authenticate with GCP + - name: Auth (Cloud Function) if: ${{ ! vars.SKIP_FUNCTION }} uses: google-github-actions/auth@v2 with: @@ -255,7 +262,7 @@ jobs: npm run build-redirects npm run build-canonicals - - name: Deploy Function + - name: Deploy function if: ${{ ! vars.SKIP_FUNCTION }} run: |- set -eo pipefail From 34cb4725ab024063f48452c5d391d30cdf60d66a Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 16:27:41 +0200 Subject: [PATCH 073/100] chore(language-menu): mark de as experimental --- .../article-actions/language-menu/index.scss | 6 ++++++ .../article-actions/language-menu/index.tsx | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.scss b/client/src/ui/organisms/article-actions/language-menu/index.scss index d5fcb7ef35a8..fac48b7a1df2 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.scss +++ b/client/src/ui/organisms/article-actions/language-menu/index.scss @@ -59,6 +59,12 @@ } } } + + .icon-experimental { + background-color: var(--icon-primary); + margin-left: 0.5em; + vertical-align: text-top; + } } @media (min-width: $screen-md) { diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 0b189c54d7cd..6f4d7ce014b9 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -95,6 +95,7 @@ export function LanguageMenu({ onClickHandler={() => setIsOpen(!isOpen)} > {native} + @@ -126,6 +127,7 @@ function LanguageMenuItem({ className="button submenu-item" > {translation.native} + ); } @@ -178,3 +180,17 @@ function LocaleRedirectSetting() { ); } + +function LocaleStatusIcon({ locale }: { locale: string }) { + switch (locale) { + case "de": + return ( + + + + ); + + default: + return null; + } +} From d1534b3b04173aafba6cdc8d98f0c88775fd58e7 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 16:35:50 +0200 Subject: [PATCH 074/100] chore(de): use experimental icon in banner --- .../src/document/molecules/localized-content-note/index.tsx | 2 +- client/src/document/molecules/note-banner/index.tsx | 5 +++-- client/src/types/notecards.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index 34597016a7fb..dfdff0462a84 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -80,7 +80,7 @@ export function LocalizedContentNote({ activeLocaleNoteContent["en-US"].url : "https://github.com/mdn/translated-content-de/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; - const type = isActive ? "neutral" : "warning"; + const type = locale === "de" ? "experimental" : isActive ? "info" : "warning"; return ( {linkText} diff --git a/client/src/document/molecules/note-banner/index.tsx b/client/src/document/molecules/note-banner/index.tsx index 8430a3954cc4..ac3e3cbd049a 100644 --- a/client/src/document/molecules/note-banner/index.tsx +++ b/client/src/document/molecules/note-banner/index.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import NoteCard from "../../../ui/molecules/notecards"; +import { NotecardType } from "../../../types/notecards"; export function NoteBanner({ url, @@ -8,11 +9,11 @@ export function NoteBanner({ children, }: { url: string; - type: "neutral" | "warning"; + type: NotecardType; children: React.ReactNode; }) { return ( - +

      {children} diff --git a/client/src/types/notecards.ts b/client/src/types/notecards.ts index a4298b8666c5..3454a44ab0c0 100644 --- a/client/src/types/notecards.ts +++ b/client/src/types/notecards.ts @@ -4,6 +4,7 @@ type NotecardType = | "deprecated" | "error" | "negative" - | "info"; + | "info" + | "experimental"; export type { NotecardType }; From 4bec46caa6216c156d587c47265f20fe1f7f8695 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 17:14:48 +0200 Subject: [PATCH 075/100] chore(test-de-build): remove MDN_MTC_PAT --- .github/workflows/test-de-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 4fe75a253407..21881f02e789 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -90,7 +90,6 @@ jobs: path: mdn/translated-content-de # See matching warning for mdn/content checkout step fetch-depth: 0 - token: ${{ secrets.MDN_MTC_PAT }} - name: Checkout (contributor-spotlight) uses: actions/checkout@v4 From 4c241b95c24b0d5c794c347e8f29398bdec79e61 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 17:45:08 +0200 Subject: [PATCH 076/100] chore(ssr): do not index German locale --- ssr/render.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ssr/render.tsx b/ssr/render.tsx index cd8164e256fd..7859de7d28bc 100644 --- a/ssr/render.tsx +++ b/ssr/render.tsx @@ -144,6 +144,10 @@ export default function render( "_" ); + if (locale === "de") { + // Prevent experimental German locale from being indexed. + onlyFollow = true; + } const robotsContent = !ALWAYS_ALLOW_ROBOTS || (doc && doc.noIndexing) || noIndexing ? "noindex, nofollow" From b2efc18fcafc0cd2ec047631f6ee4c80e415e71f Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 17:45:52 +0200 Subject: [PATCH 077/100] Revert "feat(build/cli): add {sort,from} options" This reverts commit 42ce509b98244d28e855be88450d48e3b8dbd776. --- build/cli.ts | 16 ++-------------- content/document.ts | 18 ------------------ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/build/cli.ts b/build/cli.ts index 6d7746a7eeeb..b59bc16eb1a8 100644 --- a/build/cli.ts +++ b/build/cli.ts @@ -135,16 +135,13 @@ async function buildDocuments( quiet = false, interactive = false, noHTML = false, - locales: Map = new Map(), - { sort = false, from = null }: { sort?: boolean; from?: string } + locales: Map = new Map() ): Promise { // If a list of files was set, it came from the CLI. // Override whatever was in the build options. const findAllOptions = { ...options, locales, - sort, - from, }; if (files) { findAllOptions.files = new Set(files); @@ -458,9 +455,7 @@ interface BuildArgsAndOptions { nohtml?: boolean; locale?: string[]; notLocale?: string[]; - sort?: boolean; sitemapIndex?: boolean; - from?: string; }; } @@ -484,15 +479,9 @@ program default: [], validator: [...VALID_LOCALES.keys()], }) - .option("--sort", "Build pages in alphabetical order", { - default: false, - }) .option("--sitemap-index", "Build a sitemap index file", { default: false, }) - .option("--from ", "Build from the first path matching this needle", { - default: null, - }) .argument("[files...]", "specific files to build") .action(async ({ args, options }: BuildArgsAndOptions) => { try { @@ -572,8 +561,7 @@ program Boolean(options.quiet), Boolean(options.interactive), Boolean(options.nohtml), - locales, - { sort: options.sort ?? false, from: options.from ?? null } + locales ); const t1 = new Date(); const count = Object.values(slugPerLocale).reduce( diff --git a/content/document.ts b/content/document.ts index f76980c1eaa9..fb1f41bdcf67 100644 --- a/content/document.ts +++ b/content/document.ts @@ -456,8 +456,6 @@ export async function findAll({ files = new Set(), folderSearch = null, locales = new Map(), - sort = false, - from = null, } = {}) { if (!(files instanceof Set)) { throw new TypeError("'files' not a Set"); @@ -529,22 +527,6 @@ export async function findAll({ const output: PathsOutput = await api.withPromise(); filePaths.push(...output); } - - if (sort) { - filePaths.sort(); - } - - if (from) { - const fromPath = filePaths - .slice() - .sort() - .find((path) => path.includes(from)); - console.log({ after: from, lastItem: fromPath }); - filePaths - .filter((path) => path.localeCompare(fromPath) < 0) - .forEach((path) => filePaths.splice(filePaths.indexOf(path), 1)); - } - return { count: filePaths.length, *iterPaths() { From 2eddb179055f79811e95366172814e26158f1768 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 17:46:28 +0200 Subject: [PATCH 078/100] Revert "fix(build): avoid error with check-images" This reverts commit 37c09c2143356a1e1fbab92dc5fc7f2270ccf917. --- build/check-images.ts | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/build/check-images.ts b/build/check-images.ts index b4053ff541fa..3d6546c2d252 100644 --- a/build/check-images.ts +++ b/build/check-images.ts @@ -179,25 +179,21 @@ export function checkImageReferences( // image name, if the file didn't exist the document doesn't exist. const parentDocument = Document.findByURL(path.dirname(finalSrc)); - if (parentDocument) { - // Base the final URL on the parent document + image file name lowercase. - finalSrc = `${parentDocument.url}/${path - .basename(finalSrc) - .toLowerCase()}`; + // Base the final URL on the parent document + image file name lowercase. + finalSrc = `${parentDocument.url}/${path + .basename(finalSrc) + .toLowerCase()}`; - if (src.startsWith("/")) { - // E.g. Date: Fri, 25 Oct 2024 17:47:25 +0200 Subject: [PATCH 079/100] fix(baseline): correct German BCD anchor --- client/src/document/baseline-indicator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/document/baseline-indicator.tsx b/client/src/document/baseline-indicator.tsx index 3ced6712b50d..6c4a0945d030 100644 --- a/client/src/document/baseline-indicator.tsx +++ b/client/src/document/baseline-indicator.tsx @@ -40,7 +40,7 @@ const ENGINES: { ]; const LOCALIZED_BCD_IDS = { - de: "browserkompatibilität", + de: "browser-kompatibilität", "en-US": "browser_compatibility", es: "compatibilidad_con_navegadores", fr: "compatibilité_des_navigateurs", From bea0ddd9c104a93279518c4d65ad6123c2f767a8 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 17:54:36 +0200 Subject: [PATCH 080/100] fix(de): use translated-content-de only for de --- build/index.ts | 15 ++++++++++----- build/spas.ts | 2 +- .../molecules/localized-content-note/index.tsx | 2 +- client/src/document/on-github.tsx | 9 +++++---- docs/envvars.md | 2 +- libs/env/index.d.ts | 3 --- libs/env/index.js | 8 -------- libs/locale-utils/index.d.ts | 1 + libs/locale-utils/index.js | 11 +++++++++++ server/translations.ts | 13 +++++-------- 10 files changed, 35 insertions(+), 31 deletions(-) diff --git a/build/index.ts b/build/index.ts index 6623779b0ca7..ed33f06ad16a 100644 --- a/build/index.ts +++ b/build/index.ts @@ -13,7 +13,7 @@ import { import { Doc } from "../libs/types/document.js"; import { Document, execGit, slugToFolder } from "../content/index.js"; -import { CONTENT_ROOT, REPOSITORY_URLS } from "../libs/env/index.js"; +import { CONTENT_ROOT } from "../libs/env/index.js"; import * as kumascript from "../kumascript/index.js"; import { DEFAULT_LOCALE, FLAW_LEVELS } from "../libs/constants/index.js"; @@ -44,6 +44,7 @@ import { postProcessSmallerHeadingIDs, } from "./utils.js"; import { addBaseline } from "./web-features.js"; +import { getRepositoryByLocale } from "../libs/locale-utils/index.js"; export { default as SearchIndex } from "./search-index.js"; export { gather as gatherGitHistory } from "./git-history.js"; export { buildSPAs } from "./spas.js"; @@ -133,7 +134,10 @@ function injectNotecardOnWarnings($: cheerio.CheerioAPI) { * @param {String} folder - the current folder we're processing. */ function getGitHubURL(root: string, folder: string, filename: string) { - const baseURL = `https://github.com/${REPOSITORY_URLS[root]}`; + const [locale] = folder.split("/", 2); + const repo = getRepositoryByLocale(locale); + const baseURL = `https://github.com/mdn/${repo}/`; + return `${baseURL}/blob/${getCurrentGitBranch( root )}/files/${folder}/${filename}`; @@ -143,8 +147,9 @@ function getGitHubURL(root: string, folder: string, filename: string) { * Return the full URL directly to the last commit affecting this file on GitHub. * @param {String} hash - the full hash to point to. */ -export function getLastCommitURL(root: string, hash: string) { - const baseURL = `https://github.com/${REPOSITORY_URLS[root]}`; +export function getLastCommitURL(locale: string, hash: string) { + const repo = getRepositoryByLocale(locale); + const baseURL = `https://github.com/mdn/${repo}`; return `${baseURL}/commit/${hash}`; } @@ -155,7 +160,7 @@ function injectSource(doc, document, metadata) { doc.source = { folder, github_url: getGitHubURL(root, folder, filename), - last_commit_url: getLastCommitURL(root, metadata.hash), + last_commit_url: getLastCommitURL(doc.locale, metadata.hash), filename, }; } diff --git a/build/spas.ts b/build/spas.ts index 5466de1c4e10..7076f3f4c014 100644 --- a/build/spas.ts +++ b/build/spas.ts @@ -502,7 +502,7 @@ async function fetchGitHubPRs(repo, count = 5) { } async function fetchRecentContributions() { - const repos = ["mdn/content", "mdn/translated-content-de"]; + const repos = ["mdn/content", "mdn/translated-content"]; const countPerRepo = 5; const pullRequests = ( await Promise.all( diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index dfdff0462a84..fcc0bdbef811 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -78,7 +78,7 @@ export function LocalizedContentNote({ const url = isActive ? activeLocaleNoteContent[locale]?.url || activeLocaleNoteContent["en-US"].url - : "https://github.com/mdn/translated-content-de/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; + : "https://github.com/mdn/translated-content/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; const type = locale === "de" ? "experimental" : isActive ? "info" : "warning"; return ( diff --git a/client/src/document/on-github.tsx b/client/src/document/on-github.tsx index fa32810d024c..f8c0e0ef3d51 100644 --- a/client/src/document/on-github.tsx +++ b/client/src/document/on-github.tsx @@ -1,3 +1,5 @@ +import { useMemo } from "react"; +import { getRepositoryByLocale } from "../../../libs/locale-utils"; import { Doc } from "../../../libs/types/document"; export function OnGitHubLink({ doc }: { doc: Doc }) { @@ -53,10 +55,9 @@ function NewIssueOnGitHubLink({ const url = new URL("https://github.com/"); const sp = new URLSearchParams(); - url.pathname = - locale !== "en-US" - ? "/mdn/translated-content-de/issues/new" - : "/mdn/content/issues/new"; + const repo = useMemo(() => getRepositoryByLocale(locale), [locale]); + + url.pathname = `/mdn/${repo}/issues/new`; sp.set( "template", locale !== "en-US" diff --git a/docs/envvars.md b/docs/envvars.md index 937744aa83e2..128ab565590f 100644 --- a/docs/envvars.md +++ b/docs/envvars.md @@ -35,7 +35,7 @@ Path to the content files, cloned from https://github.com/mdn/content. **Default: `../translated-content/files`** Path to the translated content files, cloned from -https://github.com/mdn/translated-content-de. +https://github.com/mdn/translated-content. ### `CONTRIBUTOR_SPOTLIGHT_ROOT` diff --git a/libs/env/index.d.ts b/libs/env/index.d.ts index fec939592c93..80ade98d71a1 100644 --- a/libs/env/index.d.ts +++ b/libs/env/index.d.ts @@ -15,9 +15,6 @@ export const CONTENT_TRANSLATED_ROOT: string; export const CONTRIBUTOR_SPOTLIGHT_ROOT: string; export const BLOG_ROOT: string; export const CURRICULUM_ROOT: string; -export const REPOSITORY_URLS: { - [path: string]: string; -}; export const ROOTS: string[]; export const MAX_FILE_SIZE: number; export const LIVE_SAMPLES_BASE_URL: string; diff --git a/libs/env/index.js b/libs/env/index.js index b276fbbb3ab1..0df406e5ef0c 100644 --- a/libs/env/index.js +++ b/libs/env/index.js @@ -91,20 +91,12 @@ export const BLOG_ROOT = correctContentPathFromEnv("BLOG_ROOT"); export const CURRICULUM_ROOT = correctPathFromEnv("CURRICULUM_ROOT"); -// This makes it possible to know, give a root folder, what is the name of -// the repository on GitHub. -// E.g. `'https://github.com/' + REPOSITORY_URLS[document.fileInfo.root]` -export const REPOSITORY_URLS = { - [CONTENT_ROOT]: "mdn/content", -}; - // Make a combined array of all truthy roots. This way, you don't // need to constantly worry about CONTENT_TRANSLATED_ROOT potentially being // null. export const ROOTS = [CONTENT_ROOT]; if (CONTENT_TRANSLATED_ROOT) { ROOTS.push(CONTENT_TRANSLATED_ROOT); - REPOSITORY_URLS[CONTENT_TRANSLATED_ROOT] = "mdn/translated-content-de"; } function correctPathFromEnv(envVarName) { diff --git a/libs/locale-utils/index.d.ts b/libs/locale-utils/index.d.ts index aaa8585792d1..c49bc6bff01a 100644 --- a/libs/locale-utils/index.d.ts +++ b/libs/locale-utils/index.d.ts @@ -1,2 +1,3 @@ export function getLocale(request: any, fallback?: string): any; export function isValidLocale(locale: any): locale is string; +export function getRepositoryByLocale(locale: string): string; diff --git a/libs/locale-utils/index.js b/libs/locale-utils/index.js index ec2e53780259..2a97e29757e4 100644 --- a/libs/locale-utils/index.js +++ b/libs/locale-utils/index.js @@ -75,3 +75,14 @@ export function getLocale(request, fallback = DEFAULT_LOCALE) { export function isValidLocale(locale) { return typeof locale === "string" && VALID_LOCALES.has(locale.toLowerCase()); } + +export function getRepositoryByLocale(locale) { + switch (locale.toLowerCase()) { + case "en-us": + return "content"; + case "de": + return "translated-content-de"; + default: + return "translated-content"; + } +} diff --git a/server/translations.ts b/server/translations.ts index 12f2f88edfd5..e5adb00837ab 100644 --- a/server/translations.ts +++ b/server/translations.ts @@ -304,7 +304,7 @@ async function getDocument(filePath) { async function packageEdits(document, parentDocument) { const { - fileInfo: { root: fileRoot, folder: fileFolder }, + fileInfo: { folder: fileFolder }, metadata: { hash: fileHash, modified, l10n }, } = document; const { @@ -312,13 +312,13 @@ async function getDocument(filePath) { metadata: { hash: parentFileHash, parentModified }, } = parentDocument; - const commitURL = getLastCommitURL(fileRoot, fileHash); - const parentCommitURL = getLastCommitURL(parentFileRoot, parentFileHash); + const commitURL = getLastCommitURL(document.locale, fileHash); + const parentCommitURL = getLastCommitURL(document.locale, parentFileHash); let sourceCommitURL; let sourceCommitsBehindCount; if (l10n?.sourceCommit) { - sourceCommitURL = getLastCommitURL(CONTENT_ROOT, l10n.sourceCommit); + sourceCommitURL = getLastCommitURL(DEFAULT_LOCALE, l10n.sourceCommit); sourceCommitsBehindCount = await getCommitBehindFromLatest( fileFolder, parentFilePath.replace(parentFileRoot, "files"), @@ -394,10 +394,7 @@ async function gatherL10NstatsSection({ } function packageEdits(document) { - const commitURL = getLastCommitURL( - document.fileInfo.root, - document.metadata.hash - ); + const commitURL = getLastCommitURL(locale, document.metadata.hash); const modified = document.metadata.modified; return { commitURL, From e70314785dcb58f1c633dc27e065def215e8bdae Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 25 Oct 2024 17:55:07 +0200 Subject: [PATCH 081/100] Revert "chore(content): resolve symlinks in findAll()" This reverts commit dee587bf7b5d99beaebd925ca4458105ffa07d6f. --- content/document.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/content/document.ts b/content/document.ts index fb1f41bdcf67..3ebb11d99120 100644 --- a/content/document.ts +++ b/content/document.ts @@ -474,7 +474,6 @@ export async function findAll({ for (const root of roots) { const api = new fdir() .withFullPaths() - .withSymlinks({ resolvePaths: false }) .withErrors() .filter((filePath) => { // Exit early if it's not a sane kind of file we expect From 755ae3c1494f4e9f4425e9c593e08ea3a4c6d17e Mon Sep 17 00:00:00 2001 From: Claas Augner <495429+caugner@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:24:58 +0200 Subject: [PATCH 082/100] Apply suggestions from code review --- kumascript/macros/AddonSidebar.ejs | 1 - kumascript/macros/Deprecated_Header.ejs | 2 +- kumascript/macros/GamesSidebar.ejs | 4 ++-- kumascript/macros/JsSidebar.ejs | 2 +- kumascript/macros/NonStandardBadge.ejs | 4 ++-- kumascript/macros/PreviousMenuNext.ejs | 2 +- kumascript/macros/ReadOnlyInline.ejs | 2 +- kumascript/macros/SeeCompatTable.ejs | 2 +- kumascript/macros/WebAssemblySidebar.ejs | 10 +++++----- kumascript/macros/js_property_attributes.ejs | 2 +- kumascript/macros/secureContext_header.ejs | 2 +- libs/constants/index.js | 1 - server/translations.ts | 2 +- 13 files changed, 17 insertions(+), 19 deletions(-) diff --git a/kumascript/macros/AddonSidebar.ejs b/kumascript/macros/AddonSidebar.ejs index ceb15a8047fc..aabe19eba4ba 100644 --- a/kumascript/macros/AddonSidebar.ejs +++ b/kumascript/macros/AddonSidebar.ejs @@ -1,5 +1,4 @@ <% - var locale = env.locale; var baseURL = '/' + locale + '/docs/Mozilla/Add-ons/'; diff --git a/kumascript/macros/Deprecated_Header.ejs b/kumascript/macros/Deprecated_Header.ejs index d61c67eb791b..a85daed57068 100644 --- a/kumascript/macros/Deprecated_Header.ejs +++ b/kumascript/macros/Deprecated_Header.ejs @@ -21,7 +21,7 @@ const note = mdn.localString({ }); const desc = mdn.localString({ - "de": "Diese Funktion wird nicht mehr empfohlen. Obwohl einige Browser sie möglicherweise noch unterstützen, wurde sie möglicherweise bereits aus den relevanten Webstandards entfernt, befindet sich im Prozess der Entfernung oder wird nur aus Kompatibilitätsgründen beibehalten. Vermeiden Sie die Verwendung und aktualisieren Sie gegebenenfalls bestehenden Code; siehe die Kompatibilitätstabelle am Ende dieser Seite, um Ihre Entscheidung zu treffen. Beachten Sie, dass diese Funktion jederzeit nicht mehr funktionieren kann.", + "de": "Diese Funktion wird nicht mehr empfohlen. Obwohl einige Browser sie möglicherweise noch unterstützen, wurde sie möglicherweise bereits aus den relevanten Webstandards entfernt, befindet sich im Prozess der Entfernung oder wird nur aus Kompatibilitätsgründen beibehalten. Vermeiden Sie die Verwendung und aktualisieren Sie gegebenenfalls bestehenden Code; siehe die Kompatibilitätstabelle am Ende dieser Seite, um Ihre Entscheidung zu treffen. Beachten Sie, dass diese Funktion jederzeit nicht mehr funktionieren kann.", "en-US": "This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.", "es": "Esta característica ya no se recomienda. Aunque es posible que algunos navegadores aún lo admitan, probablemente ya se ha eliminado de los estándares web relevantes, está en proceso de eliminación o solo se conserva por motivos de compatibilidad. Evite usarlo y actualice el código existente si es posible; consulte la tabla de compatibilidad en la parte inferior de esta página para orientar su decisión. Tenga en cuenta que esta característica puede dejar de funcionar en cualquier momento.", "fr": "Cette fonctionnalité a été supprimée des standards du Web. Bien que quelques navigateurs puissent encore la supporter, elle est en cours d'éradication. Ne l'utilisez ni dans d'anciens projets, ni dans de nouveaux. Les pages et applications Web l'utilisant peuvent cesser de fonctionner à tout moment.", diff --git a/kumascript/macros/GamesSidebar.ejs b/kumascript/macros/GamesSidebar.ejs index 3c7dc6415159..9837f8c01e70 100644 --- a/kumascript/macros/GamesSidebar.ejs +++ b/kumascript/macros/GamesSidebar.ejs @@ -11,13 +11,13 @@ const text = mdn.localStringMap({ "Introduction": "Einführung", "Anatomy": "Anatomie", "APIs_for_game_development": "APIs für die Spieleentwicklung", - "Canvas": "Leinwand", + "Canvas": "Canvas", "CSS": "CSS", "Full_Screen": "Vollbild", "Gamepad": "Gamepad", "IndexedDB": "IndexedDB", "JavaScript": "JavaScript", - "Pointer_Lock": "Mauszeiger Sperre", + "Pointer_Lock": "Pointer Lock", "SVG": "SVG", "Typed_Arrays": "Typisierte Arrays", "Web_Audio": "Web Audio", diff --git a/kumascript/macros/JsSidebar.ejs b/kumascript/macros/JsSidebar.ejs index 0243b6f54843..9b735f06f7c8 100644 --- a/kumascript/macros/JsSidebar.ejs +++ b/kumascript/macros/JsSidebar.ejs @@ -30,7 +30,7 @@ var text = mdn.localStringMap({ 'Guide_Objects': 'Arbeiten mit Objekten', 'Guide_Classes': 'Verwendung von Klassen', 'Guide_Promises': 'Verwendung von Promises', - 'Guide_Typed_arrays': 'JavaScript-getypte Arrays', + 'Guide_Typed_arrays': 'Typisierte Arrays in JavaScript', 'Guide_Iterators_generators': 'Iteratoren und Generatoren', 'Guide_Meta': 'Metaprogrammierung', 'Guide_Modules': 'JavaScript-Module', diff --git a/kumascript/macros/NonStandardBadge.ejs b/kumascript/macros/NonStandardBadge.ejs index e5d95c94cd90..002350a59ddf 100644 --- a/kumascript/macros/NonStandardBadge.ejs +++ b/kumascript/macros/NonStandardBadge.ejs @@ -6,7 +6,7 @@ */ const title = mdn.localString({ - "de": "Kein Standard. Überprüfen Sie die Unterstützung in verschiedenen Browsern, bevor Sie es verwenden.", + "de": "Nicht standardisiert. Überprüfen Sie die Unterstützung in verschiedenen Browsern, bevor Sie es verwenden.", "en-US": "Non-standard. Check cross-browser support before using.", "ko": "비표준. 사용하기전에 다른 브라우저에서도 사용 가능한지 확인 해주세요.", "zh-CN": "非标准。请在使用前检查跨浏览器支持。", @@ -15,7 +15,7 @@ const title = mdn.localString({ const abbreviation = mdn.localString({ "de": "Nicht standardisiert", - "en-US": "Kein Standard", + "en-US": "Non-standard", "ko": "비표준", "zh-CN": "非标准", "zh-TW": "非標準" diff --git a/kumascript/macros/PreviousMenuNext.ejs b/kumascript/macros/PreviousMenuNext.ejs index eda4f3e63aa2..d2a9a9886766 100644 --- a/kumascript/macros/PreviousMenuNext.ejs +++ b/kumascript/macros/PreviousMenuNext.ejs @@ -15,7 +15,7 @@ const nextPage = $1?.replace(/ /g, "_"); const mainMenu = $2?.replace(/ /g, "_"); const previousNextStr = mdn.localString({ - "de" : [" Vorherige ", " Nächste "], + "de" : [" Zurück ", " Weiter "], "en-US": [" Previous ", " Next "], "es" : [" Anterior ", " Siguiente "], "fr" : [" Précédent ", " Suivant "], diff --git a/kumascript/macros/ReadOnlyInline.ejs b/kumascript/macros/ReadOnlyInline.ejs index 5815d8ca2b14..9a0d0b8df0d5 100644 --- a/kumascript/macros/ReadOnlyInline.ejs +++ b/kumascript/macros/ReadOnlyInline.ejs @@ -1,6 +1,6 @@ <% var str = mdn.localString({ - "de": "Nur lesen ", + "de": "Nur lesbar ", "en-US": "Read only ", "fr": "Lecture seule ", "ja": "読取専用 ", diff --git a/kumascript/macros/SeeCompatTable.ejs b/kumascript/macros/SeeCompatTable.ejs index 3ef886726af5..e1f08ed247e9 100644 --- a/kumascript/macros/SeeCompatTable.ejs +++ b/kumascript/macros/SeeCompatTable.ejs @@ -1,7 +1,7 @@ <% const str = mdn.localString({ - "de": "Dies ist eine experimentelle Technologie
      Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese in der Produktion verwenden.", + "de": "Dies ist eine experimentelle Technologie
      Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese in der Produktion verwenden.", "es": "Esta es una tecnología experimental
      Comprueba la Tabla de compabilidad de navegadores cuidadosamente antes de usarla en producción.", "fr": "Cette fonction est expérimentale
      Puisque cette fonction est toujours en développement dans certains navigateurs, veuillez consulter le tableau de compatibilité pour les préfixes à utiliser selon les navigateurs.
      Il convient de noter qu'une fonctionnalité expérimentale peut voir sa syntaxe ou son comportement modifié dans le futur en fonction des évolutions de la spécification.", "ja": "これは実験的な機能です。
      本番で使用する前にブラウザー互換性一覧表をチェックしてください。", diff --git a/kumascript/macros/WebAssemblySidebar.ejs b/kumascript/macros/WebAssemblySidebar.ejs index ed973960519c..6e53a15d12ce 100644 --- a/kumascript/macros/WebAssemblySidebar.ejs +++ b/kumascript/macros/WebAssemblySidebar.ejs @@ -7,12 +7,12 @@ var text = mdn.localStringMap({ 'WebAssembly_home_page' : 'WebAssembly-Startseite', 'Tutorials' : 'Anleitungen', 'WebAssembly_concepts' : 'WebAssembly-Konzepte', - 'Compiling_to_wasm' : 'Kompilierung von C/C++ zu WebAssembly', - 'Compiling_rust_to_wasm' : 'Kompilierung von Rust zu WebAssembly', - 'JavaScript_API' : 'Verwendung der WebAssembly JavaScript API', - 'Text_format' : 'Verständnis des WebAssembly-Textformats', + 'Compiling_to_wasm' : 'C/C++ zu WebAssembly kompilieren', + 'Compiling_rust_to_wasm' : 'Rust zu WebAssembly kompilieren', + 'JavaScript_API' : 'WebAssembly JavaScript API verwenden', + 'Text_format' : 'WebAssembly-Textformats verstehen', 'Text_format_to_wasm' : 'Umwandlung des WebAssembly-Textformats in wasm', - 'Loading_and_running' : 'Laden und Ausführen von WebAssembly-Code', + 'Loading_and_running' : 'WebAssembly-Code laden und ausführen', 'Exported_functions' : 'Exportierte WebAssembly-Funktionen', 'JavaScript_interface' : 'JavaScript-Schnittstelle' }, diff --git a/kumascript/macros/js_property_attributes.ejs b/kumascript/macros/js_property_attributes.ejs index de5fd484e063..e01b6087b70b 100644 --- a/kumascript/macros/js_property_attributes.ejs +++ b/kumascript/macros/js_property_attributes.ejs @@ -15,7 +15,7 @@ const isConfigurable = $2 == 1; const text = mdn.localStringMap({ "de": { - header: `Eigenschaftsattribute von ${env.title}`, + header: `Eigenschaften der ${env.title}-Property`, writableName: "Schreibbar", enumerableName: "Aufzählbar", configurableName: "Konfigurierbar", diff --git a/kumascript/macros/secureContext_header.ejs b/kumascript/macros/secureContext_header.ejs index 813759272057..95095e5ce388 100644 --- a/kumascript/macros/secureContext_header.ejs +++ b/kumascript/macros/secureContext_header.ejs @@ -15,7 +15,7 @@ var str_title = mdn.localString({ }); var str_desc = mdn.localString({ - "de" : "Diese Funktion ist nur in sicheren Kontexten (HTTPS) in einigen oder allen unterstützenden Browsern verfügbar.", + "de" : "Diese Funktion ist nur in sicheren Kontexten (HTTPS) in einigen oder allen unterstützenden Browsern verfügbar.", "en-US": "This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.", "es" : "Esta función está disponible solo en contextos seguros (HTTPS), en algunos o todos los navegadores que lo soportan.", "fr" : "Cette fonctionnalité est uniquement disponible dans des contextes sécurisés (HTTPS), pour certains navigateurs qui la prennent en charge.", diff --git a/libs/constants/index.js b/libs/constants/index.js index 150944ca22d1..b979102cebf1 100644 --- a/libs/constants/index.js +++ b/libs/constants/index.js @@ -10,7 +10,6 @@ export const RETIRED_LOCALES = new Map( "bg", "bn", "ca", - //"de", "el", "fa", "fi", diff --git a/server/translations.ts b/server/translations.ts index e5adb00837ab..c5b3d73dbc91 100644 --- a/server/translations.ts +++ b/server/translations.ts @@ -394,7 +394,7 @@ async function gatherL10NstatsSection({ } function packageEdits(document) { - const commitURL = getLastCommitURL(locale, document.metadata.hash); + const commitURL = getLastCommitURL(document.locale, document.metadata.hash); const modified = document.metadata.modified; return { commitURL, From c23e84cd26830bd3f1569c1b003b532fe9286c8c Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 28 Oct 2024 11:53:48 +0100 Subject: [PATCH 083/100] chore(de): abbreviate disclaimer --- .../src/document/molecules/localized-content-note/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index fcc0bdbef811..2b84210bf098 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -11,10 +11,8 @@ export function LocalizedContentNote({ de: { linkText: ( <> - Diese Seite wurde mit GPT-4o aus dem Englischen übersetzt. -
      - Disclaimer: Dies ist eine Vorschau. Übersetzungen - können unvollständig oder fehlerhaft sein. + Experimentell: Dieser Inhalt wurde mit GPT-4o aus dem + Englischen übersetzt, und kann fehlerhaft sein. ), url: "https://github.com/orgs/mdn/discussions/741", From 74fe8c8bbd77853be767494eafdbf0f9b59a140d Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 28 Oct 2024 14:51:16 +0100 Subject: [PATCH 084/100] enhance(document-survey): support dynamic source --- .../ui/molecules/document-survey/index.tsx | 22 ++++++++++++++++--- .../ui/molecules/document-survey/surveys.ts | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/client/src/ui/molecules/document-survey/index.tsx b/client/src/ui/molecules/document-survey/index.tsx index 8f1ff6fd844f..3e97f12a5172 100644 --- a/client/src/ui/molecules/document-survey/index.tsx +++ b/client/src/ui/molecules/document-survey/index.tsx @@ -47,15 +47,31 @@ export function DocumentSurvey({ doc }: { doc: Doc }) { [doc, isServer, location.hash, force] ); - return survey ? : <>; + return survey ? ( + + ) : ( + <> + ); } -function SurveyDisplay({ survey, force }: { survey: Survey; force: boolean }) { +function SurveyDisplay({ + doc, + survey, + force, +}: { + doc: Doc; + survey: Survey; + force: boolean; +}) { const gleanClick = useGleanClick(); const details = React.useRef(null); const [originalState] = React.useState(() => getSurveyState(survey.bucket)); const [state, setState] = React.useState(() => originalState); + const source = React.useMemo( + () => (typeof survey.src === "function" ? survey.src(doc) : survey.src), + [survey, doc] + ); React.useEffect(() => { writeSurveyState(survey.bucket, state); @@ -166,7 +182,7 @@ function SurveyDisplay({ survey, force }: { survey: Survey; force: boolean }) { {state.opened_at && ( diff --git a/client/src/ui/molecules/document-survey/surveys.ts b/client/src/ui/molecules/document-survey/surveys.ts index a6ee88d4edd1..c46849f4562c 100644 --- a/client/src/ui/molecules/document-survey/surveys.ts +++ b/client/src/ui/molecules/document-survey/surveys.ts @@ -12,7 +12,7 @@ export interface Survey { // Proportion slice of users to target. rateFrom: number; rateTill: number; - src: string; + src: string | ((doc: Doc) => string); teaser: string; question: string; footnote?: string; From f70117ecccb390c4d1ce70fa4be170abb245bf29 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Mon, 28 Oct 2024 14:53:29 +0100 Subject: [PATCH 085/100] chore(document-survey): add German evaluation survey --- .../ui/molecules/document-survey/surveys.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/client/src/ui/molecules/document-survey/surveys.ts b/client/src/ui/molecules/document-survey/surveys.ts index c46849f4562c..cab978fe0466 100644 --- a/client/src/ui/molecules/document-survey/surveys.ts +++ b/client/src/ui/molecules/document-survey/surveys.ts @@ -24,6 +24,7 @@ enum SurveyBucket { CONTENT_DISCOVERY_2023 = "CONTENT_DISCOVERY_2023", CSS_CASCADE_2022 = "CSS_CASCADE_2022", DE_LOCALE_2024 = "DE_LOCALE_2024", + DE_LOCALE_2024_EVAL = "DE_LOCALE_2024_EVAL", FIREFOX_WEB_COMPAT_2023 = "FIREFOX_WEB_COMPAT_2023", INTEROP_2023 = "INTEROP_2023", WEB_COMPONENTS_2023 = "WEB_COMPONENTS_2023", @@ -40,6 +41,7 @@ enum SurveyKey { CSS_CASCADE_2022_A = "CSS_CASCADE_2022_A", CSS_CASCADE_2022_B = "CSS_CASCADE_2022_B", DE_LOCALE_2024 = "DE_LOCALE_2024", + DE_LOCALE_2024_EVAL = "DE_LOCALE_2024_EVAL", FIREFOX_WEB_COMPAT_2023 = "FIREFOX_WEB_COMPAT_2023", INTEROP_2023_CSS_HTML = "INTEROP_2023_CSS_HTML", INTEROP_2023_API_JS = "INTEROP_2023_API_JS", @@ -67,4 +69,23 @@ export const SURVEYS: Survey[] = [ ...survey_duration(SurveyBucket.WEB_APP_AUGUST_2024), ...survey_rates(SurveyKey.WEB_APP_AUGUST_2024), }, + { + key: SurveyKey.DE_LOCALE_2024_EVAL, + bucket: SurveyBucket.DE_LOCALE_2024_EVAL, + show: (doc: Doc) => /de(\/|$)/i.test(doc.mdn_url), + src: (doc: Doc) => { + const url = new URL( + "https://survey.alchemer.com/s3/8073795/Feedback-zur-deutschen-Version-von-MDN" + ); + url.searchParams.set("referrer", doc.mdn_url); + return url.toString(); + }, + teaser: + "Wir arbeiten daran, die deutsche Übersetzung von MDN zu verbessern.", + question: "Hätten Sie 2 Minuten, um uns 4 Fragen zu beantworten?", + rateFrom: 0, + rateTill: 1, + start: 0, + end: Infinity, + }, ]; From 8969dfa4090f1c718dd1ecb1672f55a97fd60e06 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 30 Oct 2024 15:12:28 +0100 Subject: [PATCH 086/100] Revert "Merge branch 'macro-render-cache' into test-de" This reverts commit 5f9ef7b43e5ec22964e723af185e1bdf8a66aa00, reversing changes made to 7dca1ab6a2b555d760f138a8ac1f0aa703bb03f4. --- kumascript/macros/AddonSidebar.ejs | 1 + kumascript/macros/CSSRef.ejs | 2 -- kumascript/macros/GlossarySidebar.ejs | 2 -- kumascript/macros/JsSidebar.ejs | 2 -- kumascript/macros/LearnSidebar.ejs | 1 - kumascript/macros/SubpagesWithSummaries.ejs | 2 +- kumascript/src/api/mdn.ts | 5 --- kumascript/src/templates.ts | 34 ++++----------------- 8 files changed, 8 insertions(+), 41 deletions(-) diff --git a/kumascript/macros/AddonSidebar.ejs b/kumascript/macros/AddonSidebar.ejs index aabe19eba4ba..2c647133b692 100644 --- a/kumascript/macros/AddonSidebar.ejs +++ b/kumascript/macros/AddonSidebar.ejs @@ -1,5 +1,6 @@ <% var locale = env.locale; +var slug = env.slug; var baseURL = '/' + locale + '/docs/Mozilla/Add-ons/'; var text = mdn.localStringMap({ diff --git a/kumascript/macros/CSSRef.ejs b/kumascript/macros/CSSRef.ejs index 372b884d8efe..eef7a89ab8ce 100644 --- a/kumascript/macros/CSSRef.ejs +++ b/kumascript/macros/CSSRef.ejs @@ -1,7 +1,5 @@ <% -mdn.cache(); - const text = mdn.localStringMap({ 'de': { 'Tutorials': 'Tutorials', diff --git a/kumascript/macros/GlossarySidebar.ejs b/kumascript/macros/GlossarySidebar.ejs index 2db17c50d99f..e42808ac8d1d 100644 --- a/kumascript/macros/GlossarySidebar.ejs +++ b/kumascript/macros/GlossarySidebar.ejs @@ -1,6 +1,4 @@ <% -mdn.cache(); - async function renderRootItem(slug) { const [link, title] = await getPageLinkAndTitle(slug); return `

    • ${title}
    • ` diff --git a/kumascript/macros/JsSidebar.ejs b/kumascript/macros/JsSidebar.ejs index 9b735f06f7c8..ab3fb3ac6f2e 100644 --- a/kumascript/macros/JsSidebar.ejs +++ b/kumascript/macros/JsSidebar.ejs @@ -1,6 +1,4 @@ <% -mdn.cache(); - var currentSection = $0; var locale = env.locale; diff --git a/kumascript/macros/LearnSidebar.ejs b/kumascript/macros/LearnSidebar.ejs index ca7ad567d824..0a96e872693b 100644 --- a/kumascript/macros/LearnSidebar.ejs +++ b/kumascript/macros/LearnSidebar.ejs @@ -1,5 +1,4 @@ <% -mdn.cache(); const l10nStrings = mdn.localStringMap({ 'de': { diff --git a/kumascript/macros/SubpagesWithSummaries.ejs b/kumascript/macros/SubpagesWithSummaries.ejs index edebb70727b6..38546f588c6c 100644 --- a/kumascript/macros/SubpagesWithSummaries.ejs +++ b/kumascript/macros/SubpagesWithSummaries.ejs @@ -6,6 +6,7 @@ // // $0 A list of pages to output instead of the subpages of the current page; // OPTIONAL. + function pageSorter(a, b) { return a.title.localeCompare(b.title); } @@ -14,7 +15,6 @@ var termList; var html = ""; if ($0 && ($0 != undefined)) { - mdn.cache(); termList = JSON.parse($0); } else { termList = await page.subpagesExpand(); diff --git a/kumascript/src/api/mdn.ts b/kumascript/src/api/mdn.ts index e14914fcbb3f..f750cef3afe7 100644 --- a/kumascript/src/api/mdn.ts +++ b/kumascript/src/api/mdn.ts @@ -1,7 +1,6 @@ import got from "got"; import { KumaThis } from "../environment.js"; import * as util from "./util.js"; -import { toggleRenderCache } from "../templates.js"; // Module level caching for repeat calls to fetchWebExtExamples(). let webExtExamples: any = null; @@ -122,10 +121,6 @@ const mdn = { */ escapeQuotes: util.escapeQuotes, - cache(this: KumaThis) { - toggleRenderCache(true); - }, - /** * Throw a deprecation error. */ diff --git a/kumascript/src/templates.ts b/kumascript/src/templates.ts index f46b246fdf8a..386385cf6ca5 100644 --- a/kumascript/src/templates.ts +++ b/kumascript/src/templates.ts @@ -27,20 +27,11 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ejs from "ejs"; -import { LRUCache } from "lru-cache"; const DEFAULT_MACROS_DIRECTORY = path.normalize( fileURLToPath(new URL("../macros", import.meta.url)) ); -const RENDER_CACHE = new LRUCache({ max: 100 }); - -export let isRenderCacheEnabled = false; - -export function toggleRenderCache(value: boolean) { - isRenderCacheEnabled = value; -} - export default class Templates { private macroDirectory: string; private macroNameToPath: Map; @@ -111,27 +102,14 @@ export default class Templates { throw new ReferenceError(`Unknown macro ${name}`); } try { - const cacheKey = `${args?.env?.locale}:${name}:${JSON.stringify(args.$$)}`; - - let output = RENDER_CACHE.get(cacheKey); - - if (!output) { - output = await ejs.renderFile(path, args, { - async: true, - cache: args.cache || process.env.NODE_ENV === "production", - }); - output = output.trim(); - } - - if (isRenderCacheEnabled) { - RENDER_CACHE.set(cacheKey, output); - toggleRenderCache(false); - } - - return output; + const rendered = await ejs.renderFile(path, args, { + async: true, + cache: args.cache || process.env.NODE_ENV === "production", + }); + return rendered.trim(); } catch (error) { console.error( - `The ${name} macro on ${args?.env?.url} failed to render.`, + `The ${name} macro on ${args.env.url} failed to render.`, error ); throw error; From 5efa3ba745fc587c9f8014f1af5f23b2813a3a58 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 30 Oct 2024 15:14:57 +0100 Subject: [PATCH 087/100] fixup! Apply suggestions from code review --- kumascript/macros/SeeCompatTable.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kumascript/macros/SeeCompatTable.ejs b/kumascript/macros/SeeCompatTable.ejs index e1f08ed247e9..26c8bdf32741 100644 --- a/kumascript/macros/SeeCompatTable.ejs +++ b/kumascript/macros/SeeCompatTable.ejs @@ -1,7 +1,7 @@ <% const str = mdn.localString({ - "de": "Dies ist eine experimentelle Technologie
      Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese in der Produktion verwenden.", + "de": "Dies ist eine experimentelle Technologie
      Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese in der Produktion verwenden.", "es": "Esta es una tecnología experimental
      Comprueba la Tabla de compabilidad de navegadores cuidadosamente antes de usarla en producción.", "fr": "Cette fonction est expérimentale
      Puisque cette fonction est toujours en développement dans certains navigateurs, veuillez consulter le tableau de compatibilité pour les préfixes à utiliser selon les navigateurs.
      Il convient de noter qu'une fonctionnalité expérimentale peut voir sa syntaxe ou son comportement modifié dans le futur en fonction des évolutions de la spécification.", "ja": "これは実験的な機能です。
      本番で使用する前にブラウザー互換性一覧表をチェックしてください。", From c8f23c4fad1c87e7358cc08c4bc4d5bee9d3ddf4 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 30 Oct 2024 15:31:53 +0100 Subject: [PATCH 088/100] Apply suggestions --- kumascript/macros/AccessibilitySidebar.ejs | 4 ++-- kumascript/macros/AddonSidebarMain.ejs | 2 +- kumascript/macros/CSSRef.ejs | 26 +++++++++++----------- kumascript/macros/Deprecated_Header.ejs | 2 +- kumascript/macros/HTMLSidebar.ejs | 8 +++---- kumascript/macros/JsSidebar.ejs | 2 +- kumascript/macros/MDNSidebar.ejs | 2 +- kumascript/macros/SVGRef.ejs | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/kumascript/macros/AccessibilitySidebar.ejs b/kumascript/macros/AccessibilitySidebar.ejs index 817a3e299318..d56124326a5a 100644 --- a/kumascript/macros/AccessibilitySidebar.ejs +++ b/kumascript/macros/AccessibilitySidebar.ejs @@ -9,8 +9,8 @@ const l10nStrings = mdn.localStringMap({ "Guides" : "Anleitungen", "Learn" : "Barrierefreiheit lernen", "ARIA" : "ARIA", - "ARIA_guides" : "ARIA Anleitungen", - "ARIA states and properties": "ARIA-Zustände und Eigenschaften", + "ARIA_guides" : "ARIA-Anleitungen", + "ARIA states and properties": "ARIA-Zustände und -Eigenschaften", "ARIA roles": "ARIA-Rollen", "WCAG": "WCAG", }, diff --git a/kumascript/macros/AddonSidebarMain.ejs b/kumascript/macros/AddonSidebarMain.ejs index f93bdba56fc5..c4938f49bd72 100644 --- a/kumascript/macros/AddonSidebarMain.ejs +++ b/kumascript/macros/AddonSidebarMain.ejs @@ -10,7 +10,7 @@ var text = mdn.localStringMap({ 'Channels': 'Kanäle', 'Blog': 'Add-ons Blog', 'Forums': 'Add-ons Foren', - '#Contact_us': 'Kontaktieren Sie uns' + '#Contact_us': 'Kontakt' }, 'en-US': { 'WebExtensions': 'Browser extensions', diff --git a/kumascript/macros/CSSRef.ejs b/kumascript/macros/CSSRef.ejs index eef7a89ab8ce..e185d1af7b6c 100644 --- a/kumascript/macros/CSSRef.ejs +++ b/kumascript/macros/CSSRef.ejs @@ -5,14 +5,14 @@ const text = mdn.localStringMap({ 'Tutorials': 'Tutorials', 'CSS_basics': 'CSS Grundlagen', 'CSS_first_steps': 'CSS erste Schritte', - 'CSS_first_steps_overview': 'CSS erste Schritte Übersicht', + 'CSS_first_steps_overview': 'CSS erste Schritte (Übersicht)', 'What_is_CSS': 'Was ist CSS?', 'Getting_started_with_CSS': 'Erste Schritte mit CSS', 'How_CSS_is_structured': 'Wie CSS aufgebaut ist', 'How_CSS_works': 'Wie CSS funktioniert', - 'Assessment_Styling_a_biography_page': 'Bewertung: Gestaltung einer Biografie-Seite', + 'Assessment_Styling_a_biography_page': 'Aufgabe: Gestaltung einer Biografie-Seite', 'CSS_building_blocks': 'CSS Bausteine', - 'CSS_building_blocks_overview': 'CSS Bausteine Übersicht', + 'CSS_building_blocks_overview': 'CSS Bausteine (Übersicht)', 'CSS_selectors': 'CSS Selektoren', 'Type_Class_and_ID_Selectors': 'Typ-, Klassen- und ID-Selektoren', 'Attribute_selectors': 'Attribut-Selektoren', @@ -24,24 +24,24 @@ const text = mdn.localStringMap({ 'Backgrounds_and_borders': 'Hintergründe und Rahmen', 'Handling_different_text_directions': 'Umgang mit verschiedenen Textrichtungen', 'Overflowing_content': 'Überlaufender Inhalt', - 'CSS_values_and_units': 'CSS Werte und Einheiten', + 'CSS_values_and_units': 'CSS-Werte und -Einheiten', 'Sizing_items_in_CSS': 'Größenanpassung von Elementen in CSS', 'Images_media_and_form_elements': 'Bilder, Medien und Formularelemente', 'Styling_tables': 'Tabellen gestalten', 'Debugging_CSS': 'CSS debuggen', 'Organizing_your_CSS': 'CSS organisieren', - 'Assessment_Fundamental_CSS_comprehension': 'Bewertung: Grundlegendes CSS-Verständnis', - 'Assessment_Creating_fancy_letterheaded_paper': 'Bewertung: Erstellung von schickem Briefpapier', - 'Assessment_A_cool_looking_box': 'Bewertung: Eine cool aussehende Box', + 'Assessment_Fundamental_CSS_comprehension': 'Aufgabe: Grundlegendes CSS-Verständnis', + 'Assessment_Creating_fancy_letterheaded_paper': 'Aufgabe: Erstellung von schickem Briefpapier', + 'Assessment_A_cool_looking_box': 'Aufgabe: Eine cool aussehende Box', 'Styling_text': 'Textgestaltung', - 'Styling_text_overview': 'Textgestaltung Übersicht', + 'Styling_text_overview': 'Textgestaltung (Übersicht)', 'Fundamental_text_and_font_styling': 'Grundlegende Text- und Schriftgestaltung', 'Styling_lists': 'Listen gestalten', 'Styling_links': 'Links gestalten', 'Web_fonts': 'Web-Schriftarten', - 'Assessment_Typesetting_a_community_school_homepage': 'Bewertung: Satz einer Community-Schulhomepage', + 'Assessment_Typesetting_a_community_school_homepage': 'Aufgabe: Satz einer Community-Schulhomepage', 'CSS_layout': 'CSS-Layout', - 'CSS_layout_overview': 'CSS-Layout Übersicht', + 'CSS_layout_overview': 'CSS-Layout (Übersicht)', 'Introduction_to_CSS_layout' : 'Einführung in CSS-Layout', 'Normal_Flow' : 'Normaler Fluss', 'Flexbox': 'Flexbox', @@ -53,7 +53,7 @@ const text = mdn.localStringMap({ 'Beginners_guide_to_media_queries': 'Anfängerkurs zu Media-Queries', 'Legacy_layout_methods': 'Veraltete Layout-Methoden', 'Supporting_older_browsers': 'Unterstützung älterer Browser', - 'Assessment_Fundamental_Layout_Comprehension' : 'Bewertung: Grundlegendes Layout-Verständnis', + 'Assessment_Fundamental_Layout_Comprehension' : 'Aufgabe: Grundlegendes Layout-Verständnis', 'Reference': 'Referenz', 'Guides': 'Leitfäden', 'Animations': 'Animationen', @@ -81,8 +81,8 @@ const text = mdn.localStringMap({ 'Content_breaks_in_Multicol': 'Inhaltsumbrüche in Multicol', 'Conditional_rules': 'Bedingte Regeln', 'Using_feature_queries': 'Feature-Abfragen verwenden', - 'Containment' : 'Eindämmung', - 'Using_CSS_containment' : 'CSS-Eindämmung verwenden', + 'Containment' : 'Containment', + 'Using_CSS_containment' : 'CSS-Containment verwenden', 'Container_queries' : 'Container-Abfragen', 'Container_size_and_style_queries' : 'Containergrößen- und Stilabfragen', 'CSSOM_view': 'CSSOM-Ansicht', diff --git a/kumascript/macros/Deprecated_Header.ejs b/kumascript/macros/Deprecated_Header.ejs index a85daed57068..936f34764b18 100644 --- a/kumascript/macros/Deprecated_Header.ejs +++ b/kumascript/macros/Deprecated_Header.ejs @@ -21,7 +21,7 @@ const note = mdn.localString({ }); const desc = mdn.localString({ - "de": "Diese Funktion wird nicht mehr empfohlen. Obwohl einige Browser sie möglicherweise noch unterstützen, wurde sie möglicherweise bereits aus den relevanten Webstandards entfernt, befindet sich im Prozess der Entfernung oder wird nur aus Kompatibilitätsgründen beibehalten. Vermeiden Sie die Verwendung und aktualisieren Sie gegebenenfalls bestehenden Code; siehe die Kompatibilitätstabelle am Ende dieser Seite, um Ihre Entscheidung zu treffen. Beachten Sie, dass diese Funktion jederzeit nicht mehr funktionieren kann.", + "de": "Diese Funktion wird nicht mehr empfohlen. Obwohl einige Browser sie möglicherweise noch unterstützen, kann sie bereits aus den relevanten Webstandards entfernt worden sein, befindet sich im Prozess der Entfernung oder wird nur aus Kompatibilitätsgründen beibehalten. Vermeiden Sie die Verwendung und aktualisieren Sie gegebenenfalls bestehenden Code; siehe die Kompatibilitätstabelle am Ende dieser Seite, um Ihre Entscheidung zu treffen. Beachten Sie, dass diese Funktion jederzeit nicht mehr funktionieren kann.", "en-US": "This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.", "es": "Esta característica ya no se recomienda. Aunque es posible que algunos navegadores aún lo admitan, probablemente ya se ha eliminado de los estándares web relevantes, está en proceso de eliminación o solo se conserva por motivos de compatibilidad. Evite usarlo y actualice el código existente si es posible; consulte la tabla de compatibilidad en la parte inferior de esta página para orientar su decisión. Tenga en cuenta que esta característica puede dejar de funcionar en cualquier momento.", "fr": "Cette fonctionnalité a été supprimée des standards du Web. Bien que quelques navigateurs puissent encore la supporter, elle est en cours d'éradication. Ne l'utilisez ni dans d'anciens projets, ni dans de nouveaux. Les pages et applications Web l'utilisant peuvent cesser de fonctionner à tout moment.", diff --git a/kumascript/macros/HTMLSidebar.ejs b/kumascript/macros/HTMLSidebar.ejs index 4b369cac3261..bc92cb7bdf40 100644 --- a/kumascript/macros/HTMLSidebar.ejs +++ b/kumascript/macros/HTMLSidebar.ejs @@ -24,8 +24,8 @@ var text = mdn.localStringMap({ 'Advanced_text_formatting': 'Erweiterte Textformatierung', 'Document_and_website_structure': 'Dokument- und Webseitenstruktur', 'Debugging_HTML': 'Debugging von HTML', - 'Assessment_Marking_up_a_letter': 'Bewertung: Briefauszeichnung', - 'Assessment_Structuring_a_page_of_content': 'Bewertung: Strukturierung einer Inhaltsseite', + 'Assessment_Marking_up_a_letter': 'Aufgabe: Briefauszeichnung', + 'Assessment_Structuring_a_page_of_content': 'Aufgabe: Strukturierung einer Inhaltsseite', 'Multimedia_and_embedding': 'Multimedia und Einbettung', 'Multimedia_and_embedding_overview': 'Übersicht zu Multimedia und Einbettung', 'Images_in_HTML': 'Bilder in HTML', @@ -33,12 +33,12 @@ var text = mdn.localStringMap({ 'From_object_to_iframe_other_embedding_technologies': 'Von Object zu Iframe - andere Einbettungstechnologien', 'Adding_vector_graphics_to_the_web': 'Hinzufügen von Vektorgrafiken zum Web', 'Responsive_images': 'Responsive Bilder', - 'Assessment_Mozilla_splash_page': 'Bewertung: Mozilla-Startseite', + 'Assessment_Mozilla_splash_page': 'Aufgabe: Mozilla-Startseite', 'HTML_tables' : 'HTML-Tabellen', 'HTML_tables_overview' : 'Übersicht zu HTML-Tabellen', 'HTML_table_basics' : 'Grundlagen der HTML-Tabellen', 'HTML_table_advanced_features_and_accessibility' : 'Erweiterte Funktionen und Zugänglichkeit von HTML-Tabellen', - 'Assessment_Structuring_planet_data' : 'Bewertung: Strukturierung von Planetendaten', + 'Assessment_Structuring_planet_data' : 'Aufgabe: Strukturierung von Planetendaten', 'Reference': 'Referenzen', 'HTML_Elements': 'HTML-Elemente', 'Global_attributes': 'Globale Attribute', diff --git a/kumascript/macros/JsSidebar.ejs b/kumascript/macros/JsSidebar.ejs index ab3fb3ac6f2e..17b652f17359 100644 --- a/kumascript/macros/JsSidebar.ejs +++ b/kumascript/macros/JsSidebar.ejs @@ -11,7 +11,7 @@ function state(section) { var text = mdn.localStringMap({ 'de': { - 'Overview': 'JavaScript-Technologieübersicht', + 'Overview': 'JavaScript-Technologie (Übersicht)', 'Tutorials': 'Tutorials', 'Guide': 'JavaScript-Handbuch', 'Guide_Introduction': 'Einführung', diff --git a/kumascript/macros/MDNSidebar.ejs b/kumascript/macros/MDNSidebar.ejs index ee5e35e7f4bd..d370be2ccbe9 100644 --- a/kumascript/macros/MDNSidebar.ejs +++ b/kumascript/macros/MDNSidebar.ejs @@ -3,7 +3,7 @@ const l10nStrings = mdn.localStringMap({ "de": { "history": "Geschichte", - "advisory_board": "Beirat", + "advisory_board": "Product Advisory Board", "community_guidelines": "Gemeinschaftsrichtlinien", "contributing_to_mdn_web_docs": "Beiträge zu MDN Web Docs", "writing_guide": "Schreibanleitung", diff --git a/kumascript/macros/SVGRef.ejs b/kumascript/macros/SVGRef.ejs index eb6356df88a6..61461c4c14f5 100644 --- a/kumascript/macros/SVGRef.ejs +++ b/kumascript/macros/SVGRef.ejs @@ -8,7 +8,7 @@ const text = mdn.localStringMap({ 'Elements': 'Elemente', 'Attributes': 'Attribute', 'Guides': 'Leitfäden', - 'Introducing SVG from scratch': 'SVG von Grund auf neu einführen' + 'Introducing SVG from scratch': 'Einführung in SVG von Anfang an' }, 'en-US': { 'Tutorials': 'Tutorials', From d729bff5edbfd7f5979a4b9c19adb2a4a128a4a0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 11:54:36 +0100 Subject: [PATCH 089/100] chore(test-de): build all locales together --- .github/workflows/test-de-build.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 21881f02e789..3108977f6d3c 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -82,14 +82,26 @@ jobs: repository: mdn/curriculum path: mdn/curriculum + - name: Checkout (translated-content) + uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} + with: + repository: mdn/translated-content + path: mdn/translated-content + # See matching warning for mdn/content checkout step + fetch-depth: 0 + - name: Checkout (translated-content-de) uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} with: repository: mdn/translated-content-de path: mdn/translated-content-de - # See matching warning for mdn/content checkout step - fetch-depth: 0 + + - name: Move de into translated-content + run: | + mv mdn/translated-content-de/files/de mdn/translated-content/files/ + rm -rf mdn/translated-content-de - name: Checkout (contributor-spotlight) uses: actions/checkout@v4 @@ -126,7 +138,7 @@ jobs: # Remember, the mdn/content repo got cloned into `pwd` into a # sub-folder called "mdn/content" CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files - CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content-de/files + CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files CONTRIBUTOR_SPOTLIGHT_ROOT: ${{ github.workspace }}/mdn/mdn-contributor-spotlight/contributors BLOG_ROOT: ${{ github.workspace }}/mdn/mdn-studio/content/posts CURRICULUM_ROOT: ${{ github.workspace }}/mdn/curriculum @@ -192,7 +204,7 @@ jobs: # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. - for locale in en-us de; do + for locale in en-us de es fr ja ko pt-br ru zh-cn zh-tw; do yarn build:docs --locale $locale 2>&1 | sed "s/^/[$locale] /" & pids+=($!) done @@ -255,7 +267,7 @@ jobs: working-directory: cloud-function env: CONTENT_ROOT: ${{ github.workspace }}/mdn/content/files - CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content-de/files + CONTENT_TRANSLATED_ROOT: ${{ github.workspace }}/mdn/translated-content/files run: | npm ci npm run build-redirects From e58c8a97d8da4d79747762c288e3ccd4a10036c4 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 12:11:17 +0100 Subject: [PATCH 090/100] fixup! chore(test-de): build all locales together --- .github/workflows/test-de-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index 1bf7777049ff..12c39eaa597d 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -200,7 +200,7 @@ jobs: yarn build:sw yarn build:prepare - yarn tool sync-translated-content de + yarn tool sync-translated-content es fr ja ko pt-br ru zh-cn zh-tw # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. From 7ab04239c7a06710ccc8a69373f2fbf60c6ff76e Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 16:01:16 +0100 Subject: [PATCH 091/100] fixup! fix(de): use translated-content-de only for de --- build/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/index.ts b/build/index.ts index ed33f06ad16a..dba4fd6ad2f0 100644 --- a/build/index.ts +++ b/build/index.ts @@ -136,7 +136,7 @@ function injectNotecardOnWarnings($: cheerio.CheerioAPI) { function getGitHubURL(root: string, folder: string, filename: string) { const [locale] = folder.split("/", 2); const repo = getRepositoryByLocale(locale); - const baseURL = `https://github.com/mdn/${repo}/`; + const baseURL = `https://github.com/mdn/${repo}`; return `${baseURL}/blob/${getCurrentGitBranch( root From e71a4438954c380ada56545a75cdf5e300b68767 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 16:04:49 +0100 Subject: [PATCH 092/100] fixup! chore(document-survey): add German evaluation survey --- client/src/ui/molecules/document-survey/surveys.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/molecules/document-survey/surveys.ts b/client/src/ui/molecules/document-survey/surveys.ts index cab978fe0466..06c437d5d837 100644 --- a/client/src/ui/molecules/document-survey/surveys.ts +++ b/client/src/ui/molecules/document-survey/surveys.ts @@ -82,7 +82,7 @@ export const SURVEYS: Survey[] = [ }, teaser: "Wir arbeiten daran, die deutsche Übersetzung von MDN zu verbessern.", - question: "Hätten Sie 2 Minuten, um uns 4 Fragen zu beantworten?", + question: "Hätten Sie 2 Minuten, um uns ein paar Fragen zu beantworten?", rateFrom: 0, rateTill: 1, start: 0, From 5f37f0ca312790ca30625761c26da6b10e5e6475 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 16:30:55 +0100 Subject: [PATCH 093/100] fix(de): use en-US git history for de --- content/document.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/content/document.ts b/content/document.ts index 3ebb11d99120..b38001fc8251 100644 --- a/content/document.ts +++ b/content/document.ts @@ -13,6 +13,7 @@ import { } from "../libs/env/index.js"; import { ACTIVE_LOCALES, + DEFAULT_LOCALE, HTML_FILENAME, MARKDOWN_FILENAME, VALID_LOCALES, @@ -302,9 +303,27 @@ export const read = memoize( // The last-modified is always coming from the git logs. Independent of // which root it is. - const gitHistory = getGitHistories(root, locale).get( - path.relative(root, filePath) - ); + const historyArgs = + locale === "de" + ? { + root: CONTENT_ROOT, + locale: DEFAULT_LOCALE, + filePath: filePath.replace( + /.*\/files\/de(?=\/)/, + path.join(CONTENT_ROOT, DEFAULT_LOCALE.toLowerCase()) + ), + } + : { + root, + locale, + filePath, + }; + + const gitHistory = getGitHistories( + historyArgs.root, + historyArgs.locale + ).get(path.relative(historyArgs.root, historyArgs.filePath)); + let modified = null; let hash = null; if (gitHistory) { From 45196cb870cdfac85ec93a3918a9d2358fafdae8 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:20:44 +0100 Subject: [PATCH 094/100] refactor(libs): extract getRepositoryUrlByLocale() --- build/index.ts | 10 ++++------ libs/locale-utils/index.js | 7 +++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/build/index.ts b/build/index.ts index dba4fd6ad2f0..5251a2955bc8 100644 --- a/build/index.ts +++ b/build/index.ts @@ -44,7 +44,7 @@ import { postProcessSmallerHeadingIDs, } from "./utils.js"; import { addBaseline } from "./web-features.js"; -import { getRepositoryByLocale } from "../libs/locale-utils/index.js"; +import { getRepositoryUrlByLocale } from "../libs/locale-utils/index.js"; export { default as SearchIndex } from "./search-index.js"; export { gather as gatherGitHistory } from "./git-history.js"; export { buildSPAs } from "./spas.js"; @@ -132,11 +132,10 @@ function injectNotecardOnWarnings($: cheerio.CheerioAPI) { /** * Return the full URL directly to the file in GitHub based on this folder. * @param {String} folder - the current folder we're processing. - */ + */ r; function getGitHubURL(root: string, folder: string, filename: string) { const [locale] = folder.split("/", 2); - const repo = getRepositoryByLocale(locale); - const baseURL = `https://github.com/mdn/${repo}`; + const baseURL = getRepositoryUrlByLocale(locale); return `${baseURL}/blob/${getCurrentGitBranch( root @@ -148,8 +147,7 @@ function getGitHubURL(root: string, folder: string, filename: string) { * @param {String} hash - the full hash to point to. */ export function getLastCommitURL(locale: string, hash: string) { - const repo = getRepositoryByLocale(locale); - const baseURL = `https://github.com/mdn/${repo}`; + const baseURL = getRepositoryUrlByLocale(locale); return `${baseURL}/commit/${hash}`; } diff --git a/libs/locale-utils/index.js b/libs/locale-utils/index.js index 2a97e29757e4..740bf6111669 100644 --- a/libs/locale-utils/index.js +++ b/libs/locale-utils/index.js @@ -76,6 +76,13 @@ export function isValidLocale(locale) { return typeof locale === "string" && VALID_LOCALES.has(locale.toLowerCase()); } +export function getRepositoryUrlByLocale(locale) { + const repo = getRepositoryByLocale(locale); + const url = `https://github.com/mdn/${repo}`; + + return url; +} + export function getRepositoryByLocale(locale) { switch (locale.toLowerCase()) { case "en-us": From 067112d731df22f892c8ddbcccfc3fc9c3d4fd08 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:23:51 +0100 Subject: [PATCH 095/100] fix(language-menu): replace "locale" with "language" --- client/src/ui/organisms/article-actions/language-menu/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/ui/organisms/article-actions/language-menu/index.tsx b/client/src/ui/organisms/article-actions/language-menu/index.tsx index 6f4d7ce014b9..ac3219c4ff34 100644 --- a/client/src/ui/organisms/article-actions/language-menu/index.tsx +++ b/client/src/ui/organisms/article-actions/language-menu/index.tsx @@ -185,7 +185,7 @@ function LocaleStatusIcon({ locale }: { locale: string }) { switch (locale) { case "de": return ( - + ); From 4d3a1f47487fd6034f5c1ad7c7ef9e26b52897b0 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:26:22 +0100 Subject: [PATCH 096/100] Revert "fix(kumascript): avoid ListSubpagesForSidebar error" This reverts commit 689a5651f0e855761235c0ea9955784669329966. --- kumascript/macros/ListSubpagesForSidebar.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kumascript/macros/ListSubpagesForSidebar.ejs b/kumascript/macros/ListSubpagesForSidebar.ejs index 6f93e266699e..916287515148 100644 --- a/kumascript/macros/ListSubpagesForSidebar.ejs +++ b/kumascript/macros/ListSubpagesForSidebar.ejs @@ -93,7 +93,7 @@ async function createLink(aPage) { } let output = '' -if(pages?.length) { +if(pages.length) { const linkArray = await Promise.all(pages.map(createLink)); const links = linkArray.join(''); output = `
        ${links}
      `; From b9d4cf1e38a38610588ff9ad080e392c8410c990 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:28:25 +0100 Subject: [PATCH 097/100] fixup! chore(kumascript): add German translations --- kumascript/macros/Non-standard_Header.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kumascript/macros/Non-standard_Header.ejs b/kumascript/macros/Non-standard_Header.ejs index 65bf18302d0b..96394f9557bd 100644 --- a/kumascript/macros/Non-standard_Header.ejs +++ b/kumascript/macros/Non-standard_Header.ejs @@ -13,7 +13,7 @@ var title = mdn.localString({ }); var description = mdn.localString({ - "de": "Diese Funktion ist kein Standard und befindet sich nicht auf dem Weg zu einem Standard. Verwenden Sie sie nicht auf Produktionsseiten, die auf das Web ausgerichtet sind: Sie wird nicht für alle Benutzer funktionieren. Außerdem kann es große Inkompatibilitäten zwischen Implementierungen geben und das Verhalten kann sich in Zukunft ändern.", + "de": "Diese Funktion ist nicht standardisiert und befindet sich nicht im Standardisierungsprozess. Verwenden Sie sie nicht auf Produktionsseiten, die auf das Web ausgerichtet sind: Sie wird nicht für alle Benutzer funktionieren. Außerdem kann es große Inkompatibilitäten zwischen Implementierungen geben und das Verhalten kann sich in Zukunft ändern.", "en-US": "This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.", "es": "Esta característica no es parte de los estándares. No la uses en sitios Web en producción: no funcionará para todos los usuarios. Podrían haber también incompatibilidades considerables entre distintas implementaciones y el comportamiento podría cambiar en el futuro.", "fr": "Cette fonctionnalité n'est ni standard, ni en voie de standardisation. Ne l'utilisez pas pour des sites accessibles sur le Web : elle ne fonctionnera pas pour tout utilisateur. Il peut également y avoir d'importantes incompatibilités entre les implémentations et son comportement peut être modifié dans le futur.", From a5f8c96cf019975de58f7d278a3b33791b062eea Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:29:25 +0100 Subject: [PATCH 098/100] fixup! Apply suggestions from code review --- kumascript/macros/SeeCompatTable.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kumascript/macros/SeeCompatTable.ejs b/kumascript/macros/SeeCompatTable.ejs index 26c8bdf32741..c19380eb3c57 100644 --- a/kumascript/macros/SeeCompatTable.ejs +++ b/kumascript/macros/SeeCompatTable.ejs @@ -1,7 +1,7 @@ <% const str = mdn.localString({ - "de": "Dies ist eine experimentelle Technologie
      Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese in der Produktion verwenden.", + "de": "Dies ist eine experimentelle Technologie
      Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig, bevor Sie diese produktiv verwenden.", "es": "Esta es una tecnología experimental
      Comprueba la Tabla de compabilidad de navegadores cuidadosamente antes de usarla en producción.", "fr": "Cette fonction est expérimentale
      Puisque cette fonction est toujours en développement dans certains navigateurs, veuillez consulter le tableau de compatibilité pour les préfixes à utiliser selon les navigateurs.
      Il convient de noter qu'une fonctionnalité expérimentale peut voir sa syntaxe ou son comportement modifié dans le futur en fonction des évolutions de la spécification.", "ja": "これは実験的な機能です。
      本番で使用する前にブラウザー互換性一覧表をチェックしてください。", From be5fdfee4e2fd8559b2e4fb2a14c850845ba9680 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:31:43 +0100 Subject: [PATCH 099/100] chore(stage-build): build de --- .github/workflows/stage-build.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stage-build.yml b/.github/workflows/stage-build.yml index 7eae27df7ad7..7697fcfa4293 100644 --- a/.github/workflows/stage-build.yml +++ b/.github/workflows/stage-build.yml @@ -138,6 +138,18 @@ jobs: # See matching warning for mdn/content checkout step fetch-depth: 0 + - name: Checkout (translated-content-de) + uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD || ! vars.SKIP_FUNCTION }} + with: + repository: mdn/translated-content-de + path: mdn/translated-content-de + + - name: Move de into translated-content + run: | + mv mdn/translated-content-de/files/de mdn/translated-content/files/ + rm -rf mdn/translated-content-de + - uses: actions/checkout@v4 if: ${{ ! vars.SKIP_BUILD }} with: @@ -283,7 +295,7 @@ jobs: # Build using one process per locale. # Note: We have 4 cores, but 9 processes is a reasonable number. - for locale in en-us es fr ja ko pt-br ru zh-cn zh-tw; do + for locale in en-us de es fr ja ko pt-br ru zh-cn zh-tw; do yarn build:docs --locale $locale 2>&1 | sed "s/^/[$locale] /" & pids+=($!) done From 8e08e4bacf0dc6c7fc7448da55d386c876cdd935 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Thu, 31 Oct 2024 17:36:17 +0100 Subject: [PATCH 100/100] fixup! refactor(libs): extract getRepositoryUrlByLocale() --- build/index.ts | 2 +- libs/locale-utils/index.d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/index.ts b/build/index.ts index 5251a2955bc8..5f13483eb7c1 100644 --- a/build/index.ts +++ b/build/index.ts @@ -132,7 +132,7 @@ function injectNotecardOnWarnings($: cheerio.CheerioAPI) { /** * Return the full URL directly to the file in GitHub based on this folder. * @param {String} folder - the current folder we're processing. - */ r; + */ function getGitHubURL(root: string, folder: string, filename: string) { const [locale] = folder.split("/", 2); const baseURL = getRepositoryUrlByLocale(locale); diff --git a/libs/locale-utils/index.d.ts b/libs/locale-utils/index.d.ts index c49bc6bff01a..50c985f4400b 100644 --- a/libs/locale-utils/index.d.ts +++ b/libs/locale-utils/index.d.ts @@ -1,3 +1,4 @@ export function getLocale(request: any, fallback?: string): any; export function isValidLocale(locale: any): locale is string; +export function getRepositoryUrlByLocale(locale: string): string; export function getRepositoryByLocale(locale: string): string;