@mdn/fred 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.env-dist +4 -0
  2. package/CHANGELOG.md +25 -0
  3. package/build/eslint-fred.js +6 -4
  4. package/build/plugins/generate-element-map.js +3 -2
  5. package/build/utils.js +0 -23
  6. package/components/curriculum/module.css +1 -0
  7. package/components/curriculum-module/server.css +1 -0
  8. package/components/env/index.js +3 -0
  9. package/components/footer/server.js +1 -1
  10. package/components/live-sample-result/element.js +1 -0
  11. package/components/menu/base.css +2 -1
  12. package/components/menu/constants.js +443 -147
  13. package/components/menu/missing-docs.json +140 -0
  14. package/components/menu/server.js +78 -657
  15. package/components/menu/types.d.ts +50 -0
  16. package/components/menu/update-missing-docs.js +66 -0
  17. package/components/outer-layout/utils.js +2 -2
  18. package/components/play-runner/element.js +14 -6
  19. package/entry.client.js +1 -0
  20. package/hooks/ga-init.js +31 -0
  21. package/hooks/glean-init.js +2 -6
  22. package/l10n/fr.ftl +145 -1
  23. package/out/service-worker.js +1 -1
  24. package/out/service-worker.js.map +1 -1
  25. package/out/static/client/{6480.09f744cd2fb69ed8.js → 6480.c839ead24f125a7e.js} +3 -2
  26. package/out/static/client/{6480.09f744cd2fb69ed8.js.map → 6480.c839ead24f125a7e.js.map} +1 -1
  27. package/out/static/client/9914.021220acc0d3e777.js +11 -0
  28. package/out/static/client/9914.021220acc0d3e777.js.map +1 -0
  29. package/out/static/client/index.0f1f5c05b3c9458b.js +412 -0
  30. package/out/static/client/index.0f1f5c05b3c9458b.js.map +1 -0
  31. package/out/static/client/{runtime.b178b9749f31202a.js → runtime.f3c0cd5b45c6e874.js} +2 -2
  32. package/out/static/client/{runtime.b178b9749f31202a.js.map → runtime.f3c0cd5b45c6e874.js.map} +1 -1
  33. package/out/static/client/stats.json +116 -116
  34. package/out/static/client/styles-curriculum-landing.cbaf6ff367369a26.css.map +1 -1
  35. package/out/static/client/styles-curriculum-module.c7ec78d3e724cf64.css.map +1 -1
  36. package/out/static/client/{styles-global.fb7afecd89ca2dff.js → styles-global.52fa98f0e9ec0040.js} +1 -1
  37. package/out/static/client/{styles-global.684fd2c5254c94b8.css → styles-global.75b8c75561733419.css} +2 -2
  38. package/out/static/client/{styles-global.684fd2c5254c94b8.css.map → styles-global.75b8c75561733419.css.map} +1 -1
  39. package/out/static/client/styles-menu.c41c14be9597dcd9.css +2 -0
  40. package/out/static/client/styles-menu.c41c14be9597dcd9.css.map +1 -0
  41. package/out/static/legacy/asset-manifest.json +5 -5
  42. package/out/static/legacy/{index.ad3600b01e18974e.html → index.4d5b9af908771553.html} +1 -1
  43. package/out/static/legacy/{index.5592b02d966df8ba.js → index.e275f57e34e5ad42.js} +3 -3
  44. package/out/static/legacy/{index.5592b02d966df8ba.js.map → index.e275f57e34e5ad42.js.map} +1 -1
  45. package/out/static/legacy/stats.json +10 -10
  46. package/out/static/legacy/{yari.8ce0be252d1ae155.js → yari.28c752c4002c881d.js} +3 -3
  47. package/out/static/legacy/{yari.8ce0be252d1ae155.js.map → yari.28c752c4002c881d.js.map} +1 -1
  48. package/out/static/ssr/index.js +306 -580
  49. package/out/static/ssr/index.js.map +1 -1
  50. package/out/static/ssr/stats.json +4 -4
  51. package/package.json +9 -9
  52. package/utils/dnt-helper.js +59 -0
  53. package/utils/name-transformation.js +40 -0
  54. package/utils/telemetry-opt-out.js +12 -0
  55. package/components/menu/check-missing-docs.js +0 -44
  56. package/out/static/client/9914.251fe19f0038e97b.js +0 -11
  57. package/out/static/client/9914.251fe19f0038e97b.js.map +0 -1
  58. package/out/static/client/index.26176fe4ab13dce5.js +0 -268
  59. package/out/static/client/index.26176fe4ab13dce5.js.map +0 -1
  60. package/out/static/client/styles-menu.5193bf2642ae7d64.css +0 -2
  61. package/out/static/client/styles-menu.5193bf2642ae7d64.css.map +0 -1
  62. /package/out/static/client/{6480.09f744cd2fb69ed8.js.LICENSE.txt → 6480.c839ead24f125a7e.js.LICENSE.txt} +0 -0
  63. /package/out/static/client/{index.26176fe4ab13dce5.js.LICENSE.txt → index.0f1f5c05b3c9458b.js.LICENSE.txt} +0 -0
  64. /package/out/static/legacy/{index.5592b02d966df8ba.js.LICENSE.txt → index.e275f57e34e5ad42.js.LICENSE.txt} +0 -0
  65. /package/out/static/legacy/{yari.8ce0be252d1ae155.js.LICENSE.txt → yari.28c752c4002c881d.js.LICENSE.txt} +0 -0
