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 diff --git a/.github/workflows/test-de-build.yml b/.github/workflows/test-de-build.yml index b2bb750fc0d0..12c39eaa597d 100644 --- a/.github/workflows/test-de-build.yml +++ b/.github/workflows/test-de-build.yml @@ -1,5 +1,8 @@ name: Test-DE Build +env: + DEFAULT_NOTES: "" + on: schedule: # * is a special character in YAML so you have to quote this string @@ -12,6 +15,23 @@ on: 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: trigger: runs-on: ubuntu-latest @@ -22,3 +42,276 @@ jobs: - run: gh workflow run "${{ github.workflow }}" --repo "${{ github.repository }}" --ref "test-de" env: GH_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }} + + build: + environment: test-de + runs-on: ubuntu-latest + + # Only run the scheduled workflows on the main repo. + if: github.repository == 'mdn/yari' && github.ref_name == 'test-de' + + steps: + - name: Checkout (yari) + uses: actions/checkout@v4 + + - name: Checkout (content) + 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 + + - name: Checkout (mdn-studio) + uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD }} + with: + repository: mdn/mdn-studio + path: mdn/mdn-studio + lfs: true + token: ${{ secrets.MDN_STUDIO_PAT }} + + - name: Checkout (curriculum) + uses: actions/checkout@v4 + if: ${{ ! vars.SKIP_BUILD }} + with: + 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 + + - 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 + if: ${{ ! vars.SKIP_BUILD }} + with: + repository: mdn/mdn-contributor-spotlight + path: mdn/mdn-contributor-spotlight + + - 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 + 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 notes + if: github.event.inputs.notes + run: | + echo "notes: ${{ github.event.inputs.notes }}" + + - name: Print CPU info + run: cat /proc/cpuinfo + + - name: Build + 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.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 + + # 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: | + set -eo pipefail + + # 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 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. + 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 + + 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 + + # SSR all pages + yarn render:html + + # 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: Auth (Cloud Storage) + 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: Auth (Cloud Function) + 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 + 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 \ + --runtime=nodejs20 \ + --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.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=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/build/index.ts b/build/index.ts index 6623779b0ca7..5f13483eb7c1 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 { 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"; @@ -133,7 +134,9 @@ 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 baseURL = getRepositoryUrlByLocale(locale); + return `${baseURL}/blob/${getCurrentGitBranch( root )}/files/${folder}/${filename}`; @@ -143,8 +146,8 @@ 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 baseURL = getRepositoryUrlByLocale(locale); return `${baseURL}/commit/${hash}`; } @@ -155,7 +158,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/client/src/document/baseline-indicator.tsx b/client/src/document/baseline-indicator.tsx index f217846188ad..6c4a0945d030 100644 --- a/client/src/document/baseline-indicator.tsx +++ b/client/src/document/baseline-indicator.tsx @@ -40,6 +40,7 @@ const ENGINES: { ]; const LOCALIZED_BCD_IDS = { + de: "browser-kompatibilität", "en-US": "browser_compatibility", es: "compatibilidad_con_navegadores", fr: "compatibilité_des_navigateurs", diff --git a/client/src/document/index.scss b/client/src/document/index.scss index 58835bce2110..41689de692ad 100644 --- a/client/src/document/index.scss +++ b/client/src/document/index.scss @@ -954,6 +954,10 @@ html a.only-in-en-us:after { 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)"; } diff --git a/client/src/document/molecules/localized-content-note/index.tsx b/client/src/document/molecules/localized-content-note/index.tsx index 50b71fb859e7..2b84210bf098 100644 --- a/client/src/document/molecules/localized-content-note/index.tsx +++ b/client/src/document/molecules/localized-content-note/index.tsx @@ -8,6 +8,15 @@ export function LocalizedContentNote({ locale: string; }) { const activeLocaleNoteContent = { + de: { + linkText: ( + <> + Experimentell: Dieser Inhalt wurde mit GPT-4o aus dem + Englischen übersetzt, und kann fehlerhaft sein. + + ), + url: "https://github.com/orgs/mdn/discussions/741", + }, "en-US": { linkText: "This page was translated from English by the community. Learn more and join the MDN Web Docs community.", @@ -69,6 +78,10 @@ export function LocalizedContentNote({ activeLocaleNoteContent["en-US"].url : "https://github.com/mdn/translated-content/blob/main/PEERS_GUIDELINES.md#activating-a-locale"; - const type = isActive ? "neutral" : "warning"; - return ; + 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 f4147383cd16..ac3e3cbd049a 100644 --- a/client/src/document/molecules/note-banner/index.tsx +++ b/client/src/document/molecules/note-banner/index.tsx @@ -1,19 +1,22 @@ +import * as React from "react"; + import NoteCard from "../../../ui/molecules/notecards"; +import { NotecardType } from "../../../types/notecards"; export function NoteBanner({ - linkText, url, type, + children, }: { - linkText: string; url: string; - type: "neutral" | "warning"; + type: NotecardType; + children: React.ReactNode; }) { return ( - +

- {linkText} + {children}

