Tickd.ai
← The Tickd Guide

Tutorials & Guides

How to Build a CLI Tool to Automate React i18n Extraction Using Gemini 1.5 Flash and Python

Localisation is vital, but pulling hardcoded strings out of React components into JSON files is incredibly dull. Automate your i18n workflows with Gemini 1.5 Flash and Pydantic structured outputs.

Updated 10/5/2026

The Mind-Numbing Reality of i18n

Building an application that supports multiple languages is great for accessibility and market reach. What isn’t great is the execution. The typical developer workflow for setting up internationalisation (i18n) is an exercise in absolute monotony.

You scan your React codebase file by file, find hardcoded strings, extract them into a separate localisation JSON dictionary, make up a nested key name, and then replace the original string with an translation helper call like t('auth.login_cta').

Because this process relies heavily on simple pattern matching and structural adjustments, it is prime real estate for automation. With Gemini 1.5 Flash, we can leverage structural JSON schema outputs to automatically inspect a React source file, strip out UI copy, compile translation files, and rewrite the original component with clean i18n keys—getting your translation keys configured without a single missed tick.

Why Gemini 1.5 Flash?

While some models excel at giant multi-step reasoning chains, this is a speed and structured accuracy game. Gemini 1.5 Flash is exceptionally fast, highly cost-effective, and natively supports Pydantic JSON schemas. It lets us dictate the exact output format we want, ensuring the LLM returns both the rewritten source code and the newly minted dictionary definitions in a single call.

For more details on the capabilities of the Gemini models, browse the Gemini platform page.

Step 1: Setting Up the Translation Schema

To ensure Gemini returns predictably formatted data, we’ll define a structured payload using Pydantic. If you run into parsing issues or API exceptions during this build, the Google Gemini support hub offers comprehensive SDK troubleshooting.

Let’s start by setting up our workspace and installing the official Google Gen AI SDK along with Pydantic:

`bash mkdir react-i18n-extractor cd react-i18n-extractor python3 -m venv venv source venv/bin/activate pip install google-genai pydantic `

Next, let's create a script named extractor.py and import our dependencies.

`python import os import json import sys from pydantic import BaseModel, Field from google import genai from google.genai import types

Instantiate the Gemini client. # This automatically reads your GEMINI_API_KEY environment variable. client = genai.Client() ```

Step 2: Defining the Structured Output

We need Gemini to output two distinct elements: 1. `modified_code`: The rewritten React file using a standard dynamic function like t('key') for translations. 2. `extracted_keys`: A dictionary containing the newly extracted namespace-keys and their corresponding English strings.

Let's define this structure in code. To learn more about standard definitions, review our AI terminology glossary.

`python class I18nExtractionResult(BaseModel): modified_code: str = Field( description="The complete, modified React code with all hardcoded user-facing strings replaced by t('key') translations." ) extracted_keys: dict[str, str] = Field( description="A dictionary mapping the generated i18n keys to their original English text values (e.g., {'auth.submit_button': 'Log In Now'})." ) `

Step 3: Constructing the System Prompt

We need to instruct the model to intelligently identify hardcoded strings while leaving imports, code logs, element keys, and logic statements alone.

`python I18N_SYSTEM_INSTRUCTION = """ You are an expert React developer tasked with preparing front-end components for internationalisation (i18n).

Your assignment: 1. Scan the React/TypeScript code provided by the user. 2. Locate all hardcoded, user-facing UI text strings (including paragraph copy, button text, list items, and input placeholders). 3. Ignore non-user-facing strings such as console logs, HTML element ids, class names, variables, react hooks parameters, and API endpoints. 4. Replace the user-facing strings with a translation hook expression: t('prefix.key_name'). 5. Ensure you import the translation hook if it is missing (e.g., import { useTranslation } from 'react-i18next'; or keep any existing custom translation hooks already declared). 6. Generate logical, camelCase nested key names based on the context of the text (e.g., 'auth.email_placeholder' or 'dashboard.header_title'). 7. Output both the modified component structure and the translated key-value map strictly as requested. """ `

Step 4: Building the File Rewriter and i18n Appender

