LanguageManager is a localization file management plugin with JetBrains IDE split-mode support. It manages JSON, YAML/YML, Laravel PHP, and JetBrains/Java ResourceBundle Properties files. The plugin only processes files that the user explicitly selects in a scheme. It never automatically enrolls or rewrites other project files.
The UI and diagnostics are available in English, Traditional Chinese, Simplified Chinese, Japanese, Korean, Spanish, and Thai. The display language follows the IDE by default and can be overridden in the plugin settings.
- Scheme switching and manual reloads run as cancellable JetBrains background tasks. A newer request supersedes an older load, and stale results are prevented from updating the table or cache.
- Background loading displays an exact dynamic step total covering planning, each language file, table construction, each eligible source file, analysis, and cache writing.
- Open the top-level Analysis Tool Window tab to find likely untranslated hardcoded quoted text under the active scheme. The scan starts only after a manual tab switch or refresh, honors the scheme base path and file/folder exclusions, reuses unchanged file results, and lists every
Value / File path / Line / Collocation with progress, statistics, confidence and multi-select naming-format exclusions, pagination, cancellation, editor navigation, and filtered all-page UTF-8 CSV export. - Create isolated language management schemes from explicitly selected files or one or more folders. Folder mode parses files first, previews recognition results, and lets the user add more folders before confirming the managed files.
- Import and export portable scheme settings as JSON from the Tool Window dropdown. Project paths are converted to relative paths when possible, and every imported file receives a parser and security preview.
- JOIN translations with the same
namespace + keyinto one table row, with a separate column for each locale. - Fuzzy and exact search, locale filtering, missing-translation and zero-usage filters, and pagination limited to 100 rows per page. Reloads and mutation-driven state refreshes retain the current page; changing a search/filter returns to page 1.
- Add or edit every locale value in one scrollable form, save locale mutations as one validated batch, bulk delete, rename keys, merge two translations, copy/paste cells, and launch IDE-native Find in Files. Rename and Merge can synchronize recorded source usages through an editable code Diff; Merge preserves existing target values, fills only missing locales, and supports Laravel nested references such as
admin/customer.keyorpackage::admin/customer.key. - Saving an edited value (quick in-place edit, Edit Selected, or paste) changes only that value inside the file. Comments, blank lines, quoting, indentation, key order, Unicode escapes,
declare(strict_types=1), and every other entry stay byte-for-byte identical, and saving an unchanged value never rewrites the file. Adding or removing keys still renders the whole file. - Batch-translate up to 100 selected rows through an OpenAI-compatible or Anthropic Claude endpoint. The modal defaults editable source text to
enwhen available (otherwise the key), supports multiple target locales, and reviews each target in its own editable column before one combined file Diff. API tokens stay in JetBrains PasswordSafe, and optional Temperature is omitted by default. Only Apply writes files; Give AI More Feedback carries the edited source values, reviewed suggestions, and feedback into a new request round. - Copy selected keys into one target locale's values, and add framework-specific usage Regex recommendations for major PHP frameworks, Spring/Java/Kotlin, ResourceBundle, and IntelliJ Platform plugins. An opt-in Laravel key-only preset can ignore uncertain package/group prefixes such as
filament::components/button.when exact namespace matching is not practical. - Create a complete new locale from an existing locale—for example, copy the key structure from
en/*.phpintoes/*.php—using a freely editable code field and an explicit ISO/BCP 47 suggestion popup. The popup never rewrites text while typing. An optional locale note is saved with the scheme, can be edited later in Scheme Settings → Language notes, and is supplied to AI as language, region, terminology, and tone context; every new file is reviewed in a Diff first. - Configure the plugin display language, issue visibility, and defaults for new schemes in IDE Settings. Existing scheme base paths, Regex patterns, and exclusions are edited independently from the Tool Window.
- Rename the active scheme from Scheme Settings without changing its managed files or isolated identity. Maintain up to 1,000 file/directory exclusions per scheme, bulk-add comma/newline input, or use the mixed multi-selection Choose Files/Folders picker. A bare name matches files or directories at any depth; a relative path targets one file or a directory subtree. The Project file tree shortcut accepts mixed selections and is disabled when no scheme is active.
- Extend an existing scheme at any time with Add Tracked Files: mixed file/folder selections are safely parsed and shown in a selectable recognition table before they are added. Operations → Add Namespace Files can create and track one empty PHP/Properties namespace file per locale; Delete Namespace Files previews and removes that namespace family from disk and tracking as a rollback-capable transaction.
- Turn an editor selection into a managed translation through Localization Manager → Create Translation from Selection. Optional ordered
%key%template conditions scan only the active scheme working directory with its exclusions. A cancellable JetBrains background task and modal progress bar report the current stage, directory/file/match counters, and relative path; the lazy result list leads to a final multi-file confirmation where source-code results are editable and generated language-file changes remain read-only. - Detect parser errors, empty values, duplicate keys, duplicate values, missing locales, and possibly unused keys. Duplicate-value and possibly-unused suggestions can be hidden in settings.
- Accumulate usage occurrences detected by multiple Regex patterns. Repeated calls on one line are counted separately, while overlapping patterns that capture the same key at the same source position are deduplicated.
- Cover runtime-built translation keys with two localization dynamic usage-source workflows. Editor comment markers such as
@languageManager(method: dynamic, enum: auth.failed,status.ready)are discovered during the normal scheme scan; the non-invasive Localization Dynamic Usage Sources Tool Window tab stores repeatable file/line/column and key groups without changing source code. An editor caret can create either form without selecting text, while an optional selection prefills keys; both forms contribute to the same usage counts and locations. - Both dynamic-source editors support scrollable repeatable groups and bulk key paste using newline, comma, semicolon, or a custom literal delimiter. Hovering a valid
@languageManagermarker shows its documentation; use Edit Localization Dynamic Usage Marker from the editor context menu or floating code toolbar to edit copied or existing markers. - Convert either dynamic-source representation into the other from its editor form. Conversion updates the source file and scheme rule together with rollback, VFS/document reload, cache invalidation, and recounting. Gutter icons mark both types in open editors: marker icons navigate in code, while non-invasive icons open and focus the corresponding Tool Window rule.
- Valid non-invasive rules also render a lightweight block inlay on the line above the target, aligned with the registered code position. It shows the distinct dynamic-key count, exposes rule details on hover, and opens the exact rule when clicked; stale coordinates are not inserted into unrelated source text.
- Choose Open in Editor Tab from the tool window options (⋮) menu to show the current view in a full-size editor tab.
- Use Visible Columns ▾ to hide or show language columns per scheme, Namespaces ▾ (shown only when a scheme has several namespaces) to filter rows by namespace, and right-click a cell for the same actions as Actions ▾ on that row.
- Double-click a language value cell (or press F2) to edit it in place; long and multi-line values wrap in the editor, Enter or focus loss saves only that locale file, Shift+Enter inserts a line break, and Esc cancels. Only cells combining several same-locale values are not edited in place and show a status hint; no dialog opens. Turn the feature off with the Quick in-place editing option in the plugin settings.
- Experimental: turn on Show a translation menu when hovering a translation key in the editor in the Experimental section of Settings → Tools → LanguageManager to underline a translation key under the mouse in writable editors. Keys are found with the active scheme's usage Regex, only in files inside the scheme base path that are not excluded (read-only/locked editors are skipped). After the configurable hover delay (default 0.7 s) a menu lists every language's translation in a scrollable, screen-bounded popup with Open in Translation Table and Edit Translation; a key without translations offers Create Translation with the key prefilled.
- Double-click a translation row's Usage cell to enable the on-demand Usage Locations tab. Location records remain backend-only and the frontend requests only that key's current page, limited to 100 rows; opening a row lazily resolves and caches its line/column before navigating the IDE caret.
- The default usage-scan exclusions cover
.git,.github,docs,vendor,storage,database,gradle,.gradle,build,out,dist,target,node_modules, and IDE-generated directories such as.idea,.fleet,.vs,.settings,.metadata, andnbproject. Users can customize the list. - Display an IDE two-pane Diff before repairing or deleting. SHA-256 is checked before applying a preview so external changes made after the preview are never overwritten silently.
- Cache parsed state and dynamic usage locations in memory and under
.idea/language-manager/to reduce repeated parsing. Saving dynamic rules invalidates the active scheme cache and starts a background reload; first load, manual reload, scheme switching, and imported schemes all use the dynamic definitions. - Bound parser memory per scheme with configurable per-file size, total content size, per-file entry, and total entry limits. File size is checked before content is read; entry and nesting limits are enforced while parsing.
- The PHP parser accepts only an optional
declare(strict_types=1);followed by staticreturn [...]orreturn array(...)data. It never executes PHP code.
- User Manual: installation, schemes, translation filters, issue visibility, usage scanning, Diff previews, and troubleshooting.
- Requirements: functional requirements, table filters, issue rules, RPC, security, caching, and acceptance criteria. Currently written in Traditional Chinese.
- Project Instructions: engineering principles, architecture boundaries, localization requirements, tests, and Git conventions.
- Compatibility Verification: verified IDE versions and the supported build range.
- Memory and Large-Scheme Verification: reproducible 12-locale parser, scanner, retained-memory, and RPC-payload measurements.
- Changelog
- Marketplace “What’s New”: generated from the current version section in CHANGELOG.md and injected into
plugin.xmlduring the build.
- Install the plugin and open LanguageManager from the IDE sidebar.
- Open the New Scheme dropdown and choose file selection or folder selection. Folder mode supports selecting locale directories such as
en,zh_CN, andzh_TWtogether. - Folder mode lists each discovered file, format, locale, namespace, entry count, and recognition error. Add more folders from the popup, enter or edit the scheme name in its dedicated field, select the recognized files, and confirm.
- Use search, locale filtering, and translation-status filtering to find rows with missing translations or zero usage. Manage the results with pagination and the Actions dropdown.
- Select one or more rows in Issues and Suggestions. Any automatic modification is written only after the user reviews and accepts its Diff.
- To hide duplicate-value or possibly-unused suggestions, open Settings → Tools → LanguageManager.
flowchart LR
UI["frontend<br/>Tool Window / JTable / Diff"]
DTO["shared<br/>RPC API / DTO / Search"]
RPC["backend RPC adapter"]
Service["LocalizationManagerService<br/>state / cache / mutations"]
Codec["LanguageFileCodec<br/>JSON / YAML / PHP / Properties"]
Files["Selected language files"]
Cache[".idea/language-manager"]
UI -->|"RPC request"| DTO
DTO --> RPC
RPC --> Service
Service --> Codec
Codec <--> Files
Service <--> Cache
Service -->|"StateFlow"| RPC
RPC -->|"durable Flow"| UI
The root project uses the IntelliJ Platform Gradle Plugin to assemble three content modules:
| Module | Runtime location | Responsibility |
|---|---|---|
shared |
frontend and backend | Serializable DTOs, RPC contract, and pure table search/JOIN/pagination logic |
frontend |
IDE client | Tool Window, table interaction, dialogs, Diff previews, localized UI, and RPC repository |
backend |
IDE backend | Scheme state, file IO, parsers, cache, quality analysis, usage scanning, and RPC implementation |
build.gradle.kts sets the plugin installation target to BOTH, so each side of split mode receives its required module.
| File | Purpose |
|---|---|
src/main/resources/META-INF/plugin.xml |
Plugin ID, name, description, vendor, and the three content module declarations |
src/main/resources/messages/LanguageManagerBundle*.properties |
Five-language dictionaries for the plugin name and description |
build.gradle.kts |
IntelliJ IDEA 2025.3.5 minimum compatibility baseline, split mode, plugin modules, tests, and Marketplace change-note injection |
gradle.properties |
Release version and Gradle/Kotlin build options |
CHANGELOG.md |
Release features and fixes |
AGENTS.md |
Product, architecture, security, localization, test, and Git rules |
.github/ISSUE_TEMPLATE/ |
Separate English and Traditional Chinese Issue Forms for bug reports, feature requests, and format compatibility |
| File | Purpose |
|---|---|
LocalizationModels.kt |
Scheme, locale-note, entry, issue, usage-default, row-filter, folder-recognition, AI-request, mutation, and Diff-preview DTOs; shared ResourceBundle locale rules (PROPERTIES_FALLBACK_LOCALE, propertiesBundleIdentity() used by both the backend parser and frontend dialogs, and preferredSourceLocale()) |
LocalizationManagerRpcApi.kt |
Shared @Rpc contract; every remote call identifies its project with ProjectId |
EntrySearch.kt |
Pure search, namespace + key JOIN, missing/zero-usage filters, and pagination capped at 100 rows |
| File | Purpose |
|---|---|
toolWindow/LanguageManagerToolWindowFactory.kt |
Creates the Tool Window and localizes its title according to the IDE/plugin language |
localization/LocalizationManagerPanel.kt |
Main UI: scheme dropdown, folder recognition, translation and issue tables, clipboard, Diff, and actions |
localization/HardcodedAnalysisPanel.kt |
Lazy top-level analysis workspace with progress, statistics, filtering, pagination, cancellation, and source navigation |
localization/MultiLanguageEntryDialog.kt |
Scrollable add/edit form that lists every locale textarea for one namespace and builds batch mutations |
localization/AiTranslationDialogs.kt |
Source/target locale selection, editable AI review, and feedback dialogs for iterative translation rounds |
RegexPresetUi.kt |
Framework-aware Regex recommendation menu shared by default and active-scheme settings (including Rust i18n t! macros) |
localization/LocalizationFrontendRepository.kt |
Converts UI operations to RPC calls and receives backend state through a durable flow |
localization/IssueVisibility.kt |
Applies duplicate-value and possibly-unused visibility preferences to the table, counts, and bulk actions |
localization/SchemeSettingsTransferDialog.kt |
Safe scheme JSON IO, atomic export, and per-file import preview |
settings/LanguageManagerSettings.kt |
Persists display language, issue preferences, new-scheme base path, Regex, and exclusions; migrates old defaults |
settings/LanguageManagerSettingsConfigurable.kt |
IDE Settings page for plugin language, issue preferences, the Experimental section, and new-scheme defaults |
localization/EditorKeyHoverService.kt |
Experimental editor key hover: mouse tracking, hyperlink highlight, dwell timer, event-driven scan cache, and edit/create dialogs |
localization/EditorKeyHoverPopup.kt / EditorKeyHoverSupport.kt |
Hover popup with per-language translations and actions; pure eligibility, match lookup, and screen-bounded size rules |
localization/SchemeUsageSettingsDialog.kt |
Edits managed files, scan path, Regex patterns, and exclusions for the active scheme |
localization/ProjectViewExclusionActions.kt |
Provides the Project-tree action group and active-scheme exclusion shortcut |
LanguageManagerBundle.kt |
Frontend resource bundle access |
resources/messages/LanguageManagerFrontendBundle*.properties |
Seven-language UI dictionaries for buttons, tabs, fields, prompts, and Diff text |
resources/icons/toolWindow*.svg |
LanguageManager Tool Window artwork in 16x16/20x20 Light and Dark variants selected automatically by the IDE |
| File | Purpose |
|---|---|
BackendRpcApiProvider.kt |
Registers LocalizationManagerRpcApi in the IntelliJ RPC backend |
BackendLocalizationManagerRpcApi.kt |
RPC adapter that delegates requests to the project service on an IO dispatcher |
LocalizationManagerService.kt |
Core workflow for schemes, state, cache, CRUD, repair previews, conflict checks, and usage scans |
LanguageFileSupport.kt |
Safe path/folder validation, bounded discovery, UTF-8 IO, atomic writes, targeted JetBrains VFS/document reload, and JSON/YAML/PHP/Properties parsing/rendering |
UsageScanSupport.kt |
Usage setting validation, Regex key extraction, base path scanning, exclusions, and resource limits |
EditorKeyScanSupport.kt |
Matches the usage Regex in one editor's text with a time budget and match cap, and resolves captures to namespace + key rows for the editor key hover |
HardcodedTextAnalysisSupport.kt |
Incremental per-file detection and statistics for likely untranslated quoted source text |
UsageExclusionSupport.kt |
Converts selected local folders into safe, precise exclusions relative to the scheme scan root |
LanguageLoadBudget.kt |
Applies pre-parse file-size and post-parse entry budgets across one isolated scheme |
FormatPreservingWriter.kt |
Writes value-only edits into the original JSON/YAML/PHP/Properties text so comments, formatting, and untouched entries stay unchanged; verifies every patch and falls back to a full render |
EntryMutationSupport.kt |
Applies validated multi-locale add/edit mutations to parsed documents before coordinated atomic writes |
AiTranslationSupport.kt |
Validates endpoints, locale-note context, and batch limits; sends OpenAI-compatible/Anthropic requests and strictly validates returned IDs and values |
SchemeSettingsTransferSupport.kt |
Versioned scheme JSON including locale notes, relative path conversion, import limits, and security validation |
TranslationInputValidation.kt |
Allows spaces, Unicode, and punctuation in keys while rejecting blank, control-character, and oversized input |
LocalizationAnalysis.kt |
Builds diagnostics for empty values, duplicate keys/values, missing translations, and unused keys |
LanguageManagerBackendBundle.kt |
Backend resource bundle access |
resources/messages/LanguageManagerBackendBundle*.properties |
Seven-language parser, validation, and diagnostic dictionaries |
| File | Coverage |
|---|---|
shared/src/test/kotlin/EntrySearchTest.kt |
Search, JOIN, missing and zero-usage filters, pagination, bulk row counts, and Find in Files Regex generation |
shared/src/test/kotlin/LocalizationModelsTest.kt |
Legacy scheme defaults, Unicode/sentence keys, usage Regex, and default exclusions |
frontend/src/test/kotlin/IssueVisibilityTest.kt |
Independent visibility preferences without affecting other issue types |
frontend/src/test/kotlin/LanguageManagerDefaultSettingsTest.kt |
Base path levels, new-scheme defaults, issue defaults, and legacy exclusion migration |
frontend/src/test/kotlin/ToolWindowIconVariantsTest.kt |
Presence, dimensions, and theme colors of all four Tool Window icon variants |
backend/src/test/kotlin/UsageScanSupportTest.kt |
Custom Regex, relative exclusions, counts, and rejection of unsafe settings |
backend/src/test/kotlin/EditorKeyScanSupportTest.kt |
Editor key hover Regex ranges, key resolution, create-translation suggestions, match cap, Regex time budget, and base-path/exclusion/language-file status |
frontend/src/test/kotlin/EditorKeyHoverSupportTest.kt |
Writable-editor eligibility, match lookup, translation listing, hover delay settings, and popup size limits |
backend/src/test/kotlin/TranslationInputValidationTest.kt |
Sentence/Unicode keys and rejection of blank, control-character, and oversized keys |
backend/src/test/kotlin/FormatPreservingWriterTest.kt |
One-value edits keep PHP/JSON/YAML/Properties comments, quoting, escapes, continuation lines, and CRLF unchanged; unchanged values do not rewrite; structural changes fall back |
backend/src/test/kotlin/SchemeSettingsTransferSupportTest.kt |
Relative path round trips, missing files, parent traversal, and format version rejection |
LocalizationManagerRpcApi is the only public contract across the frontend/backend boundary. The frontend resolves it through RemoteApiProviderService.resolve(). The backend provider creates the adapter, which resolves LocalizationManagerService by ProjectId.
| API | Purpose | Writes language files |
|---|---|---|
state(projectId) |
Streams schemes, entries, issues, busy state, and errors | No |
hardcodedAnalysisProgress(projectId) |
Streams lazy hardcoded-analysis discovery, scan, cache, match, and current-path progress | No |
scanEditorKeys(...) |
Experimental editor key hover: checks the file against the active scheme base path, exclusions, and language files, then returns usage-Regex key ranges and their namespace + key rows; requested only when the mouse moves over a stale editor |
No |
analyzeHardcodedText(...) |
Runs the active scheme's cancellable incremental hardcoded-text analysis and returns locations plus separate statistics | No |
createScheme(...) |
Validates user-selected files, persists the scheme, and loads it | No; writes plugin scheme data only |
deleteScheme(...) |
Deletes a scheme and its cache without deleting language files | No |
activateScheme(...) |
Switches the active scheme and loads cache or reparses | No |
reload(...) |
Reloads forcibly or according to fingerprints | No |
updateSchemeSettings(...) |
Validates and stores the scheme name, base path, Regex, exclusions, and language notes, invalidates cache, and recounts | No; writes plugin scheme data only |
addActiveSchemeExcludedDirectories(...) |
Validates Project-tree files/folders against the active scheme base path, adds relative exclusions, invalidates cache, and recounts | No; writes plugin scheme data only |
discoverLanguageFiles(...) |
Safely scans selected folders with the new-scheme loading budget, deduplicates files, and returns recognition results | No |
discoverAdditionalLanguageFiles(...) / addTrackedFiles(...) |
Previews mixed file/folder parser results, then validates and appends selected recognized files to an existing scheme | No; writes plugin scheme data only |
exportSchemeSettings() |
Serializes every scheme into portable, versioned JSON | No |
previewSchemeSettingsImport(...) |
Parses JSON, resolves relative paths, and reports file/parser status | No |
importSchemeSettings(...) |
Revalidates files and scan settings before creating new schemes | No; writes plugin scheme data only |
saveEntry(...) |
Adds or edits one entry; retained for single-cell paste and compatibility | Yes |
saveEntries(...) |
Validates and writes all locale values from the scrollable translation form as one batch | Yes |
translateWithAi(...) |
Sends transient provider credentials, bounded source/target locale notes, and up to 100 source values to the configured endpoint; returns suggestions only | No |
previewEntryMutations(...) |
Renders proposed translation mutations into file-level before/after content and source SHA-256 | No |
applyPreviewedEntryMutations(...) |
Rebuilds translation mutations, verifies every preview hash, and atomically writes unchanged sources | Yes |
deleteEntries(...) |
Deletes entries by entry ID | Yes |
renameKey(...) |
Renames a key in every applicable file of the scheme | Yes |
previewRenameKey(...) |
Validates cached usage offsets and builds language/source before-after content without writing | No |
applyPreviewedRenameKey(...) |
Regenerates the preview, verifies hashes and the edited file set, then atomically writes user-reviewed content | Yes |
repair(...) |
Normalizes parseable files and fills empty values with their keys | Yes; UI uses preview first |
repairEntries(...) |
Repairs specific empty entries | Yes; UI uses preview first |
previewLocaleVersion(...) |
Produces new-locale content and a Diff for each file | No |
createLocaleVersion(...) |
Verifies preview state, creates locale files, and updates the scheme file list | Yes |
previewNamespaceFiles(...) / createNamespaceFiles(...) |
Derives one target per locale from a tracked PHP/Properties structure reference, previews it, then creates and tracks the accepted files | Preview: no; create: yes |
previewDeleteNamespaceFiles(...) / deleteNamespaceFiles(...) |
Previews one PHP/Properties namespace family, verifies hashes, then transactionally removes its files and tracking metadata | Preview: no; delete: yes |
previewChanges(...) |
Produces in-memory before/after content and source SHA-256 | No |
applyPreviewedChanges(...) |
Rebuilds the preview, verifies hashes, and atomically writes only when unchanged | Yes |
Every service mutation is serialized with one coroutine Mutex per project. The RPC adapter moves file operations to Dispatchers.IO; Swing updates run only on the EDT.
plugin.xmlloads the shared, frontend, and backend modules.- Backend XML registers
BackendRpcApiProvider; frontend XML registers the Tool Window. LanguageManagerToolWindowFactorycreatesLocalizationManagerPanel.- The panel creates a project coroutine scope and collects the repository's durable
stateflow. LocalizationManagerServicereads.idea/language-manager/schemes.jsonin an IO coroutine and immediately loads the active scheme when one exists.
- The user explicitly selects individual files or one or more folders from the new-scheme dropdown.
- Folder mode performs bounded, depth-limited, deduplicated discovery, skips dependency/build directories, safely reads supported files, and attempts to parse each one. The frontend popup shows successes and failures and can add more folders.
- After the user confirms the scheme name and selected recognized files, the backend validates and deduplicates every path again before persisting the scheme.
loadScheme()computes alastModifiedTime XOR sizefingerprint for each file.- If the cache format and all fingerprints match,
cache-{schemeId}.jsonis loaded directly. - On a cache miss, files are parsed independently and entries/parser issues are published before the usage scan finishes.
- The backend applies the scheme base path, Regex patterns, and exclusions to every regular file under the scan root, regardless of extension or IDE file type. Managed language files are excluded so definitions are not counted as usages.
- Quality analysis runs, the disk cache is updated, and the complete state is emitted through
StateFlow.
EntrySearch.filter()applies the query mode and locale;filterRows()then applies missing-translation or zero-usage filtering.EntrySearch.join()groups bynamespace + keyand creates one column per locale.EntrySearch.paginate()limits every page to 100 rows.- Selecting one cell still maps row actions to that cell's row. Find in Files searches only the actual key and never prefixes a filename or bundle namespace.
- Find in Files with Usage Regex replaces the scheme Regex
(?<key>…)group with the selected literal key, removes boundary anchors, and enables IDE Regex mode. Ctrl+Ccopies selected cells; multiple cells produce TSV.Ctrl+Vwrites only to one locale value cell.IssueVisibilityapplies global preferences consistently to the issue table, status counts, and bulk processing.
- The frontend creates an
EntryMutationDtoor entry ID list. - The backend validates the scheme, path, locale, namespace, key length, and control characters. Keys may be natural-language sentences containing spaces, Unicode, and punctuation.
- The managed file is parsed again so stale UI state cannot overwrite newer content.
- When only existing values changed,
FormatPreservingWriterreplaces just those value spans in the original text and re-parses the result to verify it; added or removed keys, structured values, or an unverifiable patch fall back to rendering the wholeParsedLanguageFile. A file whose content would not change is not written; otherwise it is written atomically. - After the whole mutation succeeds, only the written user files are synchronously refreshed through JetBrains VFS; an already cached IDE document is reloaded from the final disk content.
- The scheme, analysis, and cache are rebuilt before the latest state is emitted to the UI.
sequenceDiagram
participant U as User
participant F as Frontend
participant B as Backend Service
participant D as Diff Dialog
participant FS as Language File
U->>F: Select repair / issue action
F->>B: previewChanges(request)
B->>FS: Safe read and parse
B-->>F: before / after / SHA-256
F->>D: Show two-pane Diff for each file
alt User cancels
D-->>F: Cancel
else User applies
F->>B: applyPreviewedChanges(request, hashes)
B->>FS: Read again and compare SHA-256
alt File changed
B-->>F: Reject overwrite and request a new preview
else No conflict
B->>FS: Atomically write after content
B-->>F: New state after reload
end
end
- A JSON root must be an object. Nested objects are flattened into dotted keys.
- JSON arrays are expanded into ordinary rows such as
sections.0.items.3.title. Each item supports the same JOIN, search, edit, delete, usage, missing-value, and AI translation actions as a scalar value, while writing reconstructs the original array shape. - Literal dots in sentence-style JSON keys are preserved through
keyPathsand do not create accidental nesting. - YAML supports space-indented
key: valuepairs and nested maps; tab indentation is rejected. - PHP parses strings, numbers, booleans, nested arrays, static string concatenation, and multiline heredoc/nowdoc values inside
return [...]orreturn array(...). For example,'first'."\n\n".'second'becomes one translation value. A heredoc/nowdoc delimiter is not fixed toTEXT; any matching valid PHP identifier such asEOT,HTML, orMESSAGE_2026works with<<<LABEL,<<<"LABEL", or<<<'LABEL'. The identifier only marks the string boundary and does not determine its content type. Variables, interpolation, functions, and arbitrary expressions are rejected and never executed. When files are grouped below a language directory,en/components/pagination.phpis identified as localeenand namespacecomponents.pagination. - Properties supports Java ResourceBundle comments, separators, continuations, and escaping. The base bundle without a locale suffix is shown as its own
fallbackcolumn (never merged withBundle_en.properties);Bundle_zh_TW.propertiesderives localezh_TWand namespaceBundle. lang/en.jsonderives localeenwith an empty namespace.lang/en/messages.phpderives localeenand namespacemessages.
- Only ordinary
.json,.yaml,.yml,.php, and.propertiesfiles explicitly selected in a scheme are accepted. - Each file is limited to 10 MB; paths are limited to 4,096 characters.
- URI, LDAP,
file:, Windows device paths,GLOBALROOT, and unsafe control characters are rejected. - All text is read and written as UTF-8. Output is written to a temporary file in the same directory and then replaced with an atomic move.
- Diff preview is read-only. Apply rebuilds the content and verifies SHA-256 before writing.
- Backend errors are stripped of unsafe control characters and limited to 500 characters before being returned.
Plugin data is stored inside the project:
.idea/language-manager/
├── schemes.json # Scheme list and active scheme
└── cache-{schemeId}.json # Fingerprints, entries, issues, usage counts, and usage locations
The authoritative in-memory state is LocalizationStateDto/StateFlow. Disk cache only accelerates restart and scheme switching; a changed fingerprint or cache format forces a reparse.
The plugin supports JetBrains Platform build 253.5 (IntelliJ IDEA 2025.3.5) and later, with no configured upper bound. See Compatibility Verification for tested versions. Development requires JDK 21 or the JBR bundled with PhpStorm 2026.1.
$env:JAVA_HOME='C:\Program Files\JetBrains\PhpStorm 2026.1\jbr'
$env:GRADLE_OPTS='-Dkotlin.incremental=false'
.\gradlew.bat test --no-daemon --console=plain
.\gradlew.bat buildPlugin --no-daemon --console=plainThe installable archive is generated at:
build/distributions/LanguageManage-{version}.zip
Test coverage includes:
shared: search, JOIN, locale/translation filters, usage defaults, Find in Files queries, and pagination.backend: JSON/YAML/PHP/Properties parse/render, safe paths, arrays, sentence-style keys, usage Regex/exclusions/counts, analysis, read-only previews, and seven-language bundle parity.frontend: setting defaults/migration, issue visibility, Tool Window API compatibility, explicit locale selection, controlled locale suggestions, and seven-language bundle parity.
- Releases: CHANGELOG.md
- Marketplace “What’s New”: generated from the matching version in CHANGELOG.md
- License: LICENSE