Skip to content

Translate the Windows installer through Inno .isl files - #15222

Open
rtibbles wants to merge 2 commits into
learningequality:developfrom
rtibbles:i18n
Open

Translate the Windows installer through Inno .isl files#15222
rtibbles wants to merge 2 commits into
learningequality:developfrom
rtibbles:i18n

Conversation

@rtibbles

@rtibbles rtibbles commented Aug 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Removes iss/isl file to po file conversion
  • Uploads them directly to crowdin instead because Crowdin directly supports the files
  • Removes committed official and unofficial innosetup translations
  • Create tooling to properly reference official, unofficial, and in-house translations of innosetup built in text, as well as our custom installer messages

References

Follows: #15079. Original issue: #15073.

Reviewer guidance

Download and run the Windows Installer. The language list should carry all 33 languages; e.g. Ukrainian shows Inno's Ukrainian wizard text with Kolibri's custom messages still in English.

AI usage

Used Claude Code to convert the installer translation pipeline to .isl, generate the [Languages] block from definitions.py, and update the Crowdin config. Verified with the desktop-app and Crowdin config test suites, prek, local review rounds, and a live upload to a Crowdin test branch.

rtibbles and others added 2 commits August 20, 2026 17:04
- .isl is the exchange format both ways now; the PO round-trip and polib go.
- Crowdin gets two sources: custom.isl for Kolibri's own installer strings,
  messages.isl for Inno's stock set — the latter only reaches the languages
  Inno does not translate.
- [Languages] and [LangOptions] are generated from definitions.py before
  iscc runs, so a language chains its own file only once Crowdin delivers
  one and no English copies need committing.
- Inno's official translations resolve through compiler: at compile time;
  only the unofficial ones are fetched, and neither kind is committed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QZSjPGfRnaQoBtcnt3kcS
- messages.isl skips the languages Inno translates, a list generated by
  generate_mapping.py from definitions.py and asserted in the config tests.
- Ignore the in-context pseudo-language where downloads land under
  platforms/; only Kolibri itself and the plugins use it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QZSjPGfRnaQoBtcnt3kcS
@github-actions github-actions Bot added DEV: dev-ops Continuous integration & deployment DEV: backend Python, databases, networking, filesystem... SIZE: very large labels Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@rtibbles
rtibbles marked this pull request as ready for review August 21, 2026 14:39
@rtibbles rtibbles added this to the Kolibri 0.20 milestone Aug 21, 2026
@marcellamaki marcellamaki self-assigned this Aug 25, 2026
@rtibblesbot

rtibblesbot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟡 Waiting for changes

Last updated: 2026-08-26 19:32 UTC

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #15222messages.isl round-trips; CI passing.


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran a phased review pipeline over the pull request diff:

  • Classified the diff to select review passes (core, frontend, backend) and whether manual QA was required
  • Core review pass checked correctness, design, architecture, testing, completeness, and DRY/SRP/Rule-of-Three principles
  • Specialized frontend/backend review passes applied framework-specific lenses where those files changed
  • For UI changes: manual QA and an accessibility audit against a live dev server, when available
  • Checked CI status and linked issue acceptance criteria
  • Synthesized one review from those passes and chose the verdict from the findings, CI status, and QA evidence


def write_languages(languages_path, locale_dir):
lines = [f"; Generated by {Path(__file__).name} — do not edit."]
for locale_code, definition in LANG_DEFINITIONS.items():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: emits ar first, where the replaced block had en. Inno falls back to index 0 (DetermineDefaultLanguage) before the language dialog, so unmatched Windows locales get an Arabic RTL installer. Emit en first, with a test.

chain.append(inno_messages_file(definition["inno_file"]))

if definition.get("translate_messages"):
chain.append(translation_file(locale_code, "messages.isl", locale_dir))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: es-419/pt-mz chain Crowdin over Inno's translation; Crowdin exports untranslated strings as source text by default, swapping Spanish for English.

Comment thread crowdin.yml
# definitions.py and asserted in test_i18n_desktop_crowdin.py; a code the project
# does not have as a target language fails the whole entry, which rules out `ach`
# (the in-context pseudo-language).
{"source": "/platforms/desktop-app/installer/translations/locale/en/custom.isl", "type": "ini", "dest": "/desktop_installer.isl", "update_option": "update_as_unapproved", "translation": "/platforms/desktop-app/installer/translations/locale/%locale%/custom.isl", "languages_mapping": &installer_language_mapping {"locale": {"ach": "ach-ug", "ar": "ar", "bg": "bg-bg", "bn": "bn-bd", "de": "de", "el": "el", "en": "en", "es-ES": "es-es", "fa": "fa", "fr": "fr-fr", "fv": "ff-cm", "gu-IN": "gu-in", "ha": "ha", "hi": "hi-in", "ht": "ht", "id": "id", "it": "it", "ka": "ka", "km": "km", "ko": "ko", "la": "es-419", "mr": "mr", "my": "my", "ny": "ny", "pa-IN": "pa", "pt-BR": "pt-br", "pt-mz": "pt-mz", "sw-TZ": "sw-tz", "te": "te", "uk": "uk", "ur-PK": "ur-pk", "vi": "vi", "yo": "yo", "zh-CN": "zh-hans"}}}, {"source": "/platforms/desktop-app/installer/translations/locale/en/messages.isl", "type": "ini", "dest": "/desktop_installer_messages.isl", "update_option": "update_as_unapproved", "translation": "/platforms/desktop-app/installer/translations/locale/%locale%/messages.isl", "excluded_target_languages": ["ar", "bg", "bn", "de", "el", "es-ES", "fa", "fr", "hi", "id", "it", "ka", "ko", "mr", "pt-BR", "uk", "ur-PK", "vi", "zh-CN"], "languages_mapping": *installer_language_mapping},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: new dest files start empty; pre-translate from TM, archive the .po.


def fetch(path, tag):
url = SOURCE_URL.format(tag=tag, path=path)
with urllib.request.urlopen(url) as response:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: signed builds now fetch unchecksummed text; commit inno/.

# crowdin.yml has to be told separately, and a stale list either bills translators
# for 281 strings Inno already covers or leaves a language without any.
e = _entry("/installer/translations/locale/en/messages.isl")
assert sorted(e["excluded_target_languages"]) == get_installer_excluded_languages()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: a language missing from LANG_DEFINITIONS silently escapes excluded_target_languages.

return "compiler:" + inno_file.replace("/", "\\")


def messages_files(locale_code, definition, locale_dir):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: clean chain abstraction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DEV: backend Python, databases, networking, filesystem... DEV: dev-ops Continuous integration & deployment SIZE: very large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants