A powerful Rust command-line tool that synchronizes i18next translation JSON files with Google Sheets, providing seamless bidirectional translation management with automatic Google Translate integration.
The tool uses a two-step workflow: authenticate once, then sync many times.
# Step 1: Authenticate (one-time setup)
i18n_google_sync auth
# Step 2: Sync operations (use as needed)
# Default: add new keys then pull translations back
i18n_google_sync sync --sheet-id "your-google-sheet-id" --dry-run
i18n_google_sync sync --sheet-id "your-google-sheet-id"
# Optional: mark a different language as the main column during the first run
i18n_google_sync sync --sheet-id "your-google-sheet-id" --main-language "fr"
# Optional one-way modes
i18n_google_sync sync --mode add-keys --sheet-id "your-google-sheet-id"
i18n_google_sync sync --mode sync-back --sheet-id "your-google-sheet-id"This approach is secure, CI-friendly, and efficient for team workflows.
- 📁 Smart File Detection: Automatically discovers i18next translation files in
/locales/$lang/$namespace.jsonstructure - 🔄 Bidirectional Sync: Add new translation keys to sheets or sync translated content back to JSON files
- 🌐 Google Translate Integration: Automatically generates
=GOOGLETRANSLATE()formulas for missing translations - ⭐ Main Language Flagging: Tag the primary language column (e.g.,
en (main)) on the very first sheet setup via--main-language - 🔑 Secure Authentication: Browser-based OAuth2 flow with secure token caching
- 📊 Organized Sheets: Each namespace becomes a separate sheet tab with proper column structure
- 🔍 Dry-Run Mode: Preview all changes before applying them
- ⚡ Duplicate Prevention: Intelligent checking to avoid duplicate entries
- 🛡️ Robust Error Handling: Comprehensive validation and user-friendly error messages
The tool organizes your translations in Google Sheets as follows:
| Column A | Column B | Column C | Column D | Column E |
|---|---|---|---|---|
| Translation Key | Description | Main Language (e.g., en (main)) | Language 2 (e.g., fr) | Language 3 (e.g., de) |
auth.login.title |
(not synced) | Login |
=GOOGLETRANSLATE(C2,"en","fr") |
=GOOGLETRANSLATE(C2,"en","de") |
auth.login.button |
(not synced) | Sign In |
Se connecter |
Anmelden |
- Column A: Translation keys (dot notation supported, e.g.,
auth.login.title) - Column B: Descriptions (preserved, not synced)
- Column C+: Languages with the default language first (tagged as
(<lang> (main))the very first time headers are created) and other locales ordered deterministically without disrupting existing columns - Separate Tabs: Each namespace (e.g.,
common,auth,dashboard) gets its own sheet tab
- Scans local translation files for new keys
- Adds missing keys to Google Sheets
- Populates default language values
- Generates
=GOOGLETRANSLATE()formulas for other languages
- Reads translated values from Google Sheets
- Updates local JSON files with translations
- Extracts actual translated text (not formulas)
- Preserves JSON file structure and formatting
- Default behaviour when
--modeis omitted - Combines both add-keys and sync-back operations in a single run
- Ensures Google Translate formulas populate before local files are updated
- Handles conflicts intelligently
Before using this tool, you need to set up Google Cloud credentials. The tool requires two types of files:
client_secret.json- OAuth2 app credentials from Google Cloudtokens.json- User authentication tokens (created by the tool)
- Go to the Google Cloud Console
- Create a new project or select an existing one
- Enable the Google Sheets API:
- Navigate to "APIs & Services" > "Library"
- Search for "Google Sheets API"
- Click "Enable"
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "OAuth client ID"
- If prompted, configure the OAuth consent screen:
- Choose "External" user type
- Fill in required fields (App name, User support email, Developer contact)
- Add your email to test users
- Choose "Desktop application" as the application type
- Name your OAuth2 client (e.g., "i18n Sync Tool")
- Download the JSON file - this is your
client_secret.json
The tool searches for client_secret.json in this order:
- Local directory:
./.i18n-google-sync/client_secret.json - Home directory:
~/.i18n-google-sync/client_secret.json - Fail if neither found
Recommended approach:
# Option A: Place in project directory (per-project credentials)
mkdir -p ./.i18n-google-sync
cp ~/Downloads/client_secret_*.json ./.i18n-google-sync/client_secret.json
# Option B: Place in home directory (global credentials)
mkdir -p ~/.i18n-google-sync
cp ~/Downloads/client_secret_*.json ~/.i18n-google-sync/client_secret.json# Test with dry-run to verify credentials work
i18n_google_sync auth
i18n_google_sync sync add-keys --sheet-id "your-google-sheet-id" --dry-run# Default: Store tokens in home directory (~/.i18n-google-sync/tokens.json)
i18n_google_sync auth
# Local: Store tokens in project directory (./.i18n-google-sync/tokens.json)
i18n_google_sync auth --local-cache
# Custom: Store tokens at specific location
i18n_google_sync auth --auth-cache /path/to/tokens.jsonWhat happens during authentication:
- Tool finds your
client_secret.jsonfile - Opens your browser to Google's consent page
- You grant permission to access Google Sheets
- Tool receives and stores authentication tokens
# Tokens are automatically found and used
i18n_google_sync sync add-keys --sheet-id "your-sheet-id" --main-language "en"
i18n_google_sync sync sync-back --sheet-id "your-sheet-id"
i18n_google_sync sync full-sync --sheet-id "your-sheet-id"The tool manages two types of files in consistent locations:
Search order:
./.i18n-google-sync/client_secret.json(local directory)~/.i18n-google-sync/client_secret.json(home directory)
Search order:
- Custom path (if
--token-pathspecified) ./.i18n-google-sync/tokens.json(local directory)~/.i18n-google-sync/tokens.json(home directory)
- Local Storage Only: All credentials stored locally on your machine
- Token Expiration: Automatic token refresh when expired
- No Hardcoded Secrets: Requires your own Google Cloud credentials
- Secure OAuth2 Flow: Uses Google's standard authentication process
"client_secret.json not found" error:
# Make sure you have the file in one of these locations:
ls ./.i18n-google-sync/client_secret.json
ls ~/.i18n-google-sync/client_secret.json"No authentication tokens found" error:
# Run the auth command first:
i18n_google_sync auth"Invalid client" error:
- Ensure Google Sheets API is enabled in your Google Cloud project
- Verify your
client_secret.jsonis valid and properly formatted - Check that your OAuth2 app is configured for "Desktop application"
Browser doesn't open automatically:
- Copy the displayed URL and manually paste it in your browser
- Rust (1.70 or later)
- Git
- A Google account with access to Google Sheets
# Clone the repository
git clone https://github.com/your-org/i18n_google_sync.git
cd i18n_google_sync
# Build the project
cargo build --release
# The binary will be available at target/release/i18n_google_sync# Clone and build for development
git clone https://github.com/your-org/i18n_google_sync.git
cd i18n_google_sync
# Run directly with Cargo
cargo run -- --helpThe tool now uses a two-step workflow: authenticate first, then sync. This approach is more secure and CI-friendly.
i18n-google-sync auth [OPTIONS]
Options:
--auth-cache <PATH> Custom path to store authentication tokens
--local-cache Store tokens in ./.i18n-google-sync/tokens.json
-h, --help Print helpi18n-google-sync sync [OPTIONS] --sheet-id <SHEET_ID>
Options:
--sheet-id <SHEET_ID> Google Sheet ID (required)
--mode <MODE> Sync operation mode [default: add-keys]
[possible values: add-keys, sync-back, full-sync]
--locales-path <PATH> Path to locales directory [default: ./locales]
--default-lang <LANG> Default language code [default: en]
--dry-run Preview changes without applying them
--token-path <PATH> Custom path to read authentication tokens
-h, --help Print help# Authenticate and store tokens in home directory (default)
i18n_google_sync auth
# Store tokens in local project directory
i18n_google_sync auth --local-cache
# Store tokens at custom location
i18n_google_sync auth --auth-cache ~/.config/i18n-sync/tokens.json# Add new translation keys to Google Sheets (dry-run)
i18n_google_sync sync add-keys --sheet-id "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" --dry-run
# Add keys for real
i18n_google_sync sync add-keys --sheet-id "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
# Sync translations back to local files
i18n_google_sync sync sync-back --sheet-id "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
# Full bidirectional sync
i18n_google_sync sync full-sync --sheet-id "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
# Use custom locales directory
i18n_google_sync sync add-keys --sheet-id "your-sheet-id" --locales-path /path/to/translations
# Spanish as default language
i18n_google_sync sync add-keys --sheet-id "your-sheet-id" --default-lang es
# Use custom token location
i18n_google_sync sync add-keys --sheet-id "your-sheet-id" --token-path /path/to/tokens.jsonThe tool automatically searches for authentication tokens in this order:
- Custom path (if
--token-pathspecified) - Local directory:
./.i18n-google-sync/tokens.json - Home directory:
~/.i18n-google-sync/tokens.json - Fail if none found
This allows you to authenticate once and run multiple sync operations without re-authentication.
Your i18next files should follow this structure:
locales/
├── en/
│ ├── common.json
│ ├── auth.json
│ └── dashboard.json
├── fr/
│ ├── common.json
│ ├── auth.json
│ └── dashboard.json
└── de/
├── common.json
├── auth.json
└── dashboard.json
Example locales/en/auth.json:
{
"login": {
"title": "Sign In",
"button": "Login",
"forgot_password": "Forgot Password?"
},
"register": {
"title": "Create Account",
"button": "Sign Up"
}
}To find your Google Sheet ID:
- Open your Google Sheet in a browser
- Look at the URL:
https://docs.google.com/spreadsheets/d/SHEET_ID/edit - Copy the
SHEET_IDpart (between/d/and/edit)
Example: 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
# 1. Create your translation files
mkdir -p locales/en locales/fr locales/de
# 2. Add some initial translations (locales/en/common.json)
echo '{"hello": "Hello", "goodbye": "Goodbye"}' > locales/en/common.json
# 3. Create a new Google Sheet and get its ID
# 4. Authenticate with Google (one-time setup)
i18n_google_sync auth
# 5. First sync - add keys to sheet (preview first)
i18n_google_sync sync add-keys --sheet-id "your-sheet-id" --dry-run
# 6. Apply the changes
i18n_google_sync sync add-keys --sheet-id "your-sheet-id"
# 7. Google Translate will auto-generate translations in the sheet
# 8. Sync back the translated content
i18n_google_sync sync sync-back --sheet-id "your-sheet-id"# 1. Add new translation keys to your JSON files
# locales/en/auth.json: {"login": {"title": "Sign In", "button": "Login"}}
# 2. Preview what will be added to the sheet
i18n_google_sync sync add-keys --sheet-id "your-sheet-id" --dry-run
# 3. Add the new keys
i18n_google_sync sync add-keys --sheet-id "your-sheet-id"
# 4. Check Google Sheets - new translations should appear with GOOGLETRANSLATE formulas
# 5. After translations are processed, sync back
i18n_google_sync sync sync-back --sheet-id "your-sheet-id"# 1. Translator updates translations directly in Google Sheets
# 2. Developer syncs changes back to code
# Preview what will be updated
i18n_google_sync sync sync-back --sheet-id "your-sheet-id" --dry-run
# Apply the changes
i18n_google_sync sync sync-back --sheet-id "your-sheet-id"
# 3. Commit the updated JSON files to version control
git add locales/
git commit -m "Update translations from Google Sheets"# In your CI/CD pipeline, you can authenticate once and then run multiple operations
# 1. Authenticate and store tokens for the pipeline
i18n_google_sync auth --local-cache
# 2. Add any new keys from feature branches
i18n_google_sync sync add-keys --sheet-id "your-sheet-id"
# 3. Sync back the latest translations for deployment
i18n_google_sync sync sync-back --sheet-id "your-sheet-id"
# 4. The tokens are automatically found in the local cache
# No need to re-authenticate for subsequent commands# Each team member authenticates once on their machine
i18n_google_sync auth # Stores tokens in home directory
# Everyone can then sync using the same commands
i18n_google_sync sync add-keys --sheet-id "shared-sheet-id"
i18n_google_sync sync sync-back --sheet-id "shared-sheet-id"
# Or use project-specific tokens for different projects
i18n_google_sync auth --local-cache # Per-project authentication# Run all tests
cargo test
# Run specific module tests
cargo test auth
cargo test config
# Run tests with output
cargo test -- --nocapturesrc/
├── main.rs # CLI entry point
├── config.rs # Configuration management
├── auth/
│ ├── mod.rs # Authentication module
│ └── oauth.rs # OAuth2 implementation
├── files/
│ ├── mod.rs # File operations
│ ├── parser.rs # JSON parsing
│ └── writer.rs # JSON writing
├── sheets/
│ ├── mod.rs # Google Sheets integration
│ ├── manager.rs # Sheet operations
│ └── translate.rs # Translation services
├── sync/
│ ├── mod.rs # Sync operations
│ ├── add_keys.rs # Add keys mode
│ ├── sync_back.rs # Sync back mode
│ └── full_sync.rs # Full sync mode
└── utils/
├── mod.rs # Utilities
└── errors.rs # Error types
"Locales path does not exist" error:
# Make sure your locales directory exists
mkdir -p locales/en
echo '{"test": "Hello"}' > locales/en/common.json"Sheet ID cannot be empty" error:
# Ensure you provide a valid Google Sheet ID
i18n_google_sync --sheet-id "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"Compilation errors:
# Make sure you have the latest Rust version
rustup update
cargo clean
cargo buildPermission denied on auth cache:
# Use a writable directory for token cache
i18n_google_sync --sheet-id "your-id" --auth-cache ~/.config/i18n-sync/tokens.json-
Check the help output:
i18n_google_sync --help
-
Enable verbose logging:
RUST_LOG=debug i18n_google_sync --sheet-id "your-id" --dry-run -
Test with dry-run first:
i18n_google_sync --sheet-id "your-id" --dry-run -
Verify your setup:
- ✅ Google Sheets API is enabled in your Google Cloud project
- ✅ OAuth2 credentials are properly configured
- ✅ Your Google account has access to the target sheet
- ✅ Locales directory exists and contains valid JSON files
This project is licensed under the Apache-2.0 license - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the project
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Built with Rust
- Uses google-sheets4 for Google Sheets API integration
- OAuth2 implementation with yup-oauth2
- CLI powered by clap
Build with ❤️ by Webmobix