-
Notifications
You must be signed in to change notification settings - Fork 222
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
base: dev
Are you sure you want to change the base?
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
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 106 out of 124 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/routes/devices.$id.settings.macros.edit.tsx:1
- The 'text' prop appears to be missing its value. This should likely be
text={m.macros_delete_macro()}
.
import { useState, useEffect } from "react";
ui/src/routes/devices.$id.settings.macros.edit.tsx:1
- Line 99 shows incomplete 'text' prop assignment. The line should include the complete prop assignment but appears to be cut off or malformed.
import { useState, useEffect } from "react";
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
I think this is ready for testing, the Update page is still borked, but will fix that ASAP. You can use a cookie-editor to switch languages until someone weighs in on how we want to present the UI language switcher on the client |
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 * 9
was 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.valid
was 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.
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
9e9311c
to
3391fa6
Compare
Rebased to pull in the new sleep mode settings. |
Thanks CoPilot!
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/messages
directory, 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 test
on 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 i18n
to 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 test
in 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-translate
to 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-translate
to do an initial pass at localizing all existing messages to the new language.Other notes
npm run i18n:validate
to ensure that language files and settings are well-formed.npm run i18n:find-excess
to look for extra keys in other language files that have been deleted from the master-list in en.json.npm run i18n:find-dupes
to look for multiple keys in en.json that have the same translated value (this is normal)npm run i18n:find-unused
to look for keys in en.json that are not referenced in the UI anywhere.npm run i18n:audit
to do all the above checks.