This guide explains how to use Roblox Slang with Rojo, a tool for managing Roblox projects with external editors.
Rojo automatically syncs files from your filesystem to Roblox Studio. By configuring your output_directory to match your Rojo project structure, generated translation files will automatically sync to Studio without manual copying.
Set your output directory to a Rojo-tracked folder:
slang-roblox.yaml:
# Output to Rojo-tracked folder
output_directory: src/ReplicatedStorage/Translations
# Other config...
base_locale: en
supported_locales:
- en
- es
input_directory: translationsdefault.project.json:
{
"name": "MyGame",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"$className": "ReplicatedStorage",
"$path": "src/ReplicatedStorage"
},
"ServerScriptService": {
"$className": "ServerScriptService",
"$path": "src/ServerScriptService"
}
}
}my-game/
├── default.project.json # Rojo config
├── slang-roblox.yaml # Slang config
├── translations/ # Source translations
│ ├── en.json
│ ├── es.json
│ └── id.json
└── src/
├── ReplicatedStorage/
│ └── Translations/ # Generated files (auto-synced by Rojo)
│ ├── init.lua # Main module (ModuleScript)
│ └── types/
│ └── Translations.d.luau # Type definitions
└── ServerScriptService/
└── MyScript.server.lua-
Edit translations:
# Edit your translation files vim translations/en.json -
Build translations:
roblox-slang build --watch
-
Rojo syncs automatically:
# In another terminal rojo serve -
Connect from Studio:
- Open Roblox Studio
- Plugins → Rojo → Connect
- Changes sync automatically!
Rojo recognizes file extensions:
| Extension | Roblox Instance Type |
|---|---|
.lua |
ModuleScript |
.server.lua |
Script |
.client.lua |
LocalScript |
Roblox Slang generates .lua files, which become ModuleScript instances in Studio.
The generated Translations.lua file is a ModuleScript that can be required from any script:
-- ServerScript
local Translations = require(game.ReplicatedStorage.Translations)
local t = Translations.new("en")
print(t.ui.buttons:buy()) -- "Buy"-- LocalScript
local Translations = require(game.ReplicatedStorage.Translations)
local t = Translations.newForPlayer(game.Players.LocalPlayer)
print(t.ui.messages:greeting({ name = "Player" }))The .d.luau file provides autocomplete in editors that support Luau LSP (VS Code with Luau extension, etc.). It's not executed in-game.
For code shared between client and server:
output_directory: src/ReplicatedStorage/Translations-- Works on both client and server
local Translations = require(game.ReplicatedStorage.Translations)For server-only translations (admin commands, logs):
output_directory: src/ServerStorage/Translations-- Server-only
local Translations = require(game.ServerStorage.Translations)Keep both Slang and Rojo running during development:
# Terminal 1: Slang watch mode
roblox-slang build --watch
# Terminal 2: Rojo serve
rojo serveChanges to translation files will automatically:
- Trigger Slang rebuild
- Update generated files
- Sync to Studio via Rojo
Add generated files to .gitignore:
# Generated by roblox-slang
src/**/Translations/
!src/**/Translations/.gitkeep
# Or specific files
src/ReplicatedStorage/Translations/init.lua
src/ReplicatedStorage/Translations/types/Keep only source translations in version control:
- ✅
translations/*.json - ✅
slang-roblox.yaml - ❌
src/ReplicatedStorage/Translations/(generated)
Problem: Generated files don't appear in Studio.
Solutions:
-
Check Rojo is running:
rojo serve -
Check Studio is connected: Plugins → Rojo → Connect
-
Verify
output_directorymatches Rojo$path:# slang-roblox.yaml output_directory: src/ReplicatedStorage/Translations
// default.project.json "ReplicatedStorage": { "$path": "src/ReplicatedStorage" }
Problem: require(ReplicatedStorage.Translations) fails.
Solutions:
-
Check file exists in Studio hierarchy
-
Verify it's a ModuleScript (not Script or LocalScript)
-
Check path is correct:
-- Correct require(game.ReplicatedStorage.Translations) -- Wrong require(game.ReplicatedStorage.Translations.init)
Problem: No autocomplete in VS Code.
Solutions:
-
Install Luau Language Server
-
Configure
.vscode/settings.json:{ "luau-lsp.types.definitionFiles": [ "src/**/types/*.d.luau" ] }
default.project.json:
{
"name": "MyGame",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"$path": "src/ReplicatedStorage"
}
}
}slang-roblox.yaml:
base_locale: en
supported_locales: [en]
input_directory: translations
output_directory: src/ReplicatedStorage/Translationsmy-game/
├── default.project.json
├── slang-roblox.yaml
├── translations/
│ ├── en.json
│ ├── es.json
│ ├── fr.json
│ └── id.json
└── src/
├── ReplicatedStorage/
│ ├── Translations/
│ │ ├── init.lua
│ │ └── types/
│ │ └── Translations.d.luau
│ └── Modules/
└── ServerScriptService/
└── LocalizationService.server.luaIf you have a custom Rojo structure, adjust output_directory accordingly:
Example: Nested structure
{
"tree": {
"ReplicatedStorage": {
"Packages": {
"$path": "src/packages"
}
}
}
}# Output to packages folder
output_directory: src/packages/TranslationsExample: Multiple games in one repo
monorepo/
├── game-a/
│ ├── default.project.json
│ ├── slang-roblox.yaml
│ └── src/
└── game-b/
├── default.project.json
├── slang-roblox.yaml
└── src/Each game has its own config and output directory.