Now, let's write the execution function. This function reads a React component, calls Gemini 1.5 Flash using the strict schema configuration, updates the React file, and merges the new translation keys into an local translation dictionary (like en.json).

`python def extract_i18n_from_file(file_path, locale_json_path, namespace_prefix): if not os.path.exists(file_path): print(f"Error: File not found at {file_path}") return

print(f"Reading React Component: {file_path}...") with open(file_path, 'r', encoding='utf-8') as f: source_code = f.read()

print("Asking Gemini 1.5 Flash to extract strings...") # Execute the structured content generation query response = client.models.generate_content( model='gemini-1.5-flash', contents=f"Apply i18n transformations to this React component. Use the namespace prefix '{namespace_prefix}':\n\n{source_code}", config=types.GenerateContentConfig( system_instruction=I18N_SYSTEM_INSTRUCTION, response_mime_type="application/json", response_schema=I18nExtractionResult, temperature=0.1, ), )

Parse response content via the Pydantic helper validation result = I18nExtractionResult.model_validate_json(response.text) # Update the React component file with translated hooks with open(file_path, 'w', encoding='utf-8') as f: f.write(result.modified_code) print(f"Successfully updated component file: {file_path}")

Read, merge, and write updated keys into local i18n JSON files existing_translations = {} if os.path.exists(locale_json_path): try: with open(locale_json_path, 'r', encoding='utf-8') as f: existing_translations = json.load(f) except json.JSONDecodeError: print("Existing translation file was corrupted. Initialising a fresh dictionary.")

Merge new translations key-by-key for key, val in result.extracted_keys.items(): existing_translations[key] = val

with open(locale_json_path, 'w', encoding='utf-8') as f: json.dump(existing_translations, f, indent=2, ensure_ascii=False) print(f"Successfully updated translation database at: {locale_json_path}") print(f"Added {len(result.extracted_keys)} translation keys!")

if __name__ == "__main__": # Quick command-line arguments setup if len(sys.argv) < 4: print("Usage: python extractor.py <path_to_react_file> <path_to_en_json> <namespace_prefix>") sys.exit(1)

target_file = sys.argv[1] translation_file = sys.argv[2] prefix = sys.argv[3] extract_i18n_from_file(target_file, translation_file, prefix) `

Let's Run a Real Test

To see this engine in action, imagine we have a simple, hardcoded onboarding UI called WelcomeCard.tsx:

`tsx import React from 'react';

export const WelcomeCard = () => { return ( <div className="p-6 max-w-sm rounded-lg shadow-md"> <h2 className="text-xl font-bold">Welcome to the Portal</h2> <p className="text-gray-600">Please log in to continue your journey and manage your settings.</p> <button className="bg-blue-500 text-white py-2 px-4 rounded">Get Started</button> </div> ); }; `

Now, fire up your script in the terminal:

`bash export GEMINI_API_KEY="your_gemini_api_key_here" python extractor.py WelcomeCard.tsx en.json onboarding `

In less than two seconds, Gemini will parse your file, determine what matters to developers versus end users, and apply the refactor. Let’s look at your newly saved WelcomeCard.tsx:

`tsx import React from 'react'; import { useTranslation } from 'react-i18next';

export const WelcomeCard = () => { const { t } = useTranslation();

return ( <div className="p-6 max-w-sm rounded-lg shadow-md"> <h2 className="text-xl font-bold">{t('onboarding.welcome_title')}</h2> <p className="text-gray-600">{t('onboarding.welcome_description')}</p> <button className="bg-blue-500 text-white py-2 px-4 rounded">{t('onboarding.get_started_btn')}</button> </div> ); }; `

And inside your en.json, you have neatly serialised translation keys ready to be sent straight to copy editors or translated into other languages:

`json { "onboarding.welcome_title": "Welcome to the Portal", "onboarding.welcome_description": "Please log in to continue your journey and manage your settings.", "onboarding.get_started_btn": "Get Started" } `

With just a few lines of Python and Gemini 1.5 Flash, you’ve taken one of the most frustrating frontend chores and automated it into a single, seamless script. Go forth and translate!

reactgemini-flashi18nlocalisationpython-cli

Keep going

Build something with the prompt generator, decode the jargon in the glossary, or compare the tools on our platform deep-dives.