Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/node_modules
/build
/src/services/fluent-bundle.js
/src/services/locale-imports.ts

18 changes: 15 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ BASE_SOURCES = \
$(SRC_DIR)/unicode.ts \
$(SRC_DIR)/browser.ts \
$(SRC_DIR)/animate.ts \
$(SRC_DIR)/services/fluent-bundle.js \
$(SRC_DIR)/services/locale-imports.ts \
$(SRC_DIR)/services/localization.ts \
$(SRC_DIR)/services/aria.ts \
$(SRC_DIR)/domFragment.ts \
$(SRC_DIR)/tree.ts \
Expand Down Expand Up @@ -101,6 +104,7 @@ endif
# http://www.gnu.org/software/make/manual/html_node/Empty-Targets.html#Empty-Targets
NODE_MODULES_INSTALLED = ./node_modules/.installed--used_by_Makefile
BUILD_DIR_EXISTS = $(BUILD_DIR)/.exists--used_by_Makefile
FLUENT_BUNDLE = $(SRC_DIR)/services/fluent-bundle.js

# environment constants

Expand All @@ -120,6 +124,8 @@ css: $(BUILD_CSS)
font: $(FONT_TARGET)
clean:
rm -rf $(BUILD_DIR)
rm -f $(FLUENT_BUNDLE)
rm -f $(SRC_DIR)/services/locale-imports.ts
# This adds an entry to your local .git/config file that looks like this:
# [include]
# path = ../.gitconfig
Expand All @@ -129,15 +135,15 @@ setup-gitconfig:
prettify-all:
npx prettier --write '**/*.{ts,js,css,html}'

$(BUILD_JS): $(INTRO) $(SOURCES_FULL) $(OUTRO) $(BUILD_DIR_EXISTS)
$(BUILD_JS): $(INTRO) $(SOURCES_FULL) $(OUTRO) $(BUILD_DIR_EXISTS) $(FLUENT_BUNDLE)
cat $^ | ./script/escape-non-ascii | ./script/tsc-emit-only > $@
perl -pi -e s/mq-/$(MQ_CLASS_PREFIX)mq-/g $@
perl -pi -e s/{VERSION}/v$(VERSION)/ $@

$(UGLY_JS): $(BUILD_JS) $(NODE_MODULES_INSTALLED)
$(UGLIFY) $(UGLIFY_OPTS) < $< > $@

$(BASIC_JS): $(INTRO) $(SOURCES_BASIC) $(OUTRO) $(BUILD_DIR_EXISTS)
$(BASIC_JS): $(INTRO) $(SOURCES_BASIC) $(OUTRO) $(BUILD_DIR_EXISTS) $(FLUENT_BUNDLE)
cat $^ | ./script/escape-non-ascii | ./script/tsc-emit-only > $@
perl -pi -e s/mq-/$(MQ_CLASS_PREFIX)mq-/g $@
perl -pi -e s/{VERSION}/v$(VERSION)/ $@
Expand Down Expand Up @@ -168,6 +174,12 @@ $(FONT_TARGET): $(FONT_SOURCE) $(BUILD_DIR_EXISTS)
rm -rf $@
cp -r $< $@

$(FLUENT_BUNDLE): package.json script/bundle-fluent.js $(NODE_MODULES_INSTALLED)
node script/bundle-fluent.js

$(SRC_DIR)/services/locale-imports.ts: script/generate-locale-imports.js src/locale/*/messages.ftl
node script/generate-locale-imports.js

#
# -*- Test tasks -*-
#
Expand All @@ -187,6 +199,6 @@ benchmark: dev $(BUILD_TEST) $(BASIC_JS) $(BASIC_CSS)
@echo
@echo "** now open benchmark/{render,select,update}.html in your browser. **"

$(BUILD_TEST): $(INTRO) $(SOURCES_FULL) $(TEST_SUPPORT) $(UNIT_TESTS) $(OUTRO) $(BUILD_DIR_EXISTS)
$(BUILD_TEST): $(INTRO) $(SOURCES_FULL) $(TEST_SUPPORT) $(UNIT_TESTS) $(OUTRO) $(BUILD_DIR_EXISTS) $(FLUENT_BUNDLE)
cat $^ | ./script/tsc-emit-only > $@
perl -pi -e s/{VERSION}/v$(VERSION)/ $@
135 changes: 126 additions & 9 deletions docs/Api_Methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Updates the default [configuration options](Config.md) for this instance of the

If there are multiple instances of the MathQuill API, `MQ.config()` only affects the math MathQuill objects created by `MQ`. E.g.:

```javascript
```js
var MQ1 = MathQuill.getInterface(3),
MQ2 = MathQuill.getInterface(3);

Expand Down Expand Up @@ -216,15 +216,15 @@ Removes focus from the editable field.

Write the given LaTeX at the current cursor position. If the cursor does not have focus, writes to last position the cursor occupied in the editable field.

```javascript
```js
mathField.write(' - 1'); // writes ' - 1' to mathField at the cursor position
```

## .cmd(latex_string)

Enter a LaTeX command at the current cursor position or with the current selection. If the cursor does not have focus, it writes it to last position the cursor occupied in the editable field.

```javascript
```js
mathField.cmd('\\sqrt'); // writes a square root command at the cursor position
```

Expand All @@ -244,7 +244,7 @@ Move the cursor to the left/right end of the editable field, respectively. These

Moves the cursor to the end of the mathfield in the direction specified. The direction can be one of `MQ.L` or `MQ.R`. These are constants, where `MQ.L === -MQ.R` and vice versa. This function may be easier to use than [moveToLeftEnd or moveToRightEnd](#movetoleftend-movetorightend) if used in the [`moveOutOf` handler](Config.md#outof-handlers).

```javascript
```js
var config = {
handlers: {
moveOutOf: function(direction) {
Expand All @@ -258,26 +258,26 @@ var config = {

Simulates keystrokes given a string like `"Ctrl-Home Del"`, a whitespace-delimited list of [key inputs](http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120614/#fixed-virtual-key-codes) with optional prefixes.

```javascript
```js
mathField.keystroke('Shift-Left'); // Selects character before the current cursor position
```

## .typedText(text)

Simulates typing text, one character at a time from where the cursor currently is. This is supposed to be identical to what would happen if a user were typing the text in.

```javascript
```js
// Types part of the demo from mathquill.com without delays between keystrokes
mathField.typedText('x=-b\\pm \\sqrt b^2 -4ac');
```

## .setAriaLabel(ariaLabel)

Specify an [ARIA label][`aria-label`] for this field, for screen readers. The actual [`aria-label`] includes this label followed by the math content of the field as speech. Default: `'Math Input'`
Specify an [ARIA label][`aria-label`] for this field, for screen readers. The actual [`aria-label`] includes this label followed by the math content of the field as speech. Default: `'Math Input'` for English or `'Entrada Matemática'` for Spanish

## .getAriaLabel()

Returns the [ARIA label][`aria-label`] for this field, for screen readers. If no ARIA label has been specified, `'Math Input'` is returned.
Returns the [ARIA label][`aria-label`] for this field, for screen readers. If no ARIA label has been specified, the default is returned (`'Math Input'` for English, `'Entrada Matemática'` for Spanish).

## .setAriaPostLabel(ariaPostLabel, timeout)

Expand All @@ -293,7 +293,7 @@ Returns the suffix to be appended to the [ARIA label][`aria-label`], after the m

Returns `true` if the user is currently selecting text with the mouse, `false` otherwise. This can be useful for preventing certain actions (like setting the cursor position) while the user is actively dragging to select text. The method tracks mouse selection from the moment the user presses the mouse button down to start selecting until they release it or the selection is cancelled due to an edit operation.

```javascript
```js
if (!mathField.isUserSelecting()) {
// Safe to programmatically change cursor position
mathField.moveToLeftEnd();
Expand All @@ -307,6 +307,123 @@ if (!mathField.isUserSelecting()) {

Changes the [configuration](Config.md) of just this math field.

## Language Support

MathQuill supports internationalization through the `language` configuration option. This affects how mathematical expressions are read aloud for screen readers and accessibility tools.

### Setting Language

You can set the language when creating a MathField:

```js
var mathField = MQ.MathField(document.getElementById('math-input'), {
language: 'es' // Spanish
});
```

Or change it later using the config method:

```js
mathField.config({ language: 'en' }); // Switch to English
```

### Supported Languages

Currently supported language codes:

- `'en'` - English (default)
- `'es'` - Spanish (Español)

### Language Features

The language setting affects how mathematical expressions are read aloud by screen readers. This includes operators, inequalities, fractions, powers, mathematical structures, and function names. For example, `sin(x^2) ≤ 1/2` reads as "sine of x squared less than or equal to 1 half" in English or "seno de x al cuadrado menor o igual que 1 medio" in Spanish.

### Language Validation and Fallback

MathQuill provides graceful fallback for unsupported languages. When an unsupported language is specified, it will automatically fall back to the closest supported language or English as a final fallback:

```js
// Language variants are resolved automatically
var mathField = MQ.MathField(element, { language: 'en-US' }); // Uses 'en'
var mathField = MQ.MathField(element, { language: 'es-MX' }); // Uses 'es'

// Unsupported languages fall back to English with a console warning
var mathField = MQ.MathField(element, { language: 'fr' }); // Falls back to 'en', logs warning
```

The fallback system works for both initial configuration and runtime language changes:

```js
mathField.config({ language: 'fr-CA' }); // Falls back to 'en', logs warning
```

### Examples

```js
// English math field (default)
var englishField = MQ.MathField(document.getElementById('english-math'));

// Spanish math field
var spanishField = MQ.MathField(document.getElementById('spanish-math'), {
language: 'es'
});

// Global default language
MQ.config({ language: 'es' }); // All new fields will default to Spanish
```

### Localization API

MathQuill provides a localization API accessible through `MQ.L10N` for working with languages and translations:

```js
// Check if a language is supported
var isSupported = MQ.L10N.isLanguageSupported('es'); // true

// Resolve language codes (handles variants like 'en-US' → 'en')
var resolved = MQ.L10N.resolveLanguage('en-US'); // returns 'en'

// Set global default language for new MathQuill instances
MQ.L10N.setLanguage('es');

// Get current global default language
var currentLang = MQ.L10N.getLanguage(); // returns current language

// Listen for global language changes
var unsubscribe = MQ.L10N.onLanguageChange(function() {
console.log('Language changed to:', MQ.L10N.getLanguage());
});

// Later, unsubscribe from changes
unsubscribe();

// Create a localization instance for custom use (advanced)
var localization = MQ.L10N.create('es');
var translation = localization.formatMessage('plus'); // 'más'
```

#### MQ.L10N.onLanguageChange(callback)

Registers a callback function that will be called whenever the global default language changes. This is useful for updating UI elements or re-rendering content when the language changes.

```js
// Listen for language changes
var unsubscribe = MQ.L10N.onLanguageChange(function() {
// This will be called whenever MQ.L10N.setLanguage() is called
var newLanguage = MQ.L10N.getLanguage();
console.log('Language changed to:', newLanguage);

// Update your UI or re-render content as needed
});

// To stop listening for changes, call the returned function
unsubscribe();
```

The callback receives no arguments. Use `MQ.L10N.getLanguage()` to get the current language within the callback.

**Returns:** A function that can be called to unregister the callback.

## .dropEmbedded(pageX, pageY, options) **[ᴇxᴘᴇʀɪᴍᴇɴᴛᴀʟ](#note-on-experimental-features)**

Insert a custom embedded element at the given coordinates, where `options` is an object like:
Expand Down
32 changes: 32 additions & 0 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ The configuration options object is of the following form:

```js
{
language: 'en',
spaceBehavesLikeTab: true,
leftRightIntoCmdGoes: 'up',
restrictMismatchedBrackets: true,
Expand Down Expand Up @@ -32,6 +33,35 @@ Defaults may be set with [`MQ.config(global_config)`](Api_Methods.md#mqconfigcon

# Configuration Options

## language

The `language` option sets the language for mathematical speech and accessibility features. This affects how screen readers and other accessibility tools announce mathematical expressions.

**Type**: `string`
**Default**: `'en'`
**Supported values**: `'en'` (English), `'es'` (Spanish)

```js
// Set language when creating a MathField
var mathField = MQ.MathField(element, { language: 'es' });

// Change language later
mathField.config({ language: 'en' });

// Set global default language
MQ.config({ language: 'es' });
```

### Language Features

The language setting affects how mathematical expressions are read aloud by screen readers. Operators (`+`, `-`, `=`), inequalities (`<`, `≤`), fractions (`1/2` becomes "1 half"), powers (`x^2` becomes "x squared"), mathematical structures (roots, summations), and function names (`sin`, `log`) are all localized. For example, `x^2 = 1/2` reads as "x squared equals 1 half" in English or "x al cuadrado igual 1 medio" in Spanish.

### Language Validation

- When creating a MathField with an invalid language code, an error is thrown
- When using `.config()` to change language, invalid codes fall back to English with a console warning
- Language codes are case-insensitive and support fallbacks (e.g., `'en-US'` falls back to `'en'`)

## spacesBehavesLikeTab

If `spaceBehavesLikeTab` is true the keystrokes `{Shift-,}Spacebar` will behave like `{Shift-,}Tab` escaping from the current block (as opposed to the default behavior of inserting a Space character).
Expand Down Expand Up @@ -84,6 +114,8 @@ Just like [`autoCommands`](#autocommands) above, this takes a string formatted a

autoOperatorNames can also accept a speech-friendly alternative for each operator name. This will get read out by screenreaders in place of the raw command. To specify a speech-friendly-alternative, add a `|` character after the command, and then add the speech-friendly-alternative as a string, with spaces replaced by `-`. E.g. `stdev|standard-deviation`.

Built-in operator names are automatically localized for screen readers based on the [`language`](#language) setting. For example, `sin` is read as "sine" in English or "seno" in Spanish. Custom speech alternatives using the `|` syntax take precedence over automatic localization.

## infixOperatorNames

`infixOperatorNames` specifies a set of operator names that should be treated as infix operators, for example for determining when to stop scanning left before a fraction.
Expand Down
Loading