-
Notifications
You must be signed in to change notification settings - Fork 236
Localize the client/browser UI with inlang paraglide-js #864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
cabdfe4 to
d81a497
Compare
f383397 to
cd9a55d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces internationalization (i18n) support to the JetKVM UI using the inlang paraglide-js localization framework. The implementation enables multilingual support with translations for 9 languages and includes a comprehensive setup for managing localized strings throughout the React frontend.
- Added inlang paraglide-js as the localization framework with configuration for 9 languages (en, da, de, es, fr, it, nb, sv, zh)
- Replaced hardcoded UI strings with localized message functions across multiple components
- Updated build configuration and TypeScript paths to support the new localization structure
Reviewed Changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/vite.config.ts | Added paraglide Vite plugin configuration for localization build integration |
| ui/tsconfig.json | Updated with new path aliases and compiler options for localization support |
| ui/tsconfig.node.json | Simplified configuration by removing redundant options |
| ui/package.json | Added inlang dependencies and updated scripts to include localization compilation |
| ui/src/ components and routes | Replaced hardcoded strings with localized message functions |
| ui/localization/ | Added complete localization setup with message files for 9 languages |
| ui/index.html | Minor formatting and path corrections |
| ui/eslint.config.cjs | Updated import resolution mapping for new path aliases |
Files not reviewed (1)
- ui/package-lock.json: Language not supported
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
cd9a55d to
7ea1942
Compare
9aa9b44 to
985b53c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 89 out of 108 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- ui/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)
ui/src/routes/devices.$id.tsx:1
- Corrected 'server' to 'serve' in comment"
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
ui/src/hooks/hidRpc.ts:1
- [nitpick] Variable declaration moved inside the loop when it could be declared outside for better readability. Consider moving the offset calculation before the macroBinary creation."
import { hidKeyBufferSize, KeyboardLedState, KeysDownState } from "./stores";
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
a61b707 to
5363baa
Compare
f4b3053 to
83d544f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 109 out of 126 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- ui/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)
ui/src/hooks/hidRpc.ts:1
- Moving the offset calculation outside the loop and incrementing it manually could lead to incorrect offsets if the loop logic changes or if steps have variable sizes. The original approach of calculating
offset = 6 + i * 9was more robust and less error-prone.
import { hidKeyBufferSize, KeyboardLedState, KeysDownState } from "./stores";
ui/src/routes/devices.$id.mount.tsx:1
- The URL validation logic now runs in useEffect on every URL change, which could cause unnecessary re-renders. The original inline validation approach
urlRef.current?.validity.validwas more efficient as it only checked validity when needed during render or form submission.
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
83d544f to
beb7bde
Compare
bcdeae4 to
c6d4fff
Compare
|
I've got the documentation created and all the remaining translations I missed (I think?) done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 114 out of 132 changed files in this pull request and generated 6 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
9e9311c to
3391fa6
Compare
|
Rebased to pull in the new sleep mode settings. |
Remove the temporary directory after extracting buildkit Localize the extension popovers. Update package and fix tsconfig.json Expand development directory guide Move messages under localization Popovers and sidebar Update Chinese translations Accidentally lost the changes that @ym provided, brought them back File formatting pass Localized all components, hooks, providers, hooks Localize all pages except Settings Bump packages Settings Access page Settings local auth page Fix ref lint warning Settings Advanced page Fix UI lint warnings there were a bunch of ref and useEffect violations. Settings appearance page Settings general pages Settings hardware page Settings keyboard page Settings macros pages Settings mouse page Settings page Settings video page Settings network page Fix compilation issues Ran machine translate Use getLocale for date, relative time, and money formatting Fix eslint Delete unused messages Added setting to choose locale Merged in dev hotfix Fix update status rendering Add note to do back-translation Improve developer guidance Clean up some localization issues and extract UpdatingStatusCard Fix copy-pasta errors Packages update Fix silly error in progress messages.
1eb4875 to
d3b0f1b
Compare
0e5a4e8 to
ca1c36b
Compare
OTA supplies port-reboot action to handle the rebooting device. Make sure we force-reload the page after redirection. Move reboot logic into hw.go and make it set the willReboot message with parameters if provided. Improve logging consistency.
ca1c36b to
f735f57
Compare
Localization
The browser/client frontend uses the paraglide-js plug-in from the inlang.com project to allow compile-time validated localization of all user-facing UI strings in the browser/client UI. This includes
title,text,name,description,placeholder,label,aria-label, message attributes (such asconfirmText,unit,badge,tag, orflag), HTML element text (such as<h?>,<span>, or<p>elements), notifications messages, and option label strings, etc.We do not translate the console log messages, CSS class names, theme names, nor the various value strings (e.g. for value/label pair options), nor URL routes.
The localizations are stored in .json files in the
ui/localizations/messagesdirectory, with one language-per-file using the ISO 3166-1 alpha-2 country code (e.g. en for English, de for German, etc.)m-function-matcher
The translations are extracted into language files (e.g. en.json for English) and then paraglide-js compiles them into helpers for use with the m-function-matcher. An example:
shakespere plug-in
If you enable the Sherlock plug-in, the localized text "tooltip" is shown in the VSCode editor after any localized text in the language you've selected for preview. In this image, it's the blue text at the end of the line :
Process
Localizing a UI
Locate a string that is visible to the end user on the client/browser
Assign that string a "key" that reflects the logical meaning of the string in snake-case (look at existing localizations for examples), for example if there's a string
This is a teston the thing edit page it would be "thing_edit_this_is_a_test"Add the key and string to the en.json like this:
{ }around the replacement token (e.g. This is your name: {name}). An complex example:Save the en.json file and execute
npm run i18nto resort the language files, validate the translations, and create the m-functionsEdit the .tsx file and replace the string with the calls to the new m-function which will be the key-string you chose in snake-case. For example
This is a testin thing edit page turns intom.thing_edit_this_is_a_test()I will call you {name}, usem.profile_i_will_call_you({ name: edit.value })When all your strings are extracted, run
npm run i18n:machine-translateto get a first-stab at the translations for the other supported languages. Make sure you use an LLM (you can use aifiesta to use multiple LLMs) or a translator of some form to back-translate each new machine-generation in each language to ensure those terms translate reasonably.Adding a new language
"locales"and the"languageTags"section (inlang and Sherlock aren't exactly current to each other, so we need it in both places)."en"because this project started out in English. Do NOT change that.npm run i18n:machine-translateto do an initial pass at localizing all existing messages to the new language.Other notes
npm run i18n:validateto ensure that language files and settings are well-formed.npm run i18n:find-excessto look for extra keys in other language files that have been deleted from the master-list in en.json.npm run i18n:find-dupesto look for multiple keys in en.json that have the same translated value (this is normal)npm run i18n:find-unusedto look for keys in en.json that are not referenced in the UI anywhere.npm run i18n:auditto do all the above checks.