From 4ced117e0569fa42c57c7f444f9f103c74c65fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?= Date: Tue, 19 Feb 2013 19:40:52 +0100 Subject: [PATCH] Initial implementation of MathJax.Localization. Some experiments with French localization. --- unpacked/MathJax.js | 312 ++++++++++++++++++++++++++++++++ unpacked/extensions/MathMenu.js | 76 +++++--- 2 files changed, 362 insertions(+), 26 deletions(-) diff --git a/unpacked/MathJax.js b/unpacked/MathJax.js index 75273002e..dda59b7c5 100644 --- a/unpacked/MathJax.js +++ b/unpacked/MathJax.js @@ -1,3 +1,5 @@ +/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /************************************************************* * * MathJax.js @@ -2476,3 +2478,313 @@ MathJax.Hub.Startup = { }} /**********************************************************/ + +MathJax.Localization = { + + locale: "fr", + directory: "[MathJax]/localization", + strings: { + fr: { + isLoaded: true, + domains: { + "_": { + strings: { + } + }, + // FontWarnings: { + // isLoaded: true, + // strings: { + // fonts: + // [ + // ["p"], + // "MathJax peut utiliser soit les ", + // ["a",{href:"http://www.stixfonts.org/",target:"_blank"},"polices STIX"], + // " soit les ", + // ["a",{href:"http://www.mathjax.org/help-v2/fonts/",target:"_blank"},["polices TeX de MathJax"]], + // ". Téléchargez et installez une de ces familles pour rendre plus confortable votre utilisation de MathJax." + // ] + // } + // }, + + Menu: { + isLoaded: true, + strings: { + WebkitNativeMMLWarning: + + "Votre navigateur ne semble pas comporter de support MathML, " + + "changer le mode de rendu pourrait rendre illisibles " + + "les expressions mathématiques.", + + MSIENativeMMLWarning: + + "Internet Explorer a besoin de module complémentaire MathPlayer " + + "pour afficher le MathML.", + + OperaNativeMMLWarning: + + "Le support MathML d'Opera est limité, changer le mode de rendu " + + "pourrait entrainer un affichage médiocre de certaines expressions.", + + SafariNativeMMLWarning: + + "Le support MathML natif de votre navigateur ne comporte pas " + + "toutes les fonctionnalités requises par MathJax, certaines " + + "expressions pourront donc ne pas s'afficher correctement.", + + FirefoxNativeMMLWarning: + + "Le support MathML natif de votre navigateur ne comporte pas " + + "toutes les fonctionnalités requises par MathJax, certaines " + + "expressions pourront donc ne pas s'afficher correctement.", + + SwitchAnyway: + "Êtes vous certain de vouloir changer le mode de rendu ?\n\n" + + "Appuyez sur OK pour valider ou Annuler pour continuer avec le " + + "mode de rendu actuellement sélectionné.", + + ScaleMath: + "Mise à l'échelle des expressions mathématiques (par rapport au " + + "text environnant) de %1%%", + + NonZeroScale: + "L'échelle ne peut être nulle", + + PercentScale: + "L'échelle doit être un pourcentage (e.g. 120%%)", + + IE8warning: + "Ceci désactivera le menu de MathJax et les fonctionalités de " + + "zoom mais vous pourrez toujours obtenir le menu de MathJax " + + "en utilisant la commande Alt+Clic sur une expression.\n\n" + + "Êtes vous certain de vouloir choisir les options de MathPlayer?", + + IE9warning: + "Le menu contextuel de MathJax sera désactivé, " + + "mais vous pourrez toujours obtenir le menu de MathJax " + + "en utilisant la commande Alt-Clic sur une expression.", + + NoOriginalForm: + "Aucune forme originelle", + + Close: + "Fermer", + + EqSource: + "Source de l'équation MathJax" + } + } + } + } + }, + + _: function (messageId, englishPhrase) { + + // These variables are used in string parsing + var plural = this.plural; + var args = arguments; + var i, s, result; + + function parseNextUnicodePoint(appendToResult) + { + var n = s.charCodeAt(i); + if (n <= 0xD7FF || 0xE000 <= n) { + // Code points U+0000 to U+D7FF and U+E000 to U+FFFF. + // Append the character. + if (appendToResult) result += s[i] + i++; + return; + } else if (i+1 < m) { + // Code points U+10000 to U+10FFFF + // Append the surrogate pairs. + if (appendToResult) { result += s[i]; result += s[i+1]; } + i+=2 + return; + } + // Ignore lead surrogate at the end of the string. + // This should not happen with valid unicode string. + i++; + } + + function parseArgument(appendToResult) + { + if (!(/\d/.test(s[0]))) return false; + + // %INTEGER argument substitution + var argIndex = s.match(/^\d+/)[0]; + i += argIndex.length; + var key = +argIndex+1; + if (key in args) { + if (appendToResult) { result += args[key]; } + return true; + } + + // invalid index: just %INTEGER and continue + if (appendToResult) { result += "%" + argIndex; } + i++; + return true; + } + + function parseInteger(appendToResult) + { + var number = s.match(/^\{(\d+)\}/); + if (!number) return false; + + // %{INTEGER} escaped integer + if (appendToResult) { result += number[1]; } + i += number[0].length; + return true; + } + + function parseChoiceBlock(blockName, choiceFunction) + { + var pattern = "^\\{"+blockName+":%(\\d)+\\|"; + var blockStart = s.match(pattern); + if (!blockStart) return false; + + var key = +blockStart[1]+1; + if (!(key in args)) return false; + + // %\{blockName:%INTEGER|form1|form2 ... \} + i = blockStart[0].length; + + var choiceIndex = choiceFunction(args[key]), j = 1; + var isChosenBlock = (j === choiceIndex); + var blockFound = false; + + while (i < m) { + if (s[i] == "|") { + // new choice block + i++; j++; + isChosenBlock = (j === choiceIndex); + if (isChosenBlock) blockFound = true; + continue; + } + if (s[i] == "}") { + // closing brace + i++; + break; + } + if (s[i] != "%" || i+1 == m) { + // normal char or % at the end of the string + parseNextUnicodePoint(isChosenBlock); + continue; + } + + // keep only the substring after the % + i++; s = s.substr(i); m -= i; i = 0; + + // %INTEGER argument substitution + if (parseArgument(isChosenBlock)) continue; + + // %{INTEGER} escaped integer + if (parseInteger(isChosenBlock)) continue; + + // %CHAR: escaped character + parseNextUnicodePoint(isChosenBlock); + continue; + } + + if (!blockFound) { + i = 0; + return false; + } + + return true; + } + + function transformString(string) + { + s = string; + i = 0; + m = s.length; + result = ""; + + while (i < m) { + if (s[i] != "%" || i+1 == m) { + // normal char or % at the end of the string + parseNextUnicodePoint(true); + continue; + } + + // keep only the substring after the % + i++; s = s.substr(i); m -= i; i = 0; + + // %INTEGER argument substitution + if (parseArgument(true)) continue; + + // %{INTEGER} escaped integer + if (parseInteger(true)) continue; + + // %\{plural:%INTEGER|form1|form2 ... \} plural forms + if (parseChoiceBlock("plural", plural)) continue; + + // %CHAR: escaped character + parseNextUnicodePoint(true); + continue; + } + + return result; + } + + function transformHTMLSnippet(snippet) + { + for (key in snippet) { + var e = snippet[key]; + if (typeof e === "string") { + snippet[key] = transformString(e); + continue; + } + var lastIndex = e.length-1; + if (Array.isArray(e[lastIndex])) { + e[lastIndex] = transformHTMLSnippet(e[lastIndex]); + } + } + return snippet; + } + + // try to get the translated phrase or use the englishPhrase fallback + var phrase = englishPhrase; + var translationData = this.strings[this.locale]; + if (translationData) { + if (translationData.isLoaded) { + var domain = "_"; + if (Array.isArray(messageId) && messageId.length == 2) { + domain = messageId[0]; + messageId = messageId[1]; + } + if (domain in translationData.domains) { + domain = translationData.domains[domain] + if (domain.isLoaded && messageId in domain.strings) { + phrase = domain.strings[messageId]; + } + } + } + } + + if (typeof phrase === "string") { + // handle the phrase as a simple string + return transformString(phrase); + } + + // handle the phrase as a HTML snippet + return transformHTMLSnippet(phrase); + }, + + setLocale: function(locale) { + this.locale = locale; + // TODO + }, + + addTranslation: function (locale, domain, definition) { + // TODO + }, + + fontFamily: function () { + return null; + }, + + plural: function(n) { + if (n == 1) return 1; + return 2; + } +}; diff --git a/unpacked/extensions/MathMenu.js b/unpacked/extensions/MathMenu.js index 8a2075a29..e849171ac 100644 --- a/unpacked/extensions/MathMenu.js +++ b/unpacked/extensions/MathMenu.js @@ -1,3 +1,5 @@ +/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /************************************************************* * * MathJax/extensions/MathMenu.js @@ -33,6 +35,13 @@ signal: SIGNAL }; + var _ = function (id) { + return MathJax.Localization._.apply( + MathJax.Localization, + [ ["Menu", id] ].concat([].slice.call(arguments,1)) + ); + }; + var isPC = HUB.Browser.isPC, isMSIE = HUB.Browser.isMSIE, isIE9 = ((document.documentMode||0) > 8); var ROUND = (isPC ? null : "5px"); @@ -690,7 +699,10 @@ return; } } else { - if (MENU.jax.originalText == null) {alert("No original form available"); return} + if (MENU.jax.originalText == null) { + alert(_("NoOriginalForm", "No original form available")); + return; + } MENU.ShowSource.Text(MENU.jax.originalText,event); } }; @@ -706,16 +718,17 @@ var w = MENU.ShowSource.Window(event); delete MENU.ShowSource.w; text = text.replace(/^\s*/,"").replace(/\s*$/,""); text = text.replace(/&/g,"&").replace(//g,">"); + var title = _("EqSource", "MathJax Equation Source"); if (MENU.isMobile) { w.document.open(); - w.document.write("MathJax Equation Source"); + w.document.write(""+title+""); w.document.write("
"+text+"
"); - w.document.write("
"); + w.document.write("
"); w.document.write(""); w.document.close(); } else { w.document.open(); - w.document.write("MathJax Equation Source"); + w.document.write(""+title+""); w.document.write("
"+text+"
"); w.document.write(""); w.document.close(); @@ -740,7 +753,7 @@ MENU.Scale = function () { var HTMLCSS = OUTPUT["HTML-CSS"], nMML = OUTPUT.NativeMML, SVG = OUTPUT.SVG; var SCALE = (HTMLCSS||nMML||SVG||{config:{scale:100}}).config.scale; - var scale = prompt("Scale all mathematics (compared to surrounding text) by",SCALE+"%"); + var scale = prompt(_("ScaleMath", "Scale all mathematics (compared to surrounding text) by %1%%",SCALE)); if (scale) { if (scale.match(/^\s*\d+(\.\d*)?\s*%?\s*$/)) { scale = parseFloat(scale); @@ -752,8 +765,9 @@ MENU.cookie.scale = scale; MENU.saveCookie(); HUB.Reprocess(); } - } else {alert("The scale should not be zero")} - } else {alert("The scale should be a percentage (e.g., 120%)")} + } else {alert(_("NonZeroScale", "The scale should not be zero"))} + } else {alert(_("PercentScale", + "The scale should be a percentage (e.g., 120%%)"))} } }; @@ -791,8 +805,10 @@ break; } if (message) { - message += "\n\nSwitch the renderer anyway?\n\n" + - "(Press OK to switch, CANCEL to continue with the current renderer)"; + message += "\n\n"; + message += _("SwitchAnyway", + "Switch the renderer anyway?\n\n" + + "(Press OK to switch, CANCEL to continue with the current renderer)"); MENU.cookie.renderer = jax[0].id; MENU.saveCookie(); if (!confirm(message)) {return} if (warned) {MENU.cookie[warned] = CONFIG.settings[warned] = true} MENU.cookie.renderer = CONFIG.settings.renderer; MENU.saveCookie(); @@ -805,28 +821,34 @@ }; MENU.Renderer.Messages = { MML: { - WebKit: "Your browser doesn't seem to support MathML natively, " + - "so switching to MathML output may cause the mathematics " + - "on the page to become unreadable.", + WebKit: _("WebkitNativeMMLWarning", + "Your browser doesn't seem to support MathML natively, " + + "so switching to MathML output may cause the mathematics " + + "on the page to become unreadable."), - MSIE: "Internet Explorer requires the MathPlayer plugin " + - "in order to process MathML output.", + MSIE: _("MSIENativeMMLWarning", + "Internet Explorer requires the MathPlayer plugin " + + "in order to process MathML output."), - Opera: "Opera's support for MathML is limited, so switching to " + - "MathML output may cause some expressions to render poorly.", + Opera: _("OperaNativeMMLWarning", + "Opera's support for MathML is limited, so switching to " + + "MathML output may cause some expressions to render poorly."), - Safari: "Your browser's native MathML does not implement all the features " + - "used by MathJax, so some expressions may not render properly.", + Safari: _("SafariNativeMMLWarning", + "Your browser's native MathML does not implement all the features " + + "used by MathJax, so some expressions may not render properly."), - Firefox: "Your browser's native MathML does not implement all the features " + - "used by MathJax, so some expressions may not render properly." + Firefox: _("FirefoxNativeMMLWarning", + "Your browser's native MathML does not implement all the features " + + "used by MathJax, so some expressions may not render properly.") }, SVG: { - MSIE: "SVG is not implemented in Internet Explorer prior to " + - "IE9, or when the browser is emulating IE8 or below. " + - "Switching to SVG output will cause the mathemtics to " + - "not display properly." + MSIE: _("MSIESVGWarning", + "SVG is not implemented in Internet Explorer prior to " + + "IE9, or when the browser is emulating IE8 or below. " + + "Switching to SVG output will cause the mathemtics to " + + "not display properly.") } }; @@ -861,13 +883,15 @@ }; MENU.MPEvents.Messages = { IE8warning: + _("IE8warning", "This will disable the MathJax menu and zoom features, " + "but you can Alt-Click on an expression to obtain the MathJax " + - "menu instead.\n\nReally change the MathPlayer settings?", + "menu instead.\n\nReally change the MathPlayer settings?"), IE9warning: + _("IE9warning", "The MathJax contextual menu will be disabled, but you can " + - "Alt-Click on an expression to obtain the MathJax menu instead." + "Alt-Click on an expression to obtain the MathJax menu instead.") }; /*************************************************************/