Rupert  Beatty

Rupert Beatty


BartyCrouch: Localization/I18n


BartyCrouch incrementally updates your Strings files from your Code and from Interface Builder files. "Incrementally" means that BartyCrouch will by default keep both your already translated values and even your altered comments. Additionally you can also use BartyCrouch for machine translating from one language to 60+ other languages. Using BartyCrouch is as easy as running a few simple commands from the command line what can even be automated using a build script within your project.

Checkout this blog post to learn how you can effectively use BartyCrouch in your projects.


  • Xcode 13.3+ & Swift 5.6+
  • Xcode Command Line Tools (see here for installation instructions)

Getting Started


Via Homebrew

To install Bartycrouch the first time, simply run the command:

brew install bartycrouch

To update to the newest version of BartyCrouch when you have an old version already installed run:

brew upgrade bartycrouch

Via Mint

To install or update to the latest version of BartyCrouch simply run this command:

mint install FlineDev/BartyCrouch


To configure BartyCrouch for your project, first create a configuration file within your projects root directory. BartyCrouch can do this for you:

bartycrouch init

Now you should have a file named .bartycrouch.toml with the following contents:

tasks = ["interfaces", "code", "transform", "normalize"]

paths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
defaultToBase = false
ignoreEmptyStrings = false
unstripped = false
ignoreKeys = ["#bartycrouch-ignore!", "#bc-ignore!", "#i!"]

codePaths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
localizablePaths = ["."]
defaultToKeys = false
additive = true
unstripped = false
ignoreKeys = ["#bartycrouch-ignore!", "#bc-ignore!", "#i!"]

codePaths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
localizablePaths = ["."]
transformer = "foundation"
supportedLanguageEnumPath = "."
typeName = "BartyCrouch"
translateMethodName = "translate"

paths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
sourceLocale = "en"
harmonizeWithSource = true
sortByKeys = true

paths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
duplicateKeys = true
emptyValues = true

This is the default configuration of BartyCrouch and should work for most projects as is. In order to use BartyCrouch to its extent, it is recommended though to consider making the following changes:

  1. To speed it up significantly, provide more specific paths for any key containing path if possible (especially in the update.transform section, e.g. ["App/Sources"] for codePaths or ["App/Supporting Files"] for supportedLanguageEnumPaths).
  2. Remove the code task if your project is Swift-only and you use the new transform update task.
  3. If you are using SwiftGen with the structured-swift4 template, you will probably want to use the transform task and change its transformer option to swiftgenStructured.
  4. If you decided to use the transform task, create a new file in your project (e.g. under SupportingFiles) named BartyCrouch.swift and copy the following code:
//  This file is required in order for the `transform` task of the translation helper tool BartyCrouch to work.
//  See here for more details:

import Foundation

enum BartyCrouch {
    enum SupportedLanguage: String {
        // TODO: remove unsupported languages from the following cases list & add any missing languages
        case arabic = "ar"
        case chineseSimplified = "zh-Hans"
        case chineseTraditional = "zh-Hant"
        case english = "en"
        case french = "fr"
        case german = "de"
        case hindi = "hi"
        case italian = "it"
        case japanese = "ja"
        case korean = "ko"
        case malay = "ms"
        case portuguese = "pt-BR"
        case russian = "ru"
        case spanish = "es"
        case turkish = "tr"