@@ -0,0 +1,50 @@
1
+ import { ServerRenderedTemplate } from "@lit-labs/ssr";
2
+ interface BaseTab {
3
+ id: string;
4
+ buttonText: ButtonText;
5
+ }
6
+
7
+ type ButtonText = string | { long: string; short: string };
8
+
9
+ interface PanelTitle {
10
+ text: string;
11
+ slug?: string;
12
+ }
13
+
14
+ interface BaseItem {
15
+ text: string;
16
+ label?: string;
17
+ }
18
+
19
+ interface SlugItem extends BaseItem {
20
+ slug: string;
21
+ }
22
+
23
+ interface HrefItem extends BaseItem {
24
+ href: string;
25
+ icon?: string;
26
+ }
27
+
28
+ type LinkItem = SlugItem | HrefItem;
29
+
30
+ interface RenderItem {
31
+ render: () => ServerRenderedTemplate;
32
+ }
33
+
34
+ type PanelItem = LinkItem | RenderItem;
35
+
36
+ interface PanelGroup {
37
+ title?: string;
38
+ items: PanelItem[];
39
+ }
40
+
41
+ interface DropdownTab extends BaseTab {
42
+ panelTitle: PanelTitle;
43
+ panelGroups: PanelGroup[];
44
+ }
45
+
46
+ interface LinkTab extends BaseTab {
47
+ href: string;
48
+ }
49
+
50
+ export type MenuTab = DropdownTab | LinkTab;
@@ -0,0 +1,66 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { TABS, TRANSLATED_LOCALES } from "./constants.js";
5
+
6
+ // Locales.
7
+ const locales = [...TRANSLATED_LOCALES].toSorted();
8
+
9
+ // Slugs.
10
+ /** @type {string[]} */
11
+ const slugs = TABS.flatMap((tab) => {
12
+ const slugs = [];
13
+ if (
14
+ "panelTitle" in tab &&
15
+ "slug" in tab.panelTitle &&
16
+ typeof tab.panelTitle.slug === "string"
17
+ ) {
18
+ slugs.push(tab.panelTitle.slug);
19
+ }
20
+
21
+ if ("panelGroups" in tab) {
22
+ for (const group of tab.panelGroups) {
23
+ for (const item of group.items) {
24
+ if ("slug" in item) {
25
+ slugs.push(item.slug);
26
+ }
27
+ }
28
+ }
29
+ }
30
+
31
+ return slugs;
32
+ }).toSorted();
33
+
34
+ console.log(`Checking ${slugs.length} slugs in ${locales.length} locales…`);
35
+
36
+ /** @type {Record<string, string[]>} */
37
+ const result = {};
38
+ for (const locale of locales) {
39
+ console.log(`\n=== ${locale} ===`);
40
+ result[locale] = [];
41
+
42
+ for (const slug of slugs) {
43
+ const url = `https://developer.mozilla.org/${locale}/docs/${slug}`;
44
+ process.stdout.write(`.`);
45
+
46
+ try {
47
+ const res = await fetch(url, { method: "HEAD" });
48
+
49
+ if (res.status === 200) {
50
+ // All good.
51
+ continue;
52
+ } else if (res.status === 404) {
53
+ // Not found.
54
+ } else {
55
+ console.log(`\n⚠️ HTTP ${res.status} [${url}]`);
56
+ }
57
+ } catch (error) {
58
+ console.log(`\n🔥 ERROR: ${error} [${url}]`);
59
+ }
60
+
61
+ result[locale].push(slug);
62
+ }
63
+ }
64
+
65
+ const file = path.join(import.meta.dirname, "missing-docs.json");
66
+ await writeFile(file, JSON.stringify(result, null, 2) + "\n", "utf8");
@@ -1,4 +1,4 @@
1
- import { toCamelCase } from "../../build/utils.js";
1
+ import { camelToKebabCase } from "../../utils/name-transformation.js";
2
2
 
3
3
  /**
4
4
  * @param {import("@rspack/core").StatsCompilation} manifest
@@ -66,5 +66,5 @@ export function stylesForComponents(components, manifest) {
66
66
  * @param {string} component
67
67
  */
68
68
  export function styleEntryForComponent(component) {
69
- return `styles-${toCamelCase(component)}`;
69
+ return `styles-${camelToKebabCase(component)}`;
70
70
  }
@@ -30,6 +30,7 @@ export class MDNPlayRunner extends LitElement {
30
30
  srcPrefix: { type: String, attribute: "src-prefix" },
31
31
  allow: { type: String },
32
32
  sandbox: { type: String },
33
+ permalink: { type: Boolean },
33
34
  _src: { state: true },
34
35
  };
35
36
 
@@ -48,7 +49,12 @@ export class MDNPlayRunner extends LitElement {
48
49
  this.allow = undefined;
49
50
  /** @type {string | undefined} */
50
51
  this.sandbox = undefined;
51
- this._subdomain = crypto.randomUUID();
52
+ /** @type {boolean} */
53
+ this.permalink = false;
54
+ /** @type {string} */
55
+ this._uuid = crypto.randomUUID();
56
+ /** @type {string} */
57
+ this._subdomain = "";
52
58
  /** @type {Promise<true>} */
53
59
  this.ready = new Promise((resolve) => {
54
60
  this._resolveReady = () => resolve(true);
@@ -85,13 +91,14 @@ export class MDNPlayRunner extends LitElement {
85
91
  this.defaults,
86
92
  this.theme.value,
87
93
  this.srcPrefix,
94
+ this.permalink,
88
95
  ]),
89
- task: async ([code, defaults, theme, srcPrefix], { signal }) => {
96
+ task: async ([code, defaults, theme, srcPrefix, permalink], { signal }) => {
90
97
  if (code && code.js && code.wat) {
91
98
  const watUrl = await compileAndEncodeWatToDataUrl(code.wat);
92
99
  code.js = code.js.replace("{%wasm-url%}", watUrl);
93
100
  }
94
- const { state } = await compressAndBase64Encode(
101
+ const { state, hash } = await compressAndBase64Encode(
95
102
  JSON.stringify({
96
103
  html: code?.html || "",
97
104
  css: code?.css || "",
@@ -102,16 +109,17 @@ export class MDNPlayRunner extends LitElement {
102
109
  );
103
110
  const prefix = (srcPrefix || "").replace(/\/$/, "");
104
111
  signal.throwIfAborted();
105
- // We're using a random subdomain for origin isolation.
112
+ const subdomain = permalink ? hash : this._uuid;
106
113
  const url = new URL(
107
114
  `${prefix}/runner.html`,
108
115
  PLAYGROUND_LOCAL
109
116
  ? location.origin.replace(PORT.toString(), PLAYGROUND_PORT.toString())
110
- : `${location.protocol}//${this._subdomain}.${PLAYGROUND_BASE_HOST}`,
117
+ : `${location.protocol}//${subdomain}.${PLAYGROUND_BASE_HOST}`,
111
118
  );
112
119
  // pass the uuid for postMessage isolation
113
- url.searchParams.set("uuid", this._subdomain);
120
+ url.searchParams.set("uuid", subdomain);
114
121
  url.searchParams.set("state", state);
122
+ this._subdomain = subdomain;
115
123
  this._src = url.href;
116
124
  this.dispatchEvent(
117
125
  new CustomEvent("mdn-play-runner-src", {
package/entry.client.js CHANGED
@@ -3,6 +3,7 @@ import "@lit-labs/ssr-client/lit-element-hydrate-support.js";
3
3
 
4
4
  // hooks:
5
5
  import "./hooks/glean-init.js";
6
+ import "./hooks/ga-init.js";
6
7
  import "./hooks/dialog-closedby.js";
7
8
  import "./hooks/load-elements.js";
8
9
  import "./components/baseline-indicator/hook.js";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Google Analytics, enabled if:
3
+ * 1. FRED_GA_ENABLED and FRED_GA_MEASUREMENT_ID are set
4
+ * 2. User has not opted out via moz-1st-party-data-opt-out cookie
5
+ * 3. User has not enabled Do Not Track (DNT)
6
+ */
7
+
8
+ import { GA_ENABLED, GA_MEASUREMENT_ID } from "../components/env/index.js";
9
+ import { dntEnabled } from "../utils/dnt-helper.js";
10
+ import { userIsOptedOut } from "../utils/telemetry-opt-out.js";
11
+
12
+ if (GA_ENABLED && GA_MEASUREMENT_ID && !userIsOptedOut() && !dntEnabled()) {
13
+ // @ts-expect-error - added by GA
14
+ globalThis.dataLayer = globalThis.dataLayer || [];
15
+
16
+ /** @param {...any} args */
17
+ function gtag(...args) {
18
+ // @ts-expect-error - added by GA
19
+ globalThis.dataLayer.push(args);
20
+ }
21
+
22
+ gtag("js", new Date());
23
+ gtag("config", GA_MEASUREMENT_ID, {
24
+ anonymize_ip: true,
25
+ });
26
+
27
+ const gaScript = document.createElement("script");
28
+ gaScript.async = true;
29
+ gaScript.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(GA_MEASUREMENT_ID)}`;
30
+ document.head.append(gaScript);
31
+ }
@@ -7,15 +7,11 @@ import {
7
7
  GLEAN_ENABLED,
8
8
  } from "../components/env/index.js";
9
9
  import { gleanClick } from "../utils/glean.js";
10
+ import { userIsOptedOut } from "../utils/telemetry-opt-out.js";
10
11
 
11
- const FIRST_PARTY_DATA_OPT_OUT_COOKIE_NAME = "moz-1st-party-data-opt-out";
12
12
  const GLEAN_APP_ID = "mdn-fred";
13
13
 
14
- const userIsOptedOut = document.cookie
15
- .split("; ")
16
- .includes(`${FIRST_PARTY_DATA_OPT_OUT_COOKIE_NAME}=true`);
17
-
18
- const uploadEnabled = !userIsOptedOut && GLEAN_ENABLED;
14
+ const uploadEnabled = !userIsOptedOut() && GLEAN_ENABLED;
19
15
 
20
16
  if (GLEAN_DEBUG) {
21
17
  Glean.setDebugViewTag("mdn-dev");
package/l10n/fr.ftl CHANGED
@@ -1 +1,145 @@
1
- blog-toc-title = Dans cet article
1
+ article-footer-last-modified = Cette page a été modifiée le <time data-l10n-name="date">{ $date }</time> par les <a data-l10n-name="contributors">contributeurs du MDN</a>.
2
+ article-footer-source-title = Dossier : { $folder } (Ouvre un onglet)
3
+
4
+ baseline-asterisk = Certaines parties de cette fonctionnalité peuvent bénéficier de prise en charge variables.
5
+ baseline-high-extra = Cette fonctionnalité est bien établie et fonctionne sur de nombreux appareils et versions de navigateurs. Elle est disponible sur tous les navigateurs depuis { $date }.
6
+ baseline-low-extra = Depuis { $date }, cette fonctionnalité fonctionne sur les appareils et les versions de navigateur les plus récents. Elle peut ne pas fonctionner sur les appareils ou navigateurs plus anciens.
7
+ baseline-not-extra = Cette fonctionnalité n'est pas Compatible car elle ne fonctionne pas dans certains des navigateurs les plus utilisés.
8
+ baseline-supported-in = Pris en charge dans { $browsers }
9
+ baseline-unsupported-in = Pas complètement pris en charge dans { $browsers }
10
+ baseline-supported-and-unsupported-in = Pris en charge dans { $supported }, mais pas complètement pris en charge dans { $unsupported }
11
+
12
+ homepage-hero-title = Des ressources pour les Développeurs,<br> par les Développeurs
13
+ homepage-hero-description = Documenter le <a data-l10n-name="css">CSS</a>, le <a data-l10n-name="html">HTML</a> et le <a data-l10n-name="js">JavaScript</a>, depuis 2005.
14
+
15
+ not-found-title = Page non trouvée
16
+ not-found-description = Désolé, la page <code data-l10n-name="url">{ $url }</code> n'a pas été trouvée.
17
+ not-found-fallback-english = <strong data-l10n-name="strong">Bonne nouvelle :</strong> La page que vous cherchez existe en <em data-l10n-name="em">Anglais</em>.
18
+ not-found-fallback-search = La page que vous avez demandée n'existe pas, mais vous pouvez essayer une recherche sur le site pour :
19
+ not-found-back = Retour à la page d'accueil
20
+
21
+ reference-toc-header = Dans cet article
22
+
23
+ footer-mofo = Visitez la société mère à but non lucratif de <a data-l10n-name="moco">Mozilla Corporation</a>, la <a data-l10n-name="mofo">Fondation Mozilla</a>.
24
+ footer-copyright = Certaines parties de ce contenu sont protégées par le droit d'auteur ©1998–{ $year } des contributeurs individuels de mozilla.org. Contenu disponible sous <a data-l10n-name="cc">une licence Creative Commons</a>.
25
+
26
+ search-modal-site-search = Rechercher sur le site <em>{ $query }</em>
27
+
28
+ site-search-search-stats = { $results } documents trouvés.
29
+ site-search-suggestion-matches = { $relation ->
30
+ [gt] plus que { $matches ->
31
+ [one] { $matches } trouvé
32
+ *[other] { $matches } trouvés
33
+ }
34
+ *[eq] { $matches ->
35
+ [one] { $matches } trouvé
36
+ *[other] { $matches } trouvés
37
+ }
38
+ }
39
+ site-search-suggestions-text = Voulez-vous dire :
40
+
41
+ blog-time-to-read = { $minutes ->
42
+ [one] { $minutes } minute de lecture
43
+ *[other] { $minutes } minutes de lecture
44
+ }
45
+ blog-post-not-found = Article de blog introuvable.
46
+
47
+ blog-previous = Article précédent
48
+ blog-next = Article suivant
49
+
50
+ -brand-name-obs = HTTP Observatory
51
+ obs-report = Signaler
52
+ obs-title = { -brand-name-obs }
53
+ obs-landing-intro = Lancé en 2016, { -brand-name-obs } améliore la sécurité du Web en analysant la conformité aux meilleures pratiques en matière de sécurité. Il a fourni des informations à plus de 6,9 millions de sites web grâce à 47 millions d'analyses.
54
+ obs-assessment = Développé par Mozilla, { -brand-name-obs } effectue une évaluation approfondie des en-têtes HTTP d'un site et d'autres configurations de sécurité clés.
55
+ obs-scanning = Son processus d'analyse automatisé fournit aux développeurs et aux administrateurs de sites web des commentaires détaillés et exploitables, axés sur l'identification et la résolution des failles de sécurité potentielles.
56
+ obs-security = Cet outil aide les développeurs et les administrateurs de sites web à renforcer la sécurité de leurs sites contre les menaces courantes dans un environnement numérique en constante évolution.
57
+ obs-mdn = { -brand-name-obs } fournit des informations efficaces en matière de sécurité, guidées par l'expertise et l'engagement de Mozilla en faveur d'un Internet plus sûr et plus sécurisé, et basées sur des tendances et des directives bien établies.
58
+
59
+
60
+ compat-loading = Chargement…
61
+
62
+ compat-browser-version-date = { $browser } { $version } – Release date: { $date }
63
+ compat-browser-version-released = Release date: { $date }
64
+
65
+ compat-link-report-issue = Signaler des problèmes avec ces données de compatibilité
66
+ compat-link-report-issue-title = Signaler un problème avec ces données de compatibilité
67
+ compat-link-report-missing-title = Signaler des données de compatibilité manquantes
68
+ compat-link-report-missing = Signaler le problème
69
+ compat-link-source = Voir les données sur GitHub
70
+ compat-link-source-title = Fichier : { $filename }
71
+
72
+ compat-deprecated = Obsolète
73
+ compat-experimental = Expérimental
74
+ compat-nonstandard = Non standard
75
+ compat-no = Non
76
+
77
+ compat-support-full = Prise en charge complète
78
+ compat-support-partial = Prise en charge partielle
79
+ compat-support-no = Pas de prise en charge
80
+ compat-support-unknown = Prise en charge inconnue
81
+ compat-support-preview = Pré-version du navigateur
82
+ compat-support-prefix = Implémenté avec le préfixe vendeur : { $prefix }
83
+ compat-support-altname = Nom alternatif : { $altname }
84
+ compat-support-removed = Supprimé en version { $version } et supérieure
85
+ compat-support-see-impl-url = Voir <a data-l10n-name="impl_url">{ $label }</a>
86
+ compat-support-flags =
87
+ { NUMBER($has_added) ->
88
+ [one] De la version { $version_added }
89
+ *[other] {""}
90
+ }{ $has_last ->
91
+ [one] { NUMBER($has_added) ->
92
+ *[zero] Jusqu'à { $versionLast } des utilisateurs
93
+ [one] {" "}jusqu'à { $versionLast } des utilisateurs
94
+ }
95
+ *[zero] { NUMBER($has_added) ->
96
+ *[zero] Utilisateurs
97
+ [one] {" "}utilisateurs
98
+ }
99
+ }
100
+ {" "}doit explicitement définir le <code data-l10n-name="name">{ $flag_name }</code>{" "}
101
+ { $flag_type ->
102
+ *[preference] préférences
103
+ [runtime_flag] indicateur d'exécution
104
+ }{ NUMBER($has_value) ->
105
+ [one] {" "}à <code data-l10n-name="value">{ $flag_value }</code>
106
+ *[other] {""}
107
+ }{"."}
108
+ { NUMBER($has_pref_url) ->
109
+ [one] { $flag_type ->
110
+ [preference] Pour changer vos préférences sur le navigateur { $browser_name }, visitez { $browser_pref_url }.
111
+ *[other] {""}
112
+ }
113
+ *[other] {""}
114
+ }
115
+
116
+ compat-legend = Légende
117
+ compat-legend-tip = Astuce : cliquer/appuyer sur une cellule pour obtenir plus d'informations.
118
+ compat-legend-yes = { compat-support-full }
119
+ compat-legend-partial = { compat-support-partial }
120
+ compat-legend-preview = En cours de développement. Pris en charge dans une pré-version.
121
+ compat-legend-no = { compat-support-no }
122
+ compat-legend-unknown = Compatibilité inconnue
123
+ compat-legend-experimental = { compat-experimental }. Attendez-vous à ce que les comportements changent à l'avenir.
124
+ compat-legend-nonstandard = { compat-nonstandard }. Vérifiez la compatibilité entre les navigateurs avant utilisation.
125
+ compat-legend-deprecated = { compat-deprecated }. Ne pas utiliser dans les nouveaux sites web.
126
+ compat-legend-footnote = Voir les notes de mise en application.
127
+ compat-legend-disabled = L'utilisateur doit explicitement activer cette fonctionnalité.
128
+ compat-legend-altname = Utilise un nom hors standard.
129
+ compat-legend-prefix = Nécessite un préfixe vendeur ou un nom différent pour être utilisé.
130
+ compat-legend-more = Contient davantage d'informations sur la compatibilité.
131
+
132
+ placement-note = Publicité
133
+ placement-no = Vous ne voulez pas voir de publicités ?
134
+
135
+ pagination-next = Page suivante
136
+ pagination-prev = Page précédente
137
+ pagination-current = Page actuelle
138
+ pagination-goto = Aller à la page { $page }
139
+
140
+ logout = Se déconnecter
141
+ login = Se connecter
142
+ settings = Mes paramètres
143
+
144
+ example-play-button-label = Exécuter
145
+ example-play-button-title = Exécutez l'exemple dans MDN Playground (ouvre un nouvel onglet)