A local, browser-based editor for Flutter Application Resource Bundle (.arb) files and JavaScript i18n.js translation dictionaries. Compare every language for a message in one workspace and save translations directly to your project.
Install Node.js 22 or newer and install dependencies once. Acorn parses JavaScript without executing it; no build step is required.
npm install
npm startOpen http://127.0.0.1:4310 in your browser. Always use this server URL for real projects. Enter the absolute path to a Flutter project (the directory containing pubspec.yaml) or directly to an i18n.js file. Choose Browse… to select a project folder or an i18n.js file. The folder picker starts at the entered path or the server user’s home folder, with Home, Up, and a path field for navigation. Use this folder fills in the project path when pubspec.yaml is present; then choose Open workspace. Browsing uses the server computer’s folders, including over the LAN, and requires sign-in when a password is configured. It lists visible subfolders and i18n.js files; selecting a file fills in its absolute path. Hidden folders and folder symlinks can be reached by entering their absolute path. To use a different port:
PORT=4400 npm startTwo editable demos are included in this repository:
| Demo | Files | Languages | Path to open |
|---|---|---|---|
| Flutter ARB project | fixtures/demo, with translations in lib/l10n | English, German, French, Polish | Absolute path to the fixtures/demo folder |
| JavaScript translations | fixtures/web/i18n.js | English, German, Polish | Absolute path to the fixtures/web/i18n.js file |
For this checkout, paste either of these paths into the welcome screen and choose Open workspace:
/Users/user_name/dev/flutter/arbeditor/fixtures/demo
/Users/user_name/dev/flutter/arbeditor/fixtures/web/i18n.js
If you clone the editor elsewhere, replace /Users/user_nam/dev/flutter/arbeditor with the absolute path to your checkout. Both demos work without a Flutter SDK; only the Node server is needed.
The ARB demo includes descriptions, placeholders, multiline text, and ICU plural messages. The JavaScript demo includes missing translations, escaped strings, and a {name} placeholder. Try Incomplete to find missing messages, or search for Cześć with Full search enabled to find a Polish translation while English is the reference.
Sample files are real files: saving, adding, renaming, or deleting entries updates the fixtures on disk. Copy a demo first if you want to keep its original contents.
Enter the full absolute file path, including i18n.js, in the welcome screen. For the NetCam web UI:
/Users/user_name/dev/flutter/project_camera_control/camera_control/assets/web/i18n.js
Choose Open workspace. A file opens independently of its Flutter project's ARB files; pubspec.yaml is not required. Paths always refer to the computer running this editor's server. Recent workspaces remember the full file path.
Supported files declare one var, let, or const messages object with language dictionaries containing string literals:
var messages = {
en: { login_title: 'Login', selected_count: '{count} selected' },
de: { login_title: 'Anmeldung' },
pl: { login_title: 'Logowanie' }
};All languages appear together in the existing editor. English is the initial reference when present. Search, Incomplete, reference selection, Undo, navigation, and Save work as for ARB files. Placeholder chips show {name} tokens from the reference text. New entry adds a key to every language; Rename and Delete affect every language containing the key. Descriptions are available only for ARB files. Language dictionaries remain fixed; the editor does not add languages or change runtime language lists.
The JavaScript is parsed as source and never executed. Dynamic translation values, template literals, spreads, methods, computed keys, duplicate properties, and __proto__ properties are rejected with a validation error. Files are limited to 200 languages and 5 MiB.
Text-only saves replace the changed string literals, retaining comments and other formatting. Adding, renaming, or deleting keys rewrites the affected language objects with quoted keys and strings; comments inside those rewritten objects are removed. The surrounding script, other language objects, BOM, newline style, trailing newline, and permissions are preserved. Changes to several languages are staged and replaced as one file. External edits and stale revisions block saves and leave your draft available.
See Demo fixtures for the included JavaScript sample and its absolute file path.
A password is optional. Pass options through npm after --:
npm start -- --password 'choose-a-long-password'The browser asks you to sign in before it can read recent projects, open files, add messages, or save translations. Sign out revokes the current browser session and resolves unsaved edits first. Sessions expire after eight idle hours or when the server restarts. Failed sign-ins are limited to ten attempts per client IP in fifteen minutes; forwarded IP headers are not trusted. Passwords are checked against a salted scrypt hash in memory and are never stored in history or sent to the browser.
To listen on your local network:
npm start -- --host 0.0.0.0 --password 'choose-a-long-password' --sslGenerate a certificate first, as described below. Open https://YOUR_SERVER_LAN_IP:4310 on another computer. The startup output lists available LAN URLs. Project paths refer to folders on the server computer. The server accepts its interface IPs and computer hostname; add a DNS alias explicitly with --allow-host arb.local if needed. Host allowlisting and same-origin checks remain enabled.
For secrets supplied through your shell environment or service configuration, use ARB_PASSWORD instead of a command-line password. Command-line arguments may be visible in shell history and process listings. HTTP password mode provides an access gate but does not encrypt traffic; use HTTPS for LAN connections. This is a shared-password editor for a trusted LAN: anyone with the password can access projects allowed by the server process’s filesystem permissions. It is not a multi-user or Internet-facing service.
The server supports native HTTPS with a PEM private key and certificate chain. It requires TLS 1.2 or newer and marks HTTPS session cookies Secure.
Create a self-signed development certificate using OpenSSL:
npm run ssl:generate
npm start -- --ssl --password 'choose-a-long-password'The helper creates .certs/key.pem and .certs/cert.pem, valid for 365 days, with SANs for localhost and the computer’s current IP addresses and hostname. Private-key permissions are restricted on Unix systems, and certificate files are ignored by Git. Existing files are never overwritten. Include a DNS alias or a future LAN address with:
npm run ssl:generate -- --host arb.local --host 192.168.1.25A self-signed certificate is not automatically trusted by other devices. For browser HTTPS without certificate warnings, use a certificate issued by a trusted CA, or explicitly trust your generated certificate through each client’s OS/browser certificate settings. Connect using a hostname or IP included in the certificate. If your LAN IP changes, generate a new certificate into a different directory using --out and select it with the file options below.
To use existing certificate files:
npm start -- --host 0.0.0.0 --password 'choose-a-long-password' \
--ssl-key /absolute/path/key.pem --ssl-cert /absolute/path/cert.pemSpecifying both files enables HTTPS automatically. Missing, unreadable, or invalid certificate files fail startup; the server does not fall back to HTTP. The key must be an unencrypted PEM key, and the certificate must match it. No certificates are installed into your system trust store automatically.
| Option | Environment variable | Default |
|---|---|---|
--password <password> |
ARB_PASSWORD |
No password |
--host <IP or localhost> |
ARB_HOST |
127.0.0.1 |
--port <number> |
PORT |
4310 |
--ssl |
— | HTTP |
--ssl-key <file> |
ARB_SSL_KEY |
.certs/key.pem with --ssl |
--ssl-cert <file> |
ARB_SSL_CERT |
.certs/cert.pem with --ssl |
--allow-host <name> (repeatable) |
— | Localhost; server interface addresses and hostname in LAN mode |
--help |
— | Display startup help |
HTTPS uses the built-in Node HTTPS server; password verification uses Node’s scrypt and timingSafeEqual APIs. Certificate generation uses OpenSSL req.
- Use Dark mode / Light mode in the header to switch themes. The browser remembers your choice across projects and reloads. Until you choose a theme, the editor follows your system appearance. Switching themes preserves unsaved edits and undo history.
- Choose Hide keys beside Save to hide the translation-key panel and give the language editor more space. Show keys restores the panel with its search, filters, and New entry button. Entry details and Save/Undo stay visible, and Previous/Next navigation still works. The browser remembers this preference across projects and reloads; toggling preserves unsaved edits and undo history.
- Choose the Reference language above the translation cards. It appears first, supplies sidebar previews and source-text search, and is remembered per project in this browser. This preference does not modify
l10n.yaml. Source descriptions and placeholders remain available when the selected reference has no metadata. - Switch between Flow (wrapping language cards) and List (one full-width card per row). The browser remembers your layout preference. Changing reference or layout preserves unsaved text and undo history.
- Select a message in the left column to see all languages together. Country flags are visual locale hints; language names and locale codes identify each file precisely.
- Use New entry in the sidebar to enter a key and reference text. Creating it adds the key to every language, with the reference text in your selected language and empty strings in the other languages. For ARB files, an optional description is stored as metadata in each file; JavaScript entries have no description field. The new key opens in the editor immediately. Keys must start with an ASCII lowercase letter and contain only
A-Z,a-z,0-9, or underscores (up to 200 characters). Spaces and non-ASCII characters are rejected; surrounding spaces are not silently trimmed. The dialog checks for clashes with messages and metadata across every language as you type, and the server checks again before writing. Unsaved edits are resolved before the creation dialog opens. - Search keys and reference text. Check Full search below the search field to search translation text in every language, including messages missing from the reference language. Search is case-insensitive. Enable Incomplete to find missing or empty translations; it also works with Full search. Keys from every language are included, even when absent in the reference file.
- Use Previous entry and Next entry beneath the language cards to move through the keys currently shown in the sidebar. Search and the Incomplete filter apply to this navigation, and buttons are disabled at either end. Unsaved edits prompt you to save, discard, or stay. After navigation, the editor returns to the top of the selected entry.
- Use Rename entry beneath the selected key to change its name across all languages. Translations and ARB
@keymetadata move together; missing translations remain missing. The new name must follow the same rules as a new entry and cannot clash with an existing message or metadata in any language. Update references in your Flutter or JavaScript code afterward. - Delete entry asks for confirmation before removing the selected key and its metadata from all language files. Unsaved edits are resolved before either dialog opens. Rename and delete write immediately and cannot be undone with the editor’s Undo button; use version control to recover saved changes.
- Text areas grow to fit the entire message, including multiline text. Regional locales and right-to-left text are supported.
- Placeholder chips and the source description provide context. Keep ICU syntax and placeholder tokens intact; the editor treats translations as text and does not validate ICU grammar.
- Save changes writes the changed languages for the selected key. Missing entries are added when you enter a translation. ARB metadata (
@message,@@locale, and other@@fields) and unrelated messages are retained. - Undo steps through unsaved edits for the current key (up to 100 steps). Undo history resets after saving or selecting another key. Native textarea undo is also available.
- Switching keys or closing the project prompts you to save, discard, or stay. Closing a browser tab, refreshing, or navigating away invokes the browser’s native unsaved-change warning; choose to stay and save in the editor first.
- Use Close project to return to the path screen. The eight most recent workspaces, including absolute JavaScript file paths, are stored in
.data/recent.jsonand survive server restarts.
Shortcuts: Ctrl/Cmd + S saves the current message. Ctrl/Cmd + Z uses native undo while typing, or the editor’s undo when focus is outside an input.
When l10n.yaml specifies arb-dir, the editor scans that directory inside your project and uses template-arb-file as the source. Without an explicit directory, it discovers .arb files recursively, excluding generated folders such as build, .dart_tool, Pods, and node_modules. Symlinked entries are skipped. Files may use names other than app_xx.arb; @@locale takes priority over the locale inferred from the filename. The YAML reader supports standard scalar directory/template settings, quoted values, and comments; it does not support YAML aliases or multiline settings.
All files must contain valid JSON objects with string message values. To keep scans bounded, projects are limited to 200 ARB files, 50,000 scanned entries, 16 directory levels, and 5 MiB per ARB file. Configure arb-dir for large projects.
Saving preserves indentation, newline style, a UTF-8 BOM if present, the trailing newline convention, and file permissions. JSON whitespace may be normalized, including inline metadata formatting. Each changed file is staged beside the original and replaced with a rename. If a later file fails, the editor attempts to restore earlier files. A multi-file save cannot be fully atomic across a machine crash; use your project’s version control for backups.
File hashes detect changes made by another editor since the project was opened. A conflict keeps your draft in the UI and blocks the save. Copy the draft text, close and reopen the project to load the latest files, then reapply your changes. There is no automatic overwrite or merge. Missing translations are represented as empty inputs; leaving one untouched does not create an empty entry.
The server binds to 127.0.0.1 by default; LAN access requires an explicit bind option. It accepts allowed hosts and same-origin requests. Files stay on the computer running the server and are sent to authorized connected browsers for editing. No external fonts, analytics, or services are used. Acorn is the only runtime package dependency. Open projects are held in memory and associated with a browser session cookie. Tabs in the same browser share that project session; stale saves are rejected. Stopping the server clears open sessions but preserves recent-project history. Password sessions are held in memory; signing in again after expiry retains the current browser draft and restores the project context. External file changes still block stale saves. Proxy TLS termination and forwarded-host/IP headers are not supported; use the built-in HTTPS listener for LAN access.
npm run dev # Restart server when server-side files change; refresh browser for client changes
npm run check # JavaScript syntax checks
npm test # File-handling and HTTP-handler integration testsThe implementation uses Node’s built-in HTTP server, filesystem APIs, and test runner. The client is plain HTML, CSS, and JavaScript.
server.js HTTP/HTTPS server, sessions, history, security headers
lib/config.js Startup options, host allowlist, TLS file loading
lib/auth.js Password hashing, rate limiting, session lifetime
scripts/generate-cert.js Self-signed certificate helper
lib/projects.js Workspace discovery, revision checks, staged saves
lib/i18n.js Static JavaScript dictionary parsing and source edits
lib/errors.js Shared API validation errors
public/ Browser client
fixtures/demo/ Four-language ARB sample project
fixtures/web/ Three-language JavaScript sample
test/ Automated tests
ARB Studio is licensed under the MIT License. Copyright (c) 2026 Peter Sauer.
The Acorn dependency is also MIT-licensed and retains its own copyright and license notice.
