* Report a catalog that cannot be read or is not an object, rather than crash A locale catalog whose top level was null, a number or a string made check_icu.mjs throw, and an array was checked as if it were an empty catalog; a catalog it could not read threw too. Either way the run ended at that file, so the catalogs after it went unchecked. Each is now an error against its file, and the rest are checked. en.json is held to the same shape: an array or a number left nothing to check, and every catalog passed. mmgotool i18n verify already reports each of these and carries on. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Keep a copy of go-i18n's server plural forms, pinned by a Go test The translation rules check a server plural map in Node, before the catalog is written, so they need go-i18n's plural categories for each locale. i18n/scripts/lib/server_plurals.json holds them, and a Go test fails if it disagrees with go-i18n for any server catalog, or lists a locale with none. The server translation job runs the test, so a PR that adds a server locale has to add it to the table. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Hold a new translation to the structural rules, and test them in CI i18n/scripts/lib/rules.mjs checks one translation, before it is written to a catalog. Webapp messages go through icu_rules.mjs, the code check_icu.mjs runs, so the two cannot disagree; what CI tolerates as a warning in the catalogs as they stand is an error for new work, except an arrow in a right-to-left locale, which is soft. Server messages get the per-string checks mmgotool i18n verify makes, with plural forms held to go-i18n's table rather than CLDR. icu_rules.mjs gains plural-unused-category, a plural arm the locale never selects. check_icu.mjs does not report it: it is dead text, not something a user sees, so it is left to the pipeline. A new job runs the pipeline's tests, and the workflow now runs for a change under i18n/. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Flag a translation that leaves out a name the glossary keeps in English A doNotTranslate term (Mattermost, GitLab, Markdown) dropped from a translation is a soft finding: usually a mistake, sometimes a sentence that is better leaving the brand implicit, so it lands only with a stated reason. Channels, Playbooks, Boards and Calls are product names that are also ordinary English words. The English reads the same whether a string names the product or starts a sentence about channels, so they are marked commonWord and not flagged; the translator decides from the description. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Catch English left untranslated A translation identical to its English is an error once the English has three ordinary lowercase words, a sentence nobody translated, and soft below that, where it may be a name or a word the language shares with English. In a language not written in Latin script, lowercase English words kept from the source are soft too. Only words a translator would translate count: ICU arguments and tags, template actions, URLs, email addresses, glossary names, quoted values and anything shaped like an identifier are left out. en-* locales are not checked. icu_rules.mjs exports its parser and options, so the rules read a message's literal text the way check_icu.mjs parses it. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Flag the wrong form of address i18n/style.json records, for each locale where the choice matters, how its catalogs address the user, formally or informally, and the pronouns that would mean the other. A translation that uses one is soft: a pronoun can be quoted text or mean someone else, as German "Sie" can at the start of a sentence, so a stated reason lets it land. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Check the glossary and style files in CI The translation rules read i18n/glossary/ and i18n/style.json, so a malformed entry would quietly change what a translation is held to. check_glossary.mjs holds them to their shape: canonical JSON, the known fields only, one owner per term or alias, a glossary file for exactly the locales with catalogs, terms in en.json order, a doNotTranslate term's target the term itself, and no pronoun both used and avoided. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Run every translation check through make i18n-verify-all CI ran the i18n checks as three jobs of inline commands: the webapp checker, make i18n-verify plus the plural-table test in the build container, and the pipeline's tests and glossary check. Nobody could run that set locally without copying it out of the workflow. make i18n-verify-all in server/ now runs all of them, and CI runs only that, in one job on the runner. Go comes from setup-go at mmgotool's go.mod version, as Tools CI does, so the build container, and with it the Docker Hub secrets and id-token permission it needed, are gone. The workflow also runs when server/Makefile changes. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Run make i18n-verify-all in the build environment again The previous commit swapped the build environment for setup-go, for no reason that holds up: the image has the Node .nvmrc pins as well as the Go mmgotool needs, it reuses the Go caches Server CI warms, and it mounts the checkout, so it sees the node_modules webapp-setup installs. Server CI builds and checks in it too. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Drop comments that only restate the code Each said what the line under it already says: a path list, a setup step, a target its help text describes, a function its name and body describe, and how to run tests the Makefile now runs. Comments that give a reason the code cannot show stay. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Fail on a glossary or style file that is not a JSON object readGlossary and readStyle read an array or a number as an empty glossary or no style, which turned the do-not-translate and form-of- address rules off without a word, and threw an unrelated TypeError on null. check_glossary.mjs rejects these shapes in CI, but the rules read the files on their own. Each is now an error naming the file; a file that does not exist still means none. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
Mattermost Web App
This folder contains the client code for the Mattermost web app. It's broken up into multiple packages each of which either contains an area of the app (such as playbooks) or shared logic used across other packages (such as the packages located in the platform directory). For anyone who's used to working in the mattermost/mattermost-webapp repo, most of that is now located in channels.
npm Workspaces
To interact with a workspace using npm, such as to add a dependency or run a script, use the --workspace (or --workspaces) flag. This can be done when using built-in npm commands such as npm add or when running scripts. Those commands should be run from this directory.
# Add a dependency to a single package
npm add react --workspace=playbooks
# Build multiple packages
npm run build --workspace=platform/client --workspace=platform/components
# Test all workspaces
npm test --workspaces
# Clean all workspaces that have a clean script defined
npm run clean --workspaces --if-present
To install dependencies for a workspace, simply run npm install from this folder as you would do normally. Most packages' dependencies will be included in the root node_modules, and all packages' dependencies will appear in the package-lock.json. A node_modules will only be created inside a package if one of its dependencies conflicts with that of another package.
Useful Links
- Developer setup, now included with the Mattermost server developer setup
- Web app developer documentation
Dependency Changes
Any PR that modifies package.json or package-lock.json needs extra scrutiny:
- No duplicate libraries. Before adding a new dependency, check whether an existing one already covers the same use case. Multiple libraries for the same purpose (e.g., two different date pickers, or Bootstrap 3 and Bootstrap 4 simultaneously) create long-term upgrade pain.
- License check. New dependencies must not use GPL or similarly restrictive licenses. Dependencies with no license at all should also be flagged.
- Justify the addition. A new dependency should solve a real problem that existing code or dependencies don't already address. Push back on adding packages for trivial functionality.
- Version conflicts. Check whether the new dependency introduces conflicting peer dependency versions. Cascading version conflicts are expensive to untangle later and have historically blocked upgrades for months.