Localizing Products The Gojek Way
By Soumyajit Das
Gojek’s growth since the launch of the app in 2015 has been both horizontal and vertical. The number of products grew and we started expanding internationally. Journeying beyond Indonesia, into other Southeast Asian countries, meant we had to localize our products to fit the demographies we were targeting.
The idea was simple: Make every user familiar with our app by presenting content in their local language.
Our developers geared up to find a solution to manage localized string copies in an effective manner across multiple platforms.
We automated String copy management on our Gojek app by developing an in-house Kotlin based CLI tool called Localized String Generator. Throughout the development process, our focus was to improve on extensibility and developer friendliness while ticking all boxes in terms of features in a String copy management tool. Some important features we included were: Command Line Interface, Release version management, Guided validations & access, Dry Run, etc.
Think big but start small
Gojek has close to 250 app modules and this number is ever-increasing. It’s vital to have a simplified, extensible solution for our string localization management.
In the first phase, we decided to solve this problem for our own product, Gopay. GoPay, being one of the most used payment apps in Indonesia, has close to 40 app modules and 4000 strings across 4 locales (i.e. 4000*4 = 16000 copies). Thus, we rolled up our sleeves and started brainstorming.
Initially, we figured out a few limitations in our current system we wanted to solve:
- UX Writers were having a hard time maintaining all strings in the app and getting approval for the same.
- There was a possibility Android and iOS had string copy variations as some of the string resources have multiple definitions according to respective locales.
- To implement and test localization, massive manual work was required by Developers and QA.
- Minor typos like the wrong case, missing period symbols were very frequent.
Initial Approach
Even though we started making the best out of this approach, it did not take long for us to realise the method needed a lot of improvements.
Major pain points of the initial approach
1. Unable to track changes
- Possible side-effects due to the huge list of new localized strings fetched while working on some other feature
- Version management was unavailable
2. Manual steps
- Had to manually download CSV from Google Sheets
- Ruby setup for executing script was required
- No CI/CD pipeline
3. Error-prone
- Duplicate keys
- Missing escape characters checks
- Duplicate strings
- The script failed without giving a proper description in case of invalid data in CSV
4. Scaling issues
- Generated single file for all strings, hence, not scalable
- Solution is rigid
- Doesn’t support the addition of new languages out of the box
5. Onboarding
- Onboarding any module to a Google Sheets based approach required lots of manual effort
Introducing ‘Localized String Generator’ 🥁
The first approach was coupled with extensive manual intervention. With the Localized String Generator, one of the main aspects we focused on was to automate it as much as possible.
Here are the features we introduced:
- Unified string management portal for iOS & Android, using Google Sheets
- Guided access which descriptively explains the errors to the user/developer
- Dry Run to visualise the changes that are going to happen
- Force mode when the developer knows they just can’t be wrong 😁
- Command Line interface along with HELP for all commands and options
- Flexible: Having JSON configurations, which can be committed in host apps along with module
- Extensible: Can be integrated with any iOS & Android apps, with minimal steps.
- Release version management (minimum support for now)
- Can be automated as part of CI pipelines
- Guided Google Sheets entries validation and guided string files generation from remote
- Takes care of escaping special chars, tags, variable args
System Architecture
As we can see above, the building blocks of the Localized String Generator are:
- Google Sheets
- Localized String Generator JAR
- Configuration JSON file
- Git for Version Management
The amalgamation of these building blocks
1. Create Google Sheet
We create a new Google spreadsheet for the product (e.g.: GoPay, GoFood, GoRide, etc.) and make a note of the Spreadsheet ID and Spreadsheet Name as shown in the below image.
Things to take note of:
👉 Once the UX Writer verifies and updates the string copy in Google Sheet, the is Verified by UxWriter
column will be updated to yes
.
👉 For Localized String Generator to work with the created Google Sheet, we need to go through the following steps:
- Create a Google Service account and download the p12/JSON credential file for that account.
- Configure the project to use this credential file to make changes to the Google sheet.
- Provide Google Sheet edit access to the service account.
2. Create Local configuration files in JSON format
A committable configuration file needs to be created which contains all the details regarding Android/iOS projects.
Here is a sample configuration JSON file:
{
"app_name": "target app ",
"product_name": "target product",
"module_name": "target module",
"string_resources_folder_path": "projects string resource path",
"google_sheet_id": "created google sheet id",
"primary_google_sheet_name": "created google sheet name",
"platform": "android/iOS"
}
If you look closely, Localized String Generator actually supports module-level granularity.
So, if we need to, we can manage string copies for each module separately in different spreadsheets.
3. Localized String Generator tool
This is the most important part of the project. We’ve written the whole tool in Kotlin and generated an executable JAR that does all the magic. There are a few responsibilities of the JAR:
▹ Generating a CSV file
The generate-csv command generates CSV files by looking at the string_resources_folder_path from the Configuration JSON file. It can generate CSV files for both Android and iOS depending on the platform mentioned in the Configuration JSON file.
▹ Add or Update Remote Strings
After using the add-update-remote-strings command, all new strings will be added to the remote Google Sheet. Any existing strings which are marked as yes
under the Verified by UxWriter
column will not be updated.
▹ Update Local Strings
With the update-local-strings command, strings files will be generated using strings available in the remote Google Sheet, and existing string files in the project will be overwritten.
▹ Update Local Strings and Release
With the update-local-strings-and-release command, strings files will be generated using strings available in the remote Google Sheet, and existing string files in the project will be overwritten. Furthermore, the CSV generated from Google Sheet will be committed and pushed to the Git repository with the commit message passed in parameter while executing the command.
▹ Compare Local CSVs
The compare-local-csv command compares two local CSV files. It’s helpful to print a summary of changes in before and after format.
Note: Localized String Generator uses Google Sheets API to perform any read and write operations on our Google Sheet.
Localized String Generator is not an end product yet, and has further scope for improvement. The prior knowledge we had while working on the initial approach definitely helped us along in this journey and when we look back at what we achieved with this project, we couldn’t be more proud. Inside Gojek, the Localized String Generator now has been adopted by multiple teams in addition to the GoPay team.
The number of teams that have begun to use Localized String Generator is constantly increasing and we couldn’t be happier. 😁
To read more stories from our vault, click here.
And we’re hiring! Click below to view open job positions.