diff --git a/client/src/document/on-github.tsx b/client/src/document/on-github.tsx index f73b59ab8a3f..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/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/client/src/document/organisms/toc/index.tsx b/client/src/document/organisms/toc/index.tsx index 99d4fdd20086..0885a616ff08 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", 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 }; 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..06c437d5d837 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; @@ -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 ein paar Fragen zu beantworten?", + rateFrom: 0, + rateTill: 1, + start: 0, + end: Infinity, + }, ]; 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..ac3219c4ff34 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; + } +} 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) { diff --git a/kumascript/macros/AccessibilitySidebar.ejs b/kumascript/macros/AccessibilitySidebar.ejs index 5f89952613b3..d56124326a5a 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 dfabdfef827d..2c647133b692 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 fda5f0cce6be..c4938f49bd72 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': 'Kontakt' + }, 'en-US': { 'WebExtensions': 'Browser extensions', 'Themes': 'Themes', diff --git a/kumascript/macros/AvailableInWorkers.ejs b/kumascript/macros/AvailableInWorkers.ejs index 9d49f9e7a58c..de5c7cf87a53 100644 --- a/kumascript/macros/AvailableInWorkers.ejs +++ b/kumascript/macros/AvailableInWorkers.ejs @@ -21,6 +21,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..e185d1af7b6c 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': 'Aufgabe: 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': '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)', + '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': 'Aufgabe: 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' : 'Aufgabe: 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' : 'Containment', + 'Using_CSS_containment' : 'CSS-Containment 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..936f34764b18 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, 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/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..9837f8c01e70 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": "Canvas", + "CSS": "CSS", + "Full_Screen": "Vollbild", + "Gamepad": "Gamepad", + "IndexedDB": "IndexedDB", + "JavaScript": "JavaScript", + "Pointer_Lock": "Pointer Lock", + "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..bc92cb7bdf40 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': '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', + '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': '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' : 'Aufgabe: 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 e6d882fda4a7..ada9f9eb56c0 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': { 'CORS_errors': 'CORS errors', 'CSPDirectives': 'CSP directives', diff --git a/kumascript/macros/JsSidebar.ejs b/kumascript/macros/JsSidebar.ejs index 6602a53522c4..17b652f17359 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': 'Typisierte Arrays in JavaScript', + '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 faf389c8a90b..0a96e872693b 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 40b4867599da..916287515148 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..d370be2ccbe9 100644 --- a/kumascript/macros/MDNSidebar.ejs +++ b/kumascript/macros/MDNSidebar.ejs @@ -1,6 +1,15 @@ <% const l10nStrings = mdn.localStringMap({ + "de": { + "history": "Geschichte", + "advisory_board": "Product Advisory Board", + "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 a54f8944dbab..5b0c254a2e49 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..96394f9557bd 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 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.", diff --git a/kumascript/macros/NonStandardBadge.ejs b/kumascript/macros/NonStandardBadge.ejs index 6447ea095577..002350a59ddf 100644 --- a/kumascript/macros/NonStandardBadge.ejs +++ b/kumascript/macros/NonStandardBadge.ejs @@ -6,6 +6,7 @@ */ const title = mdn.localString({ + "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": "非标准。请在使用前检查跨浏览器支持。", @@ -13,6 +14,7 @@ const title = mdn.localString({ }); const abbreviation = mdn.localString({ + "de": "Nicht standardisiert", "en-US": "Non-standard", "ko": "비표준", "zh-CN": "非标准", diff --git a/kumascript/macros/PreviousMenuNext.ejs b/kumascript/macros/PreviousMenuNext.ejs index 820d55c32de1..d2a9a9886766 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" : [" Zurück ", " Weiter "], "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 a10ff066958f..e0f27537e407 100644 --- a/kumascript/macros/ReadOnlyInline.ejs +++ b/kumascript/macros/ReadOnlyInline.ejs @@ -1,5 +1,6 @@ <% var str = mdn.localString({ + "de": "Nur lesbar ", "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 f71af844c142..537eb86e4c50 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..61461c4c14f5 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': 'Einführung in SVG von Anfang an' + }, 'en-US': { 'Tutorials': 'Tutorials', 'Reference': 'Reference', diff --git a/kumascript/macros/SeeCompatTable.ejs b/kumascript/macros/SeeCompatTable.ejs index 0cf7df921472..c19380eb3c57 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 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": "これは実験的な機能です。
本番で使用する前にブラウザー互換性一覧表をチェックしてください。", @@ -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..6e53a15d12ce 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' : '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' : 'WebAssembly-Code laden und ausführen', + '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..e01b6087b70b 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: `Eigenschaften der ${env.title}-Property`, + 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..95095e5ce388 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", diff --git a/libs/constants/index.js b/libs/constants/index.js index a2ac0bbcb19b..b979102cebf1 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,6 @@ export const RETIRED_LOCALES = new Map( "bg", "bn", "ca", - "de", "el", "fa", "fi", @@ -49,6 +48,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", 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 253b341d7999..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"; } function correctPathFromEnv(envVarName) { diff --git a/libs/l10n/l10n.ts b/libs/l10n/l10n.ts index 41015f2c9b2e..55bdf744e030 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", diff --git a/libs/locale-utils/index.d.ts b/libs/locale-utils/index.d.ts index aaa8585792d1..50c985f4400b 100644 --- a/libs/locale-utils/index.d.ts +++ b/libs/locale-utils/index.d.ts @@ -1,2 +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; diff --git a/libs/locale-utils/index.js b/libs/locale-utils/index.js index ec2e53780259..740bf6111669 100644 --- a/libs/locale-utils/index.js +++ b/libs/locale-utils/index.js @@ -75,3 +75,21 @@ export function getLocale(request, fallback = DEFAULT_LOCALE) { 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": + return "content"; + case "de": + return "translated-content-de"; + default: + return "translated-content"; + } +} 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" diff --git a/server/translations.ts b/server/translations.ts index 12f2f88edfd5..c5b3d73dbc91 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(document.locale, document.metadata.hash); const modified = document.metadata.modified; return { commitURL, 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"