    static func translate(key: String, translations: [SupportedLanguage: String], comment: String? = nil) -> String {
        let typeName = String(describing: BartyCrouch.self)
        let methodName = #function

            "Warning: [BartyCrouch]",
            "Untransformed \(typeName).\(methodName) method call found with key '\(key)' and base translations '\(translations)'.",
            "Please ensure that BartyCrouch is installed and configured correctly."

        // fall back in case something goes wrong with BartyCrouch transformation
  1. If you don't develop in English as the first localized language, you should update the sourceLocale of the normalize task.
  2. If you want to use the machine translation feature of BartyCrouch, add translate to the tasks list at the top and copy the following section into the configuration file with secret replaced by your Microsoft Translator Text API Subscription Key:
paths = "."
translator = "microsoftTranslator"
secret = "<#Subscription Key#>"
sourceLocale = "en"


Before using BartyCrouch please make sure you have committed your code. Also, we highly recommend using the build script method described below.

bartycrouch accepts one of the following sub commands:

  • update: Updates your .strings file contents according to your configuration.
  • lint: Checks your .strings file contents for empty values & duplicate keys.

Also the following command line options can be provided:

  • -v, --verbose: Prints more detailed information about the executed command.
  • -x, --xcode-output: Prints warnings & errors in Xcode compatible format.
  • -w, --fail-on-warnings: Returns a failed status code if any warning is encountered.
  • -p, --path: Specifies a different path than current to run BartyCrouch from there.

update subcommand

The update subcommand can be run with one or multiple of the following tasks:

  • interfaces: Updates .strings files of Storyboards & XIBs.
  • code: Updates Localizable.strings file from NSLocalizedString entries in code.
  • transform: A mode where BartyCrouch replaces a specific method call to provide translations in multiple languages in a single line. Only supports Swift files.
  • translate: Updates missing translations in other languages than the source language.
  • normalize: Sorts & cleans up .strings files.

In order to configure which tasks are executed, edit this section in the config file:

tasks = ["interfaces", "code", "transform", "normalize"]

Options for interfaces

  • paths: The directory / directories to search for Storyboards & XIB files.
  • subpathsToIgnore: The subpaths to be ignored inside the directories found via the paths option.
  • defaultToBase: Add Base translation as value to new keys.
  • ignoreEmptyStrings: Doesn't add views with empty values.
  • unstripped: Keeps whitespaces at beginning & end of Strings files.
  • ignoreKeys: Keys (e.g. in the comment) indicating that specific translation entries should be ignored when generating String files. Useful to ignore strings that are gonna be translated in code.

Options for code

  • codePaths: The directory / directories to search for Swift code files.
  • subpathsToIgnore: The subpaths to be ignored inside the directories found via the paths option.
  • localizablePaths: The enclosing path(s) containing the localized Localizable.strings files.
  • defaultToKeys: Add new keys both as key and value.
  • additive: Prevents cleaning up keys not found in code.
  • customFunction: Use alternative name to search for strings to localize, in addition to NSLocalizedString, and CFCopyLocalizedString. Defaults to LocalizedStringResource.
  • customLocalizableName: Use alternative name for Localizable.strings.
  • unstripped: Keeps whitespaces at beginning & end of Strings files.
  • plistArguments: Use a plist file to store all the code files for the ExtractLocStrings tool. (Recommended for large projects.)
  • ignoreKeys: Keys (e.g. in the comment) indicating that specific translation entries should be ignored when generating String files.
  • overrideComments: Always overrides the comment with the keys new translation, useful for IB files.

Options for transform

  • codePaths: The directory / directories to search for Swift code files.
  • subpathsToIgnore: The subpaths to be ignored inside the directories found via the paths option.
  • localizablePaths: The enclosing path(s) containing the localized Localizable.strings files.
  • transformer: Specifies the replacement code. Use foundation for NSLocalizedString or swiftgenStructured for L10n entries.
  • supportedLanguageEnumPath: The enclosing path containing the SupportedLanguage enum.
  • typeName: The name of the type enclosing the SupportedLanguage enum and translate method.
  • translateMethodName: The name of the translate method to be replaced.
  • customLocalizableName: Use alternative name for Localizable.strings.
  • separateWithEmptyLine: Set to false if you don't want to have empty lines between Strings entries. Defaults to `true.

Options for translate

  • paths: The directory / directories to search for Strings files.
  • subpathsToIgnore: The subpaths to be ignored inside the directories found via the paths option.
  • translator: Specifies the translation API. Use microsoftTranslator or deepL.
  • secret: Your Microsoft Translator Text API Subscription Key or Authentication Key for DeepL API.
  • sourceLocale: The source language to translate from.
  • separateWithEmptyLine: Set to false if you don't want to have empty lines between Strings entries. Defaults to `true.

Options for normalize

  • paths: The directory / directories to search for Strings files.
  • subpathsToIgnore: The subpaths to be ignored inside the directories found via the paths option.
  • sourceLocale: The source language to harmonize keys of other languages with.
  • harmonizeWithSource: Synchronizes keys with source language.
  • sortByKeys: Alphabetically sorts translations by their keys.
  • separateWithEmptyLine: Set to false if you don't want to have empty lines between Strings entries. Defaults to `true.

lint subcommand

The lint subcommand was designed to analyze a project for typical translation issues. The current checks include:

  • duplicateKeys: Finds duplicate keys within the same file.
  • emptyValues: Finds empty values for any language.

Note that the lint command can be used both on CI and within Xcode via the build script method:

  • In Xcode the -x or --xcode-output command line argument should be used to get warnings which point you directly to the found issue.
  • When running on the CI you should specify the -w or --fail-on-warnings argument to make sure BartyCrouch fails if any warnings are encountered.

Localization Workflow via transform

When the transform update task is configured (see recommended step 4 in the Configuration section above) and you are using the build script method, you can use the following simplified process for writing localized code during development:

  1. Instead of NSLocalizedString calls you can use BartyCrouch.translate and specify a key, translations (if any) and optionally a comment. For example:
self.title = BartyCrouch.translate(key: "onboarding.first-page.header-title",  translations: [.english: "Welcome!"])
  1. Once you build your app, BartyCrouch will automatically add the new translation key to all your Localizable.strings files and add the provided translations as values for the provided languages.
  2. Additionally, during the same build BartyCrouch will automatically replace the above call to BartyCrouch.translate with the proper translation call, depending on your transformer option setting.

The resulting code depends on your transformer option setting:

When set to foundation, the above code will transform to:

self.title = NSLocalizedString("onboarding.first-page.header-title", comment: "")

When set to swiftgenStructured it will transform to:

self.title = L10n.Onboarding.FirstPage.headerTitle

Advantages of transform over the code task:

  • You can provide translations for keys without switching to the Strings files.
  • In case you use SwiftGen, you don't need to replace calls to NSLocalizedString with L10n calls manually after running BartyCrouch.
  • Can be combined with the machine translation feature to provide a source language translation in code and let BartyCrouch translate it to all supported languages in a single line & without ever leaving the code.

Disadvantages of transform over the code task:

  • Only works for Swift Code. No support for Objective-C. (You can use both methods simultaneously though.)
  • Xcode will mark the freshly transformed code as errors (but build will succeed anyways) until next build.
  • Not as fast as code since SwiftSyntax currently isn't particularly fast. (But this should improve over time!)

NOTE: As of version 4.x of BartyCrouch formatted localized Strings are not supported by this automatic feature.

Localizing strings of LocalizableStringResource type (AppIntents, ...)

Historically, Apple platforms used CFCopyLocalizedString, and NSLocalizedString macros and their variants, to mark strings used in code to be localized, and to load their localized versions during runtime from Localizable.strings file.

Since introduction of the AppIntents framework, the localized strings in code can also be typed as LocalizedStringResource, and are no longer marked explicitly.

Let's examine this snippet of AppIntents code:

struct ExportAllTransactionsIntent: AppIntent {
    static var title: LocalizedStringResource = "Export all transactions"
    static var description =
        IntentDescription("Exports your transaction history as CSV data.")

In the example above, both the "Export all transactions", and "Exports your transaction history as CSV data." are actually StaticString instances that will be converted during compilation into LocalizedStringResource instances, and will lookup their respective localizations during runtime from Localized.strings file the same way as when using NSLocalizedString in the past. The only exception being that such strings are not marked explicitly, and require swift compiler to parse and extract such strings for localization. This is what Xcode does from version 13 when using Product -> Export Localizations... option.

In order to continue translating these strings with bartycrouch it is required to mark them explicitely with LocalizedStringResource(_: String, comment: String) call, and specify customFunction="LocalizedStringResource" in code task options.

The example AppIntents code that can be localized with bartycrouch will look like this:

struct ExportAllTransactionsIntent: AppIntent {
    static var title = LocalizedStringResource("Export all transactions", comment: "")
    static var description =
        IntentDescription(LocalizedStringResource("Exports your transaction history as CSV data.", comment: ""))

Note that you must use the full form of LocalizedStringResource(_: StaticString, comment: StaticString) for the bartycrouch, or more specifically for the extractLocStrings (see xcrun extractLocStrings) to properly parse the strings.

Build Script

In order to truly profit from BartyCrouch's ability to update & lint your .strings files you can make it a natural part of your development workflow within Xcode. In order to do this select your target, choose the Build Phases tab and click the + button on the top left corner of that pane. Select New Run Script Phase and copy the following into the text box below the Shell: /bin/sh of your new run script phase:

export PATH="$PATH:/opt/homebrew/bin"

if which bartycrouch > /dev/null; then
    bartycrouch update -x
    bartycrouch lint -x
    echo "warning: BartyCrouch not installed, download it from"

Next, make sure the BartyCrouch script runs before the steps Compiling Sources (and SwiftGen if used) by moving it per drag & drop, for example right after Target Dependencies.

Now BartyCrouch will be run on each build and you won't need to call it manually ever (again). Additionally, all your co-workers who don't have BartyCrouch installed will see a warning with a hint on how to install it.

Note: Please make sure you commit your code using source control regularly when using the build script method.

Exclude specific Views / NSLocalizedStrings from Localization

Sometimes you may want to ignore some specific views containing localizable texts e.g. because their values are going to be set programmatically.

For these cases you can simply include #bartycrouch-ignore! or the shorthand #bc-ignore! into your value within your base localized Storyboard/XIB file. Alternatively you can add #bc-ignore! into the field "Comment For Localizer" box in the utilities pane.

This will tell BartyCrouch to ignore this specific view when updating your .strings files.

Here's an example of how a base localized view in a XIB file with partly ignored strings might look like:

Here's an example with the alternative comment variant:

You can also use #bc-ignore! in your NSLocalizedString macros comment part to ignore them so they are not added to your Localizable.strings. This might be helpful when you are using a .stringsdict file to handle pluralization (see docs).

For example you can do something like this:

func updateTimeLabel(minutes: Int) {
  String.localizedStringWithFormat(NSLocalizedString("%d minute(s) ago", comment: "pluralized and localized minutes #bc-ignore!"), minutes)

The %d minute(s) ago key will be taken from Localizable.stringsdict file, not from Localizable.strings, that's why it should be ignored by BartyCrouch.


BartyCrouch was brought to you by Cihat Gündüz in his free time. If you want to thank me and support the development of this project, please make a small donation on PayPal. In case you also like my other open source contributions and articles, please consider motivating me by becoming a sponsor on GitHub or a patron on Patreon.

Thank you very much for any donation, it really helps out a lot! 💯

Migration Guides

See the file


Contributions are welcome. Feel free to open an issue on GitHub with your ideas or implement an idea yourself and post a pull request. If you want to contribute code, please try to follow the same syntax and semantic in your commit messages (see rationale here). Also, please make sure to add an entry to the file which explains your change.

In order for the tests to run build issues, you need to run – also add an an API key in the new file to run the translations tests, too:

cp Tests/BartyCrouchTranslatorTests/Secrets/secrets.json.sample Tests/BartyCrouchTranslatorTests/Secrets/secrets.json

After Release Checklist:

  1. Run make portable_zip to generate .build/release/
  2. Create new release with text from new section & attach as binary
  3. Run pod trunk push to make a new release known to CocoaPods
  4. Update tag and revision in Formula/bartycrouch.rb, commit & push change
  5. Run brew bump-formula-pr bartycrouch --tag=<tag> --revision=<revision>

Important Notice

There's now a new Mac app called ReMafoX which is the successor to BartyCrouch. It improves upon several aspects of BartyCrouch, such as having no flaky dependencies, adding pluralization support, smart machine translation, a built-in SwiftUI-compatible enum generator, built-in step-by-step instructions for easier setup, detailed explanations of all config options, and even a set of video guides for things like setup, key naming best practices and team onboarding. Get it for free here.

Note that ReMafoX is being actively worked on, you can even vote for or request new features here. In comparison, BartyCrouch is kept up-to-date mostly by the community.

Download Details:

Author: FlineDev
Source Code: 
License: MIT license

#swift #language #translation #localization #xcode 

BartyCrouch: Localization/I18n

Translates JSON into A Go Type In Your Browser Instantly (original)

Translates JSON into a Go type definition. Check it out!

This is a sister tool to curl-to-Go, which converts curl commands to Go code.

Things to note:

  • The script sometimes has to make some assumptions, so give the output a once-over.
  • In an array of objects, it is assumed that the first object is representative of the rest of them.
  • The output is indented, but not formatted. Use go fmt!

Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change.


JSON-to-Go is brought to you by Matt Holt (mholt6).

The Go Gopher is originally by Renee French. This artwork is an adaptation.

JSON-to-Go converts JSON to a Go struct

Download Details:

Author: mholt
Source Code: 
License: MIT license

#go #golang #json #translation 

Translates JSON into A Go Type In Your Browser Instantly (original)

The Translation Component Provides tools To internationalize Your App

Translation Component

The Translation component provides tools to internationalize your application.

Getting Started

$ composer require symfony/translation
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;

$translator = new Translator('fr_FR');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
    'Hello World!' => 'Bonjour !',
], 'fr_FR');

echo $translator->trans('Hello World!'); // outputs « Bonjour ! »


The Translation component for Symfony 6.1 is backed by:

  • Crowdin, a cloud-based localization management software helping teams to go global and stay agile.

Help Symfony by sponsoring its development!


Download Details:

Author: Symfony
Source Code: 
License: MIT license

#php #i18n #translation #symfony 

The Translation Component Provides tools To internationalize Your App
Rupert  Beatty

Rupert Beatty


Linguist: Multilingual Urls and Redirects for Laravel

Linguist - Multilingual urls and redirects for Laravel

This package provides an easy multilingual urls and redirection support for the Laravel framework.

In short Laravel will generate localized urls for links and redirections.


Linguist works perfectly well with named Laravel routes for javascript package!


Linguist is very easy to use. The locale slug is removed from the REQUEST_URI leaving the developer with the cleanest multilingual environment possible.

Install using Composer:

composer require keevitaja/linguist

There are several options to make Linguist work.

Option 1: Modify the public/index.php

Add following line after the vendor autoloading to your projects public/index.php file.

(new Keevitaja\Linguist\UriFixer)->fixit();

End result would be this:

| Register The Auto Loader
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.

require __DIR__.'/../vendor/autoload.php';

(new Keevitaja\Linguist\UriFixer)->fixit();

Option 2: Use LocalizedKernel

Note: This option works only if you have not changed your applications root namespace. Default is App.

In your projects bootstrap/app.php swap the App\Http\Kernel with Keevitaja\Linguist\LocalazedKernel:

| Bind Important Interfaces
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.


Option 3: modify the App\Http\Kernel

Note: This also works with custom root namespace.


namespace App\Http;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Routing\Router;
use Keevitaja\Linguist\UriFixer;

class Kernel extends HttpKernel

    public function __construct(Application $app, Router $router)
        (new UriFixer)->fixit();

        parent::__construct($app, $router);

Publish config

Finally you need to publish the Linguist config to set your enabled locales and other relavant configurations.

php artisan vendor:publish --provider="Keevitaja\Linguist\LinguistServiceProvider"

Your personal configuration file will be config/linguist.php.


You can add the LocalizeUrls middleware your web middleware group as the first item to get the linguist support:

 * The application's route middleware groups.
 * @var array
protected $middlewareGroups = [
    'web' => [

Note: This middleware has to be the first item in group!

Another option is to use Linguist in your applications service provider:

class AppServiceProvider extends ServiceProvider
     * Bootstrap any application services.
     * @return void
    public function boot(\Keevitaja\Linguist\Linguist $linguist)

UrlGenerator will add the locale slug in front of the URI when needed. No extra actions needed.

Route::get('people', ['as' => 'people.index', 'uses' => ''PeopleController@index'']);
{{ route('people.index') }} or {{ url('people') }} // default locale from linguist config

Switcher is a little helper to get the current URLs for the locale switcher.

$urls = dispatch_now(new \Keevitaja\Linguist\Switcher);

NB! Both config and route caching are working!


Use linguist helpers for a correct routing of assets

Regular Assets

<link rel="stylesheet" href="{{ linguist_asset('css/style.css') }}">
<script type="text/javascript" src="{{ linguist_asset('js/my_js.js') }}"></script>

Secure Assets

<link rel="stylesheet" href="{{ secure_linguist_asset('css/style.css') }}">
<script type="text/javascript" src="{{ secure_linguist_asset('js/my_js.js') }}"></script>


To make localization work in queues you need to run Linguist->localize($theLocaleYouWant) inside the queued item.

Author: Keevitaja
Source Code: 
License: MIT license

#laravel #i18n #php #translation #localization 

Linguist: Multilingual Urls and Redirects for Laravel
Rupert  Beatty

Rupert Beatty


Translation: Better Translation Management for Laravel

Better localization management for Laravel


Keeping a project's translations properly updated is cumbersome. Usually translators do not have access to the codebase, and even when they do it's hard to keep track of which translations are missing for each language or when updates to the original text require that translations be revised.

This package allows developers to leverage their database and cache to manage multilanguage sites, while still working on language files during development and benefiting from all the features Laravel's Translation bundle has, like pluralization or replacement.

WAAVI is a web development studio based in Madrid, Spain. You can learn more about us at

Laravel compatibility

5.5.x2.3.x and higher
5.6.x2.3.x and higher
6.x|7.x2.4.x and higher

Features overview

  • Allow dynamic changes to the site's text and translations.
  • Cache your localization entries.
  • Load your translation files into the database.
  • Force your urls to be localized (ex: /home -> /es/home) and set the locale automatically through the browser's config.
  • Localize your model attributes.


Require through composer

composer require waavi/translation 2.3.x

Or manually edit your composer.json file:

"require": {
	"waavi/translation": "2.3.x"

Once installed, in your project's config/app.php file replace the following entry from the providers array:




Remove your config cache:

php artisan config:clear

Publish both the configuration file and the migrations:

php artisan vendor:publish --provider="Waavi\Translation\TranslationServiceProvider"

Execute the database migrations:

php artisan migrate

You may check the package's configuration file at:


Translations source

This package allows you to load translation from the regular Laravel localization files (in /resources/lang), from the database, from cache or in a mix of the previous for development. You may configure the desired mode of operation through the translator.php config file and/or the TRANSLATION_SOURCE environment variable. Accepted values are:

  • 'files' To load translations from Laravel's language files (default)
  • 'database' To load translations from the database
  • 'mixed' To load translations both from the filesystem and the database, with the filesystem having priority.
  • 'mixed_db' To load translations both from the filesystem and the database, with the database having priority. [v2.1.5.3]

NOTE: When adding the package to an existing Laravel project, 'files' must be used until migrations have been executed.

For cache configuration, please go to cache configuration

Load translations from files

If you do not wish to leverage your database for translations, you may choose to load language lines exclusively through language files. This mode differs from Laravel in that, in case a line is not found in the specified locale, instead of returning the key right away, we first check the default language for an entry. In case you wish to use this mode exclusively, you will need to set the 'available_locales' config file:

	'available_locales' => ['en', 'es', 'fr'],


The content in en/validations.php, where 'en' is the default locale, is:

			'missing_name'			=>	'Name is missing',
			'missing_surname'		=>	'Surname is missing',

The content in es/validations.php is:

			'missing_name'			=>	'Falta el nombre',

Output for different keys with 'es' locale:

trans('validations.missing_name'); 		// 		'Falta el nombre'
		trans('validations.missing_surname'); 	// 		'Surname is missing'
		trans('validations.missing_email'); 	// 		'validations.missing_email'

Load translations from the database

You may choose to load translations exclusively from the database. This is very useful if you intend to allow users or administrators to live edit the site's text and translations. In a live production environment, you will usually want this source mode to be activated with the translation's cache. Please see Loading your files into the database for details on the steps required to use this source mode.


The content in the languages table is:

	| id | locale | name    |
	| 1  | en     | english |
	| 2  | es     | spanish |

The relevant content in the language_entries table is:

	| id | locale | namespace | group       | item	          | text                    |
	| 1  | en     | *         | validations |    | Name is missing         |
	| 2  | en     | *         | validations | missing.surname | Surname is missing      |
	| 3  | en     | *         | validations | min_number      | Number is too small     |
	| 4  | es     | *         | validations |    | Falta nombre   			|
	| 5  | es     | *         | validations | missing.surname | Falta apellido 			|

Output for different keys with es locale:

trans('');   //    'Falta nombre'
		trans('validations.min_number');     //    'Number is too small'
		trans('');  //    'missing_email'

Mixed mode

In mixed mode, both the language files and the database are queried when looking for a group of language lines. Entries found in the filesystem take precedence over the database. This source mode is useful when in development, so that both the filesystem and the user entries are taken into consideration.


When files and database are set like in the previous examples:
trans('validations.missing_name');     //    'Falta el nombre'
		trans('validations.missing_surname');  //    'Falta apellido'
		trans('validations.min_number');       //    'Number is too small'
		trans('validations.missing_email');    //    'missing_email'

Loading your files into the database

When using either the database or mixed translation sources, you will need to first load your translations into the database. To do so, follow these steps:

Run the migrations detailed in the installation instructions.

Add your languages of choice to the database (see Managing Database Languages)

Load your language files into the database using the provided Artisan command:

php artisan translator:load

When executing the artisan command, the following will happen:

  • Non existing entries will be created.
  • Existing entries will be updated except if they're locked. When allowing users to live edit the translations, it is recommended you do it throught the updateAndLock method provided in the Translations repository. This prevents entries being overwritten when reloading translations from files.
  • When an entry in the default locale is edited, all of its translations will be flagged as pending review. This gives translators the oportunity to review translations that might not be correct, but doesn't delete them so as to avoid minor errata changes in the source text from erasing all translations. See Managing translations for details on how to work with unstable translations.

Both vendor files and subdirectories are supported. Please keep in mind that when loading an entry inside a subdirectory, Laravel 5 has changed the syntax to:


Cache translations

Since querying the database everytime a language group must be loaded is grossly inefficient, you may choose to leverage Laravel's cache system. This module will use the same cache configuration as defined by you in app/config/cache.php.

You may enable or disable the cache through the translator.php config file or the 'TRANSLATION_CACHE_ENABLED' environment variable. Config options are:

Env keytypedescription
TRANSLATION_CACHE_ENABLEDbooleanEnable / disable the translations cache
TRANSLATION_CACHE_TIMEOUTintegerMinutes translation items should be kept in the cache.
TRANSLATION_CACHE_SUFFIXstringDefault is 'translation'. This will be the cache suffix applied to all translation cache entries.

Cache tags

Available since version, if the cache store in use allows for tags, the TRANSLATION_CACHE_SUFFIX will be used as the common tag to all cache entries. This is recommended to be able to invalidate only the translation cache, or even just a given locale, namespace and group configuration.

Clearing the cache

Available since version, you may clear the translation cache through both an Artisan Command and a Facade. If cache tags are in use, only the translation cache will be cleared. All of your application cache will however be cleared if you cache tags are not available.

Cache flush command:

php artisan translator:flush

In order to access the translation cache, add to your config/app.php files, the following alias:

    'aliases'         => [
        /* ... */
        'TranslationCache' => \Waavi\Translation\Facades\TranslationCache::class,

Once done, you may clear the whole translation cache by calling:


You may also choose to invalidate only a given locale, namespace and group combination.

    \TranslationCache::flush($locale, $group, $namespace);
  • The locale is the language locale you wish to clear.
  • The namespace is either '*' for your application translation files, or 'package' for vendor translation files.
  • The group variable is the path to the translation file you wish to clear.

For example, say we have the following file in our resources/lang directory: en/auth.php, en/auth/login.php and en/vendor/waavi/login.php. To clear the cache entries for each of them you would call:

    \TranslationCache::flush('en', 'auth', '*');
    \TranslationCache::flush('en', 'auth/login', '*');
    \TranslationCache::flush('en', 'login', 'waavi');

Managing languages and translations in the Database

The recommended way of managing both languages and translations is through the provided repositories. You may circumvent this by saving changes directly through the Language and Translation models, however validation is no longer executed automatically on model save and could lead to instability and errors.

Both the Language and the Translation repositories provide the following methods:

hasTable();Returns true if the corresponding table exists in the database, false otherwise
all($related = [], $perPage = 0);Retrieve all records from the DB. A paginated record will be return if the second argument is > 0, with $perPage items returned per page
find($id);Find a record by id
create($attributes);Validates the given attributes and inserts a new record. Returns false if validation errors occured
delete($id);Delete a record by id
restore($id);Restore a record by id
count();Return the total number of entries
validate(array $attributes);Checks if the given attributes are valid
validationErrors();Get validation errors for create and update methods

Managing Languages

Language management should be done through the \Waavi\Translation\Repositories\LanguageRepository to ensure proper data validation before inserts and updates. It is recommended that you instantiate this class through Dependency Injection.

A valid Language record requires both its name and locale to be unique. It is recommended you use the native name for each language (Ex: English, Español, Français)

The provided methods are:

update(array $attributes);Updates a Language entry [id, name, locale]
trashed($related = [], $perPage = 0);Retrieve all trashed records from the DB.
findTrashed($id, $related = []);Find a trashed record by id
findByLocale($locale);Find a record by locale
findTrashedByLocale($locale);Finds a trashed record by locale
allExcept($locale);Returns a list of all languages excluding the given locale
availableLocales();Returns a list of all available locales
isValidLocale($locale);Checks if a language exists with the given locale
percentTranslated($locale);Returns the percent translated for the given locale

Managing Translations

Translation management should be done through the \Waavi\Translation\Repositories\TranslationRepository to ensure proper data validation before inserts and updates. It is recommended that you instantiate this class through Dependency Injection.

A valid translation entry cannot have the same locale and language code than another.

The provided methods are:

update($id, $text);Update an unlocked entry
updateAndLock($id, $text);Update and lock an entry (locked or not)
allByLocale($locale, $perPage = 0);Get all by locale
untranslated($locale, $perPage = 0, $text = null);Get all untranslated entries. If $text is set, entries will be filtered by partial matches to translation value.
pendingReview($locale, $perPage = 0);List all entries pending review
search($locale, $term, $perPage = 0);Search by all entries by locale and a partial match to both the text value and the translation code.
randomUntranslated($locale);Get a random untranslated entry
translateText($text, $textLocale, $targetLocale);Translate text to another locale
flagAsReviewed($id);Flag entry as reviewed

Things to consider:

  • You may lock translations so that they can only be updated through updateAndLock. The language file loader uses the update method and will not be able to override locked translations.
  • When a text entry belonging to the default locale is updated, all of its siblings are marked as pending review.
  • When deleting an entry, if it belongs to the default locale its translations will also be deleted.

Model attributes translation

You can also use the translation management system to manage your model attributes translations. To do this, you only need to:

  • Make sure either the database or mixed source are set.
  • Make sure your models use the Waavi\Translation\Translatable\Trait
  • In your model, add a translatableAttributes array with the names of the attributes you wish to be available for translation.
  • For every field you wish to translate, make sure there is a corresponding attributeName_translation field in your database.


\Schema::create('examples', function ($table) {

    class Example extends Model
	    use \Waavi\Translation\Traits\Translatable;
	    protected $translatableAttributes = ['title', 'text'];

Uri localization

You may use Waavi\Translation\Middleware\TranslationMiddleware to make sure all of your urls are properly localized. The TranslationMiddleware will only redirect GET requests that do not have a locale in them.

For example, if a user visits the url /home, the following would happen:

  • The middleware will check if a locale is present.
  • If a valid locale is present:
    • it will globally set the language for that locale
    • the following data will be available in your views:
      • currentLanguage: current selected Language instance.
      • selectableLanguages: list of all languages the visitor can switch to (except the current one)
      • altLocalizedUrls: a list of all localized urls for the current resource except this one, formatted as ['locale' => 'en', 'name' => 'English', 'url' => '/en/home']
  • If no locale is present:
    • Check the first two letters of the brower's accepted locale HTTP_ACCEPT_LANGUAGE (for example 'en-us' => 'en')
    • If this is a valid locale, redirect the visitor to that locale => /es/home
    • If not, redirect to default locale => /en/home
    • Redirects will keep input data in the url, if any

You may choose to activate this Middleware globally by adding the middleware to your App\Http\Kernel file:

protected $middleware = [
		/* ... */

Or to apply it selectively through the 'localize' route middleware, which is already registered when installing the package through the ServiceProvider.

It is recommended you add the following alias to your config/app.php aliases:

'aliases'         => [
		/* ... */
		'UriLocalizer'	=> Waavi\Translation\Facades\UriLocalizer::class,

Every localized route must be prefixed with the current locale:

// If the middleware is globally applied:
	Route::group(['prefix' => \UriLocalizer::localeFromRequest()], function(){
		/* Your routes here */

	// For selectively chosen routes:
	Route::group(['prefix' => \UriLocalizer::localeFromRequest(), 'middleware' => 'localize')], function () {
	    /* Your routes here */

Starting on v2.1.6, you may also specify a custom position for the locale segment in your url. For example, if the locale info is the third segment in a URL (/api/v1/es/my_resource), you may use:

    // For selectively chosen routes:
    Route::group(['prefix' => 'api/v1'], function() {
        /** ... Non localized urls here **/

        Route::group(['prefix' => \UriLocalizer::localeFromRequest(2), 'middleware' => 'localize:2')], function () {
            /* Your localized routes here */

In your views, for routes where the Middleware is active, you may present the user with a menu to switch from the current language to another by using the shared variables. For example:

<li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ $currentLanguage->name }} <b class="caret"></b></a>
    <ul class="dropdown-menu">
        @foreach ($altLocalizedUrls as $alt)
            <li><a href="{{ $alt['url'] }}" hreflang="{{ $alt['locale'] }}">{{ $alt['name'] }}</a></li>

Author: Waavi
Source Code: 
License: MIT license

#laravel #translation #management 

Translation: Better Translation Management for Laravel
Royce  Reinger

Royce Reinger


Translations with Speech Synthesis in Your Terminal As A Ruby Gem


Termit is an easy way to translate stuff in your terminal. You can check out its node.js npm version normit.


gem install termit


termit 'source_language' 'target_language' 'text'


termit en es "hey cowboy where is your horse?"
=> "Hey vaquero dónde está tu caballo?"

termit fr en "qui est votre papa?"
=> "Who's Your Daddy?"

Quotation marks are not necessary for text data input:

termit fr ru qui est votre papa
=> "Кто твой папочка?"

Speech synthesis

Specify a -t (talk) flag to use speech synthesis (requires mpg123):

termit en fr "hey cowboy where is your horse?" -t
=> "Hey cowboy où est votre cheval ?" # and a french voice says something about a horse

You can use termit as a speech synthesizer of any supported language without having to translate anything:

termit en en "hold your horses cowboy !" -t
=> "hold your horses cowboy !" # and an english voice asks you to hold on

Learning language when committing to git

Idea by Nedomas. See and hear your messages translated to target lang every time you commit. You can do this two ways: overriding the git command, and using a post-commit hook in git.

Override the git command (zsh only)

In ~/.zshrc

export LANG=es
git(){[[ "$@" = commit\ -m* ]]&&termit en $LANG ${${@:$#}//./} -t;command git $@}

I am no shell ninja so if you know how to make it work in bash then please submit a PR.

Using a post-commit hook

Add a file named post-commit to your project's .git/hooks directory, with this in it:

termit en es "`git log -1 --pretty=format:'%s'`" -t

Remember to switch the languages according to your preference.

If you want this to be in every one of your git repositories, see this Stack Overflow answer.

Language codes:

To find all available language codes visit


Works with Ruby 1.9.2 and higher.

To use speech synthesis you need to have mpg123 installed.

For Ubuntu:

sudo apt-get install mpg123

For MacOSX:

brew install mpg123


It was rewritten to work with Bing Translator . Thanks to Ragnarson for supporting it !


Termit works by scraping the private APIs and is therefore not recommended for use in production or on a large scale.

Author: Pawurb
Source Code: 
License: MIT license

#ruby #translation #google 

Translations with Speech Synthesis in Your Terminal As A Ruby Gem
Royce  Reinger

Royce Reinger


Ruby-gettext: A Pure Ruby Localization(L10n) Library & tool


gettext for Ruby

Gettext gem is a pure Ruby Localization(L10n) library and tool which is modeled after the GNU gettext package.

This library was called as "Ruby-GetText-Package". Since 2.3.0, this library is called just "gettext". You can call this library as "gettext gem" or "Ruby gettext" to distinguish from GNU gettext.

This library translates original messages to localized messages using client-side locale information(environment variable or CGI variable).

The tools for developers support creating, useing, and modifying localized message files(message catalogs).

Rails support has been removed.


  • Translate singular/plural messages with simple APIs (similar to GNU gettext)
  • Thread safety. Message resources are shared from all threads, but returns translated messages of the current thread's locale.
  • Tools to find message IDs
    • Extract message IDs to po-files using rxgettext from
      • ruby scripts
      • glade-2 XML file (.glade)
      • ERB file (.rhtml, .erb)
      • Anything (with your own parsers)
      • The po-files are compatible with GNU gettext.
    • rmsgfmt creates a mo-file from a po-file. The mo-file is compatible with GNU gettext (msgfmt).
    • Using rxgettext/rmsgfmt as Rake tasks
  • textdomain's scope is adapt to ruby class/module mechanism.
    • A class/module can have plural textdomains.
    • a message is looked up in its class/module and ancestors.
  • CGI support (gettext/cgi)
    • Locale is retrieved from client informations using Ruby-Locale. (HTTP_ACCEPT_LANGUAGE, HTTP_ACCEPT_CHARSET, QUERY_STRING (lang), Cookies (lang)).


gem install locale

For development:

  • Racc 1.4.3 or later (for compiling src/rmsgfmt.ry only)


Uninstall old gettext if exists. (You need to do this when updating 1.93.0 -> 2.0.1)

# sudo/su on POSIX system
gem uninstall gettext


# sudo/su on POSIX system
gem install gettext

Download tar-ball

# De-Compress archive and enter its top directory.
# sudo/su on POSIX system
ruby setup.rb

You can also install files in your favorite directory by supplying setup.rb some options. Try ruby setup.rb --help.



_() or gettext(): basic translation method

Translates the message, using the msgid if a translation is not found.

_("Hello") => "Bonjour"  # Found

This translation will appear in the po or pot file as:

msgid: "Hello"
msgstr: "Bonjour"

When a translation is not found it, it will return the msgid. This is a core benefit of gettext and applies to all its translation methods.

_("Hello") => "Hello"  # Not Found

Additional gettext methods come in 3 combinable flavors:

n_() or ngettext(): pluralized

Returns singular or plural form, depending on how many you have.

n_("Apple", "%{num} Apples", n) => "3 Pommes"  # When n = 3
n_("Apple", "%{num} Apple", n)  => "Pomme"     # When n = 1
n_(["Apple", "%{num} Apple"], n)  => "Pomme"   # 2 arg variation

This translation will appear in the po or pot file as:

msgid "Apple"
msgid_plural "%{num} Apples"
msgstr[0] "Pomme"
msgstr[1] "#{num} Pommes"

p_() or pgettext(): context aware

A context is a prefix to your translation, useful when one word has different meanings, depending on its context.

p_("Printer","Open") => "Öffnen" #translation found
p_("Printer","Open") => "Open"   #translation not found

This translation will appear in the po or pot file as:

msgctxt "Printer"
msgid "Open"
msgstr "Öffnen"

Note that the parser when sorting by msgid will strictly sort by the msgid ignoring the msgctxt. If you prefer to sort with the msgctxt you should consider the s_() method.

s_() or sgettext(): without context

The s_() method is very similar to the p_() method except that the context is inside the msgid.

s_("Printer|Open") => "Öffnen" #translation found
s_("Printer|Open") => "Open"   #translation not found
msgid "Printer|Open"
msgstr "Öffnen"

Note the the parser when sorting by msgid will take the context into consideration as it is part of the msgid unlike the p_() method.

Your preference of using s_() or p_() will depend on your translation workflow and process.


You can combine n_() with either p_() or s_().

np_() or npgettext(): context aware pluralized

np_("Fruit", "Apple", "%{num} Apples", 3)
np_(["Fruit","Apple","%{num} Apples"], 3) # 2 arg variation
msgctxt "Fruit"
msgid "Apple"
msgid_plural "%{num} Apples"
msgstr[0] ""
msgstr[1] ""

sp_() or spgettext(): context aware pluralized

ns_("Fruit|Apple","%{num} Apples", 3)
ns_(["Fruit|Apple","%{num} Apples"], 3) # 2 arg variation
msgid "Fruit|Apple"
msgid_plural "%{num} Apples"
msgstr[0] ""
msgstr[1] ""

N_() and Nn_(): makes dynamic translation messages readable for the gettext parser

_(fruit) cannot be understood by the gettext parser. To help the parser find all your translations, you can add fruit = N_("Apple") which does not translate, but tells the parser: "Apple" needs translation.

fruit = N_("Apple")   # same as fruit = "Apple"
_(fruit)              # does a normal translation

fruits = Nn_("Apple", "%{num} Apples")
n_(fruits, 3)

Interpolating translations

This is not a feature of gettext but worth noting. You can interpolate translated strings without the ruby String % operator.

N_("active"); N_("inactive"); N_("paused") # possible value of status for parser to find.
_("Your account is #{account_state}.") % { account_state: _(status) }

Bind textdomains to the classes

A textdomain has a translation file in each language. A module/class can have multiple textdomains. This means the libraries/applications can have their own textdomains.

class Foo
  include GetText
  bindtextdomain "your_app_domain_name"

class Book
  include GetText
  bindtextdomain "general"
  bindtextdomain "book"


If you need to set the locale by yourself, then use:

GetText.locale = "en_US" # translate into english from now on
GetText.locale # => en_US


include GetText
set_locale "en_US"

For more details and options, have a look at the samples folder.


This program is licenced under the same licence as Ruby (See doc/text/ruby-license.txt) or LGPL (Lesser General Public License: doc/text/lgpl-3.0.txt or


Copyright (C) 2001-2009 Masao Mutoh <mutoh at>
Copyright (C) 2001,2002 Masahiro Sakai <s01397ms at>


Copyright (C) 2001-2009 Masao Mutoh <mutoh at>
Copyright (C) 2001,2002 Masahiro Sakai <s01397ms at>


Copyright (C) 2001-2009 Masao Mutoh <mutoh at>
Copyright (C) 2001,2002 Yasushi Shoji <yashi at>


Copyright (C) 2001-2009 Masao Mutoh <mutoh at>


Bosnian (bs)Sanjin Sehic <saserr at>1.90.0 (old)
Bulgarian (bg)Sava Chankov <sava.chankov at>2.0.1
Catalan (ca)Ramon Salvadó <rsalvado at>2.0.1
Chinese (Simplified)(zh_CN)Yang Bob < at> (current) 
Yingfeng <blogyingfeng at>
Chinese (Traditional)(zh_TW)Yang Bob < at> (current) 
Lin Chung-Yi <xmarsh at>
Croatian (hr)Sanjin Sehic <saserr at>1.90.0 (old)
Czech (cs)Karel Miarka <kajism at>1.9.0 (old)
Dutch (nl)Menno Jonkers <ruby-gettext at>1.90.0 (old)
English (default) 2.1.0
Esperanto (eo)Malte Milatz <malte at>2.0.1
Estonian (et)Erkki Eilonen <erkki at>2.0.1
French (fr)Vincent Isambart <vincent.isambart at> (current) 
David Sulc <davidsulc at> 
Laurent Sansonetti <laurent.sansonetti at>
German (de)Patrick Lenz <patrick at> (current) 
Detlef Reichl <detlef.reichl at> 
Sven Herzberg <herzi at> 
Sascha Ebach <se at>
Greek (el)Vassilis Rizopoulos <damphyr at>2.0.1
Hungarian (hu)Tamás Tompa <tompata at>2.0.1
Italian (it)Marco Lazzeri <marco.lazzeri at> 
Gabriele Renzi <surrender_it at>
1.6.0 (old)
Japanese (ja)Masao Mutoh <mutomasa at>2.1.0
Korean (ko)Gyoung-Yoon Noh <nohmad at>1.9.0 (old)
Latvian (lv)Aivars Akots <aivars.akots at>2.0.1
Norwegian (nb)Runar Ingebrigtsen <runar at>2.0.1
Portuguese (Brazil)(pt_BR)Antonio S. de A. Terceiro <terceiro at> (current) 
Joao Pedrosa <joaopedrosa at>
Russian (ru)Yuri Kozlov <kozlov.y at>2.0.1
Serbian (sr)Slobodan Paunović <slobodan.paunovic at>2.0.1
Spanish (es)David Espada <davinci at> (current) 
David Moreno Garza <damog at>
Swedish (sv)Nikolai Weibull <mailing-lists.ruby-talk at>0.8.0 (very old)
Ukrainian (uk)Alex Rootoff <rootoff at>2.0.1
Vietnamese (vi)Ngoc Dao Thanh <ngocdaothanh at>2.0.1


  • Kouhei Sutou <>

Old maintainer

  • Masao Mutoh <mutomasa at>


NOTE: Gettext gem 3.0.0 removed many deprecated APIs and improves internal APIs. We want to keep backward compatibility as much as possible but some existing codes may be broken by gettext gem API change. If your code breaks by gettext gem 3.0.0, please report your problem. We will fix the problem and release a new version. is the official gettext gem repository. It is moved from . Mutoh agreed with the move.

Author: Ruby-gettext
Source Code: 

#ruby #localization #text 

Ruby-gettext: A Pure Ruby Localization(L10n) Library & tool
Royce  Reinger

Royce Reinger


A Ruby Library to interface with The Gengo API for Translation

Gengo Ruby Library (for the Gengo API)

Translating your tools and products helps people all over the world access them; this is, of course, a somewhat tricky problem to solve. Gengo is a service that offers human-translation (which is often a higher quality than machine translation), and an API to manage sending in work and watching jobs. This is a Ruby interface to make using the API simpler (some would say incredibly easy).

Installation & Requirements

Installing Gengo is fairly simple:

$ gem install gengo

Tests - Running Them, etc

Gengo has a full suite of tests, however they're not currently automated. Each script in the examples directory tests a different Gengo API endpoint; run against those if you wish to test for now.

Question, Comments, Complaints, Praise?

If you have questions or comments and would like to reach us directly, please feel free to do so at the following outlets. We love hearing from developers!

  • Email: api [at] gengo dot com

If you come across any issues, please file them on the Github project issue tracker. Thanks!


The usage of the API is very simple - the most important part is getting authenticated. To do this is just a few lines of code:

require 'gengo'

gengo ={
    :public_key => 'your_public_key',
    :private_key => 'your_private_key',
    :sandbox => true, # Or false, depending on your work

# Get some information
puts gengo.getAccountBalance()

With that, you can call any number of methods supported by this library. The entire library is rdoc supported, so you can look at more method information there. There's also a full suite of test code/examples, located in the examples directory. Be sure to checkout the Gengo API documentation. Enjoy!

Author: Gengo
Source Code: 

#ruby #translation #api 

A Ruby Library to interface with The Gengo API for Translation
Nigel  Uys

Nigel Uys


Go-i18n: Translate Your Go Program into Multiple Languages


go-i18n is a Go package and a command that helps you translate Go programs into multiple languages.

Package i18n


The i18n package provides support for looking up messages according to a set of locale preferences.

import ""

Create a Bundle to use for the lifetime of your application.

bundle := i18n.NewBundle(language.English)

Load translations into your bundle during initialization.

bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
// If use go:embed
//go:embed locale.*.toml
var LocaleFS embed.FS

bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
bundle.LoadMessageFileFS(LocaleFS, "")

Create a Localizer to use for a set of language preferences.

func(w http.ResponseWriter, r *http.Request) {
    lang := r.FormValue("lang")
    accept := r.Header.Get("Accept-Language")
    localizer := i18n.NewLocalizer(bundle, lang, accept)

Use the Localizer to lookup messages.

    DefaultMessage: &i18n.Message{
        ID: "PersonCats",
        One: "{{.Name}} has {{.Count}} cat.",
        Other: "{{.Name}} has {{.Count}} cats.",
    TemplateData: map[string]interface{}{
        "Name": "Nick",
        "Count": 2,
    PluralCount: 2,
}) // Nick has 2 cats.

Command goi18n


The goi18n command manages message files used by the i18n package.

go get -u
goi18n -help

Extracting messages

Use goi18n extract to extract all i18n.Message struct literals in Go source files to a message file for translation.

# active.en.toml
description = "The number of cats a person has"
one = "{{.Name}} has {{.Count}} cat."
other = "{{.Name}} has {{.Count}} cats."

Translating a new language

Create an empty message file for the language that you want to add (e.g.

Run goi18n merge active.en.toml to populate with the messages to be translated.

hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "Hello {{.Name}}"

After has been translated, rename it to

hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "Hola {{.Name}}"

Load into your bundle.

bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)

Translating new messages

If you have added new messages to your program:

  1. Run goi18n extract to update active.en.toml with the new messages.
  2. Run goi18n merge active.*.toml to generate updated translate.*.toml files.
  3. Translate all the messages in the translate.*.toml files.
  4. Run goi18n merge active.*.toml translate.*.toml to merge the translated messages into the active message files.

For more information and examples:

English · 简体中文

Author: Nicksnyder
Source Code: 
License: MIT license

#go #golang #i18n #translation 

Go-i18n: Translate Your Go Program into Multiple Languages

Wnk_translation: Translation Plugin for CakePHP 3

Translation Plugin for CakePHP 3.6

It provides translation services for pot files:

  • import strings to be translated from pot files
  • translations
    • manual translations
    • translations by google translation services
    • community translations - self service (planned)
  • export translated strings to pot files


composer require ava007/wnk_translation


Plugin::load('WnkTranslation', ['routes' => true, 'autoload' => true, 'bootstrap' => false]);

Configure::write('WnkTranslation', [
    'default_lang' => 'en',    // Language in which the application has been developed
    'trans_lang' => ['de','fr','it'],   // Languages in which the application should be translated to
    'tablePrefix' => '',     // optional prefix for database tables


class Application //extends BaseApplication {

  public function bootstrap() {


run one of the appropriate sql-ddl-script:

  • postgresql: ddl-postgresql.sql
  • mysql: ddl-mysql.sql


After installation the plugins is called used the following url:



visit to see this plugin in action

Author: ava007
Source Code: 

#php #translation #cakephp 

Wnk_translation: Translation Plugin for CakePHP 3

Cakephp-translate: CakePHP Translate Plugin

CakePHP Translate Plugin      

A CakePHP plugin for managing translations DB driven.

This branch is for use with CakePHP 4.0+. For details see version map.

Key features

  • Import from POT, PO files or any service/API.
  • Web-based and without external dependencies.
  • Translate strings in all languages simultaneously.
  • Allow others to help translating without having to know technical details.
  • Auto-Translate and Auto-Suggest with Translate APIs (e.g. Google Translate PHP/JS, Yandex, ...) for efficiency.

Benefits over normal PO editing

  • Prevent duplicates, missing translations, collisions.
  • Auto-Features like trim(), h(), newlines to <p>/<br>, espacing of %s.
  • Validate placeholders ({0}, %s, ...).
  • Preview and code excerpts of references.
  • Auto-Add Controller names (singular + plural).
  • Manage in Groups (=Domains) and export/enable/disable them.
  • Creates clean PO files with all translations in usage to easier diff changes.

Included translation services via APIs

  • Google (free, limited)
  • Yandex (free, limited)
  • Transltr (free)

Add your translation engine here in a heartbeat.


Including the plugin is pretty much as with every other CakePHP plugin:

composer require dereuromark/cakephp-translate

Then, to load the plugin run the following command:

bin/cake plugin load Translate -b -r
# If you haven't loaded the Tools plugin already
bin/cake plugin load Tools -b -r

Routes are needed for the backed, the bootstrap sets up a few defaults.

Run this in console to create the necessary DB tables:

bin/cake migrations migrate -p Translate


Use dereuromark/cakephp-queue for larger projects to avoid timeout issues when importing PO files.


Web Backend

  • Navigate to /admin/translate/ in your browser.


  • Run bin/cake translate.


  • Use TinyAuth or alike to manage access to the translation backend for user groups.
  • Implement your own Translation engine if you want to have even better auto-suggest.

Configuration and documentation

Author: Dereuromark
Source Code: 
License: MIT license

#php #cakephp #translation #i18n 

Cakephp-translate: CakePHP Translate Plugin
Waylon  Bruen

Waylon Bruen


Gitea: Git with A Cup Of Tea, Painless Self-hosted Git Service

 Gitea - Git with a cup of tea        


The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. Using Go, this can be done with an independent binary distribution across all platforms which Go supports, including Linux, macOS, and Windows on x86, amd64, ARM and PowerPC architectures. Want to try it before doing anything else? Do it with the online demo! This project has been forked from Gogs since 2016.11 but changed a lot.


From the root of the source tree, run:

TAGS="bindata" make build

or if SQLite support is required:

TAGS="bindata sqlite sqlite_unlock_notify" make build

The build target is split into two sub-targets:

  • make backend which requires Go 1.17 or greater.
  • make frontend which requires Node.js LTS or greater and Internet connectivity to download npm dependencies.

When building from the official source tarballs which include pre-built frontend files, the frontend target will not be triggered, making it possible to build without Node.js and Internet connectivity.

Parallelism (make -j <num>) is not supported.

More info:


./gitea web

NOTE: If you're interested in using our APIs, we have experimental support with documentation.


Expected workflow is: Fork -> Patch -> Push -> Pull Request


  2. If you have found a vulnerability in the project, please write privately to Thanks!


Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.

You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope fo fill it as questions pop up.


Further information

For more information and instructions about how to install Gitea, please look at our documentation. If you have questions that are not covered by the documentation, you can get in contact with us on our Discord server or create a post in the discourse forum.

We maintain a list of Gitea-related projects at gitea/awesome-gitea.
The hugo-based documentation theme is hosted at gitea/theme.
The official Gitea CLI is developed at gitea/tea.



How do you pronounce Gitea?

Gitea is pronounced /ɡɪ’ti:/ as in "gi-tea" with a hard g.

Why is this not hosted on a Gitea instance?

We're working on it.


Looking for an overview of the interface? Check it out!

DashboardUser ProfileGlobal Issues
BranchesWeb EditorActivity
New MigrationMigratingPull Request View
Pull Request DarkDiff Review DarkDiff Dark

View the chinese version of this document

Author: Go-gitea
Source Code: 
License: MIT license

#go #golang #github #git 

Gitea: Git with A Cup Of Tea, Painless Self-hosted Git Service

How to Localize Banner Ad Campaign

Banners for All Your Markets

Depending on your industry, the language of communication with potential clients can be more or less important. Every content must be in the language with which your users feel most comfortable.

Translating your banner advertisement into other languages can really impact the effectiveness of your marketing creatives.

In this article, we’re not only going to provide some advice on translating advertising creatives, but also show you how to make this process much more streamlined. For more tech-savvy marketers, I will show how to easily manage HTML translations of banners and how to automatically display the appropriate language version.

But first, here are few tips on how to make the process easier.

Read More:

#javascript #advertisement #internationalization #localization #translation #html

How to Localize Banner Ad Campaign

Translation in Symfony 4 (Language based on user preference)

This video covers how you can create a basic system where the user can select a language and it will be stored in both database and the session, and when he user change’s it it will refresh the user in the session so it takes the changes in consideration, if you have any questions about this, please leave them in the comment section below and i’ll do my best to answer them :))


#symfony #translation

Translation in Symfony 4 (Language based on user preference)
Wasswa  Meagan

Wasswa Meagan


Creating a simple speech to speech translation pipeline

Using microsoft azure services we will create the following pipeline:

  1. ASR (automatic speech recognition)
  2. MT (machine translation)
  3. TTS (Text-to-speech)
  4. (optional) Use neural voices to improve the output speech

My desire to speak other languages came when I was 18 and went to live in Accra, Ghana. I was a missionary for the Church of Jesus Christ of Latter-day Saints, called to serve the people I would meet there. In Ghana over 50 languages are recognized and where I lived (I moved four times) there were 3 or 4 main languages spoken. I wanted to connect with and serve these super great people, so I did what I could to learn their languages.

Almost 6 years now since I got back, I’m studying machine translation and looking to make my first attempt at making MT a thing for languages that I spoke like Twi. This article isn’t those efforts, but a starting point for people interested in machine translation.

This is not an in-depth look, or from scratch, article either. This is showing how to use some very powerful tools in a basic way, just to get you started. Most of this can be found in following along with Microsoft’s own quickstarts for the azure speech service or pyaudio’s documenation.

Honestly, I’m very impressed with Microsoft azure. In a machine translation course that I’m taking we found that for several languages (maybe more, but we haven’t tested them all) it was outpacing Google translate in accuracy (mainly tested with bleu scores). Plus the TTS allows you to improve the voice, which is a ton of fun!

Let’s get started with code and a basic understanding of what is happening.


Automatic speech recognition takes some pretty serious work. If you want to work with a little more exposed example you can try using pyaudio. They are awesome for experimenting with. Let me show you an example of that and then the Microsoft one and you can decide which you want to proceed with.

_This file above lets you record and listen to .wav files. It’s nice in that you see a little more behind the scenes of what is going on. There are lots of wonderful articles and writings that explain this better than I will take the time to here. One little technicality to note is that if you are using vs code on a mac, and you try to call record your mic won’t turn on. Try _this if that is case.


Machine translation, this is a massive field of research all on its own, but that is a discussion for another time in your life. What is so cool is that the training and fine tuning of a very powerful NMT is accessible to us, and it can help us start our MT journey. So, for now, rather than building an NMT from scratch, we will focus on using azure’s speech MT. This requires a ‘translation’ resource which you set up the same way you set up your ASR. A reminder to wait while it finishes setting up so that you can find it later (I made that mistake and took a long time wondering where it went only to have to make it again).


Text-to-speech is a very fun part of this, especially if you continue to option four. You may be able to use your original speech api key, but you’ll need to have a specific region region set to use neural voices. You can find your best region at that link and set it when creating this speech resource.

Combined pipeline

Now to put it all together, a simple file helps us easily call each service. Some parts have been commented out that you can play with to see both how pyaudio works and the changes other options make.

#translation #nmt #azure #machine-translation

Creating a simple speech to speech translation pipeline