Skip to content

Managing localization

Wouter Koppenol edited this page Feb 10, 2026 · 12 revisions

Localization overview

All documentation for localization can be found at https://laravel.com/docs/12.x/localization.

One thing you should know of is the command php artisan localization:sync en_US <target_locale>. This command will take the translation strings of the first language and add the missing ones to the second locale. This is useful when adding translation strings. You will add them to (most likely) en_US and use this command to copy them over to the remaining localizations.

If you do not pass a second locale, it will add the strings to all other locales.

Adding a new localization

In file config/language.php you can find the key allowed which will allow you to add another locale to the existing list. Note: the key all should also have this locale, along with an image in https://github.com/RaiderIO/keystone.guru.assets/tree/main/images/flags

Switching localizations

This is again all handled in the config/language.php. A route is defined there and it handles the setting of the locale to the proper user.

Crowdin integration

Crowdin is a platform for Crowd Internationalization. It allows you to publish your translation strings/keys and have them be translated by the public, machines or professionals. Keystone.guru has an integration with Crowdin, but it comes with a manual. This manual.

Order of operations

WARNING: this order of operations is logical, from source to Crowdin and back, but when working with Crowdin you want to ALWAYS pull changes first, otherwise you will lose translations done by volunteers in Crowdin! See section Exporting localizations.csv into Laravel

Implementation

Keystone.guru requires a package called lukasss93/laravel-larex. This package provides a way for Laravel to interface with a single localizations.csv file and vice versa. Then, another package called lukasss93/laravel-larex-crowdin provides integrations to upload this localizations.csv to Crowdin and vice versa. This distinction is very important to know.

Writing localizations.csv after making changes to Laravel translations

The Artisan commands provided by laravel-larex are nice, but a few of them are needed to produce a localizations.csv that we can work with. In order for this file to be generated properly, I have created a new artisan command which you can call as follows:

php artisan larex:write-ksg

This will do the following:

  • Remove the localization.csv (otherwise it will complain it exists already)
  • Import the translations from the Laravel files, but exclude certain groups (currently datatables, dungeons, npcs, spells, view_admin and validation, these do not need to be translated)
  • Replaces the Laravel locales with locales that Crowdin understands (de-DE -> de for example)
  • Move en to the first column, since that is the source language (for some reason de is the first one, probably because alphabet)
  • Removes zh_TW and ho_HO because Crowdin doesn't recognize Taiwanese, and the Hodor language because.. well, it's just Hodor anyway 😄

Uploading localizations.csv to Crowdin

Now that the localizations.csv is written to disk we need to upload it to Crowdin properly. You can do that as follows:

php artisan larex:export crowdin

This will take the localizations.csv and uploads the contents to Crowdin.

Downloading localizations.csv from Crowdin

People have done translations for you and you want to include them in Keystone.guru. You can do that with the following command:

rm -f lang/localization.csv && php artisan larex:import crowdin

You can also use php artisan larex:write-ksg-from-crowdin but you won't see the progress. The end result will be a new localizations.csv which you can then import into the Laravel translations

Exporting localizations.csv into Laravel

I created a new command which you can use for this:

php artisan larex:read-ksg

It will ensure localization.csv exists, if not, throws an error. It will then ensure that the headers are Laravel-compatible. Finally, this will apply localizations.csv to the Laravel translations, allowing you to use the crowd-sourced translations. Yay!

Translating new strings through OpenAI

We can use Crowdin to have people supply translations, but when we don't have people doing that, we need to rely on something else. I integrated with laravel-auto-translation to make this possible.

Api key

To get started, you're going to need an API key from OpenAI (or any other provider). You need to charge your account/organization with some tokens before this will all work.

CHATGPT_API_KEY=sk-proj-<your key>
CHATGPT_MODEL=gpt-4o
TRANSLATION_DEFAULT_DRIVER=openai
TRANSLATION_SOURCE_LANGUAGE=en_US

Choosing a model

I tried to analyse some text through various models: gpt-4.1-nano, gpt-4.1-mini, gpt-4.1 and gpt-4o. Each model costs more than the one before it. But the results also improved with each model. I translated some keys to Dutch so that I can judge the translation myself, and gpt-4o was the clear winner. I also analysed the results with regular ChatGPT 5.1 and it agreed (there's something funny about AI analysing the work of other AIs but I digress).

Costs wise, I charged my account with $10 and I managed to translate ALL of Keystone.guru into 10 languages, and not run out of credits. So spending some more money on using the best model was an easy choice.

Translating new keys

I added support in laravel-auto-translation for this when you use language folders instead of language .json files. You can add new keys to en_US, and only the keys missing in the target locale will be translated. Right now, what you need to run is the following:

php artisan translate:scan --exclude-files=datatables,dungeons,npcs,spells,view_admin,validation

This will create a file called lang/texts_to_translate.json. Any subsequent requests to translate keys will now source its info from this file. Review this file before spending money on running the following command(s)

php artisan translate:ksg pt_BR openai

This command will look into lang/texts_to_translate.json and translate all keys that are not empty in Portugese (Brasilian). Any empty keys in the target language will be translated (we do not want to re-translate keys, since that's pointless, costs time and money). If you want to re-translate something, empty out the translated string.

Updating Datatables translations

I have made two commands to help with this. The first fetches translations from the Datatables repository:

php artisan localization:dt-download

Note: you do have to edit the version number manually to match

The second command takes the created files and applies them to our own translation files:

php artisan localization:dt-convert

Your datatables will now be updated.

Clone this wiki locally