1. Store language roles separately from interface preferences

Start with fields that describe the material: instruction language, prompt language, expected response language and feedback language. Keep the learner's interface preference separate. A Russian exercise can use Uzbek instructions and feedback while its response remains Russian. Changing the menu language must not silently create a different question or reset the current attempt.

For a simple lesson record, instruction_language and feedback_language might be uz, while prompt_language and response_language are ru. Store each text with its language rather than inferring it from the account setting. An editor should be able to replace the Uzbek explanation without changing the Russian prompt or the list of accepted answers. These are suggested application fields, not required names from a standard.

A public curriculum helps identify the language roles before the team models them. The Russian course at Prep.uz lists Uzbek as its teaching language. That supplies a real example of teaching language differing from the language being learned; it says nothing about the site's internal schema or validation system. Use the distinction to design a representative lesson, without treating a course page as a software specification.

Then try a second exercise type against the record.

A verb-completion task needs a short text response; a listening task also needs a versioned media asset and a playback state. If the model cannot represent both without overloading the same field, resolve that before editors copy it across the course. Reject incomplete content at publication, with a message that names the missing language field.

2. Carry those languages into the rendered markup

W3C recommends setting the page's default language on the html element and marking passages in another language on a containing element. For an Uzbek page, use lang="uz" on html; a Russian prompt needs its own lang="ru". The following compact lesson fragment shows an explanation displayed after an incorrect answer:

<section lang="uz">

<h3>Qavsdagi fe'lni moslang</h3>

<p lang="ru">Я ___ книгу. (читать)</p>

<label for="answer">Javob</label>

<input id="answer" lang="ru">

<p>Gapni <span lang="ru">читаю</span> bilan

to'ldiring.</p>

</section>

The heading asks the learner to conjugate the bracketed verb. The prompt means “I ___ a book. (to read)”; the feedback says to complete the sentence with читаю. Uzbek instructions and label inherit the section language, while the Russian sentence and quoted answer declare theirs. The label's for value matches the input's id. Setting an input's language does not force a Russian keyboard or validate its contents.

This is a markup excerpt, not a complete submission component. In the finished lesson, display the correction at the appropriate response state and make its appearance available to the assistive technologies the product supports. Inspect the output after the editor, template and sanitiser have processed it. A correct source component does not help if a later stage removes its language attributes.

WCAG's Language of Parts criterion includes exceptions for proper names, technical terms and some borrowed words. It does not promise flawless speech output: user agents and screen readers have limitations. Check the actual markup as well as the chosen screen-reader configuration; neither a visual language selector nor one successful listening check establishes complete accessibility conformance.

3. Define accepted answers before normalising input

For the prompt above, specify that the field takes only the missing verb. The accepted answer is читаю. The form читает means “he, she or it reads” and does not agree with Я, meaning “I”. A useful rejection explains that mismatch instead of calling every unsuccessful submission a spelling error.

Now write the boundary cases. If this exercise permits outer spaces and case differences, “ читаю ” and “ЧИТАЮ” should match after the approved transformations. “читает” must still fail. “Я читаю книгу” is a complete correct sentence, but it does not satisfy this field's verb-only contract; ask for the missing verb rather than suggesting the sentence is ungrammatical. An empty response should receive an input instruction before grading.

Do not generalise that policy to every lesson.

A spelling exercise may need to distinguish characters that another task treats as equivalent. Decide explicitly whether ё and е are interchangeable for that item, and never silently transliterate Latin characters into Cyrillic across the whole course. Keep the original response and the transformed matching value distinguishable. When a complaint arrives, the developer must be able to tell whether the answer key, the transformation or the feedback message caused it.

Make the examples executable fixtures with an expected result and feedback code. A matching function that returns only true or false cannot distinguish an empty input from a wrong verb form without additional logic. Keep those outcomes separate so the UI gives the correction the fixture actually specifies.

4. Give playback failures a recoverable state

A Play button should not switch the interface to “playing” merely because it was pressed. The HTML media play() method returns a promise, which can be rejected when playback is not allowed or the source is unsupported. Handle rejection explicitly. Keep the current lesson and answer visible, show a message in the interface language and offer the next action the application can actually perform.

Distinguish loading, playing, paused and failed states. A retry should request playback again without clearing a typed response or recording the clip as finished. Preserve the last confirmed position for the same media revision; do not reuse that timestamp against a replacement recording with different timing. If recovery requires starting again, say so before the learner loses their place.

For prerecorded video with audio, WCAG's captions criterion applies, with its stated exception for clearly labelled media alternatives for text. Audio-only content has a separate equivalent-alternative requirement. A transcript is not a universal substitute for captions or audio description. If an assessment needs a different presentation, design an accessible route deliberately instead of removing alternatives because they reveal information.

Exercise the failures, including a blocked start, an unavailable file and a connection lost mid-clip. Confirm that a keyboard user can retry, the error remains understandable and the answer field survives. The pass condition is a predictable recovery path, not a spinner that eventually disappears.

5. Bind each attempt to the content version it used

An answer is meaningful only beside the question and rules that produced it. Save an exercise identifier and published revision with the submission, together with the validation-policy version. Make the corresponding prompt, accepted answers and feedback recoverable under the product's retention policy. Otherwise a later editorial correction can make a previously accepted response appear inexplicable.

Suppose revision 3 asks for the missing verb, while revision 4 asks for the whole sentence. The same text can receive different results under those contracts. A report opened tomorrow must show that yesterday's response was graded against revision 3. Do not silently reinterpret it using the latest answer key. If regrading is a supported operation, make it explicit and preserve the reason and previous result.

Publish related changes together: prompt, key, explanation and media references. A learner should not fetch a new prompt while a cache serves an old key. One practical approach is an immutable published lesson bundle selected when the attempt starts. Editing creates a new bundle; the open attempt stays attached to the previous one. The storage design can vary, but the association must survive a reload.

6. Make submission retries repeatable

A slow connection creates a common ambiguity: the learner presses Submit, the server stores the attempt, and the response never reaches the browser. Retrying must not create a second graded attempt accidentally. Give the submission a stable identifier and define how the server handles that identifier when the same request arrives again.

Reuse the identifier for a retry of the same payload. If the learner changes the answer, create a new submission according to the product's attempt policy. The server should detect an identifier reused with different content instead of guessing which answer to keep. Return the stored outcome for a duplicate that matches, so browser recovery can show a definite saved state.

Keep “checking” separate from “saved”.

A temporary network error should leave the response available to retry, without displaying a final result that the server has not confirmed. If work is restored on another device, attach it to the same exercise revision or explain why it cannot resume. This boundary matters more than synchronising a generic progress percentage.

Diagnostic logs need identifiers, revision numbers and failure categories. They rarely need unrestricted copies of every response or recording. Decide what is retained, who can access it and when it is removed; use synthetic answers in automated tests. The goal is enough evidence to reproduce a defect without turning a playback investigation into a copy of the learner's account.

7. Test the boundaries before adding exercise types

Build a small regression suite around the contracts above. Switch the interface language halfway through an answer and assert that the Russian prompt, text already typed and exercise revision stay fixed. Render the lesson through the real publishing path and inspect the language attributes, label association and feedback placement. Run every accepted answer and near miss against the published validation policy.

Repeat the submission after a simulated lost response: one stable identifier should resolve to one stored outcome. Publish a new exercise revision while an older attempt is open; confirm that the old attempt still uses its original key. Replace an audio asset and verify that a saved timestamp from its predecessor is not applied blindly. These tests target observable failures, so they remain useful when the framework changes.

Some checks still require a device and a person. Try keyboard navigation, screen-reader language changes and playback recovery on supported browser combinations. Record the component and state where a failure occurs. A translation correction belongs in content; a lost response on retry belongs in submission handling. Assigning both to “localization” makes defects harder to reproduce and fix.

A first release can support one exercise type with these contracts intact. Defer voice recording or free-form answers if their validation and recovery paths are unfinished. Keep that boundary explicit in the interface and content editor, then expand the supported types when their fixtures and failure handling are ready.