Create a Translation.io library
(for your own stack)
Translation.io is designed to help developers localize and translate applications that are constantly evolving.
Since we want to offer the best experience to developers, our approach is to create the most integrated library for each stack: Ruby on Rails, Laravel, React & JS, Angular etc. Some of them are officially supported, some are unofficially supported and others are in progress.
— In a nutshell —
Need help, have a question or found a bug?
contact@translation.io
Table of contents
Introduction
Only 3 HTTP requests with a JSON body are needed to create a complete Translation.io library:
• segments/init: | Push existing translations to Translation.io. |
• segments/sync: | Send new keys/strings and get new translations from Translation.io. |
• source_edits/pull: | Get source editions made on Translation.io interface (see here). |
segments/init only needs to be called once just after the project configuration (following calls will be denied by the backend). source_edits/pull and segments/sync should ideally be called successively each time the translations are synchronized.
If you create a library for a framework, the command lines should look like this:
task translation:init
→ calls segments/init
task translation:sync
→ calls source_edits/pull
followed by segments/sync
Authentication
There are three ways to authenticate through Translation API: JSON parameter, URL query parameter and HTTP header.
If several authentications are used at the same time, the priority order is the following:
- JSON parameter.
- URL query parameter.
- HTTP header.
JSON parameter
Add the project API key as api_key
parameter in the JSON body of your request.
curl -X POST https://translation.io/api/v1/segments/init.json \
-H 'content-type: application/json' \
-d '{
"api_key": "YOUR_API_KEY"
}'
URL query parameter
Add the project API key as api_key
parameter in the request URL:
curl -X POST https://translation.io/api/v1/segments/init.json?api_key=YOUR_API_KEY
HTTP header
Add the project API key as x-api-key
in the request header.
curl -X POST https://translation.io/api/v1/segments/init.json \
-H 'x-api-key: YOUR_API_KEY'
Errors
Translation.io uses conventional HTTP codes to indicate if a request
is successful (2xx
) or if it failed (4xx
).
Errors will be returned with a JSON body warning you about missing params,
extra params or erroneous values in params.
errors
is an array of strings, so if many errors are
triggered at the same time, all of them will be returned in the
response.
Example
request (segments/init){
"target_languages": ["nl"],
"segments": {
"fr": [],
"nl": []
}
}
{
"errors": [
"Missing param(s): source_language.",
"'segments' must be a Hash with target_languages as keys and arrays of segments as values."
]
}
Segments
segment
is the most important model of Translation.io.
Each segment corresponds to a single unit of translation
from a source language to a target language.
There are two different types of segments: key segments and source segments. Segment types are immutable so an existing key segment can never become a source segment, and vice versa.
As a general rule of thumb, you should use:
-
key segments if you localize in your code using a key:
translate("some.custom.key")
. -
source segments if you localize in your code using the source text:
translate("My source text")
.
If you have both syntaxes available in your framework, you should use both types at the same time in the HTTP requests.
Key segments
Type for segments that are in the key → translation
format in your application. They are
usually stored in YAML
, JSON
, PHP
, INI
or XML
files.
Plural and context information are not part of the key segments. However, depending on the framework conventions, they can sometimes be deducted from the key or a special character in the source text.
They are uniquely identified by their key
.
Required parameters
Name | Type | Description |
---|---|---|
type
|
string
|
Segment type.
Note: should be
"key" .
|
key
|
string
|
Key associated with the text to be translated.
Example:
"home.call_to_action.bottom"
|
source
|
string
|
Source text.
Example:
"Click here to subscribe"
|
Optional parameters
Name | Type | Description |
---|---|---|
target
|
string
|
Translated target text.
Example:
"Cliquez ici pour vous inscrire"
|
comment
|
string
|
Comment to help translate this segment.
Example:
"Homepage call-to-action (the bottom one)."
|
references
|
array of strings
|
File(s) where this key is stored or called inside
the source code. May provide more context for translators.
Example:
["config/locales/fr.yml", "app/home/index.html:33"]
|
Notes
-
Segments are always encapsulated in a specific target language, so the language combination
(
en → fr
in above example) doesn't need to be specified in every segment. See init parameters.
Source segments
Type for segments that are in the source → translation
format in your application.
They are usually stored in PO
(GetText) or
XLIFF
files that
have been specifically designed for translations.
Plural and context information are an integral part of the segments.
They are uniquely identified by the combination of context
, source
and source_plural
.
Required parameters
Name | Type | Description |
---|---|---|
type
|
string
|
Segment type.
Note: should be
"source" .
|
source
|
string
|
Source text.
Example:
"product in the cart"
|
Optional parameters
Name | Type | Description |
---|---|---|
target
|
string
|
Translated target text.
Example:
"article dans le panier"
|
source_plural
|
string
|
Plural form of source text.
Note: only if segment is plural.
Example: "products in the cart"
|
target_plural
|
string
|
Plural form of target text.
Note: only if segment is plural.
Example: "articles dans le panier"
|
target_plural_2
|
string
|
Second plural form of target text.
Note: only if segment is plural and 2 plural forms or more. See list of languages.
|
target_plural_3
|
string
|
Third plural form of target text.
Note: only if segment is plural and has 4 plural forms or more (
target included). See list of languages.
|
target_plural_4
|
string
|
Fourth plural form of target text.
Note: only if segment is plural and has 5 plural forms or more (
target included). See list of languages.
|
target_plural_5
|
string
|
Fifth plural form of target text.
Note: only if segment is plural and has 6 plural forms or more (
target included). See list of languages.
|
context
|
string
|
Used to differentiate 2 segments that have the same source
but need different translations because of their context.
Note:
"orange" with context="fruit" and "orange" with
context="color" should coexist and have different translations.
|
comment
|
string
|
Comment to help translate this segment.
Example:
"Order summary that is displayed next to the cart."
|
references
|
array of strings
|
File(s) where this segment is stored or where the segment is used inside the
source code. May provide more context for translators.
Example:
["gettext/fr/app.po", "app/home/index.html:33"]
|
Notes
-
Segments are always encapsulated in a specific target language, so the language combination
(
en → fr
in above example) doesn't need to be specified in every segment. See init parameters.
Initialization
Populate a new Translation.io project by pushing all source and translated text into the backend. This call should only be executed one time per project.
Endpoint
POST https://translation.io/api/v1/segments/init(.json)Parameters
Name | Type | Description |
---|---|---|
source_language
|
string
|
Source language code (from here).
Example:
"en"
|
target_languages
|
array
|
Array of target language codes (from here).
Example:
["fr", "es"]
|
segments
|
hash
|
Hash with the language codes as keys and an array of segments as value. |
Response
Name | Type | Description |
---|---|---|
project
|
hash
|
Project name and URL.
Note: should be displayed back to the user in the console.
Example:
|
segments
|
hash
|
Hash with the language codes as keys and an array of segments as value. |
Example
request{
"source_language": "en",
"target_languages": ["fr", "es"],
"segments": {
"fr": [segment_1_fr, segment_2_fr, ...],
"es": [segment_1_es, segment_2_es, ...]
}
}
The segments could be translated or not, and look like this:
segment_1_fr Translated French segment of type "key"{
"type": "key",
"key": "goodbye.message",
"source": "Goodbye world",
"target": "Au revoir le monde"
}
{
"type": "key",
"key": "goodbye.message",
"source": "Goodbye world",
"target": "Adios al mundo"
}
{
"type": "source",
"source": "Hello world",
"target": "Bonjour le monde"
}
{
"type": "source",
"source": "Hello world",
"target": ""
}
The response from Translation.io would be:
response{
"project": {
"name": "project_name",
"url" : "https://translation.io/owner_name/project_name"
},
"segments": {
"fr": [segment_1_fr, segment_2_fr, ...],
"es": [segment_1_es, segment_2_es, ...]
}
}
In this case, segment_1_fr
, segment_2_fr
, segment_1_es
and segment_2_es
are the same as above.
For some specific stacks, they may have been pre-translated by Translation.io (see here).
Synchronization
Synchronize your current branch with Translation.io by pushing all source text and receiving new translations from Translation.io. If a new source text is found in the application, it will be added to Translation.io
Endpoint
POST https://translation.io/api/v1/segments/sync(.json)Parameters
Name | Type | Description |
---|---|---|
source_language
|
string
|
Source language code (from here).
Example:
"en"
|
target_languages
|
array
|
Array of target language codes (from here).
Example:
["fr", "es"]
|
segments
|
array
|
Array of segments without translations (no target parameter).
|
readonly
optional
|
boolean
|
If true , synchronize and receive new segments translations from
Translation.io, but don't create or update segments on Translation.io interface
(it skips steps 2, 3 and 4 from above schema).
Note: Useful if you want to synchronize faster in your CI/CD pipeline. It is also
the only way to execute this call concurrently (we use a
mutex if
readonly is false ).
Default: false
|
purge
optional
|
boolean
|
If true , remove unused segments from Translation.io, using segments
as reference.
Note: You need it if you want to remove segments from Translation.io that
are not anymore in the current state of your application. Make sure to merge your active branches
before you purge because it uses the current branch of the application as reference
(
segments parameter).
Important: Synchronize with purge = false first to be
sure the returned unused_segment_ids are really the segments you want to remove
from Translation.io.
Default: false
|
Response
Name | Type | Description |
---|---|---|
project
|
hash
|
Project name and URL.
Note: should be displayed back to the user in the console.
Example:
|
segments
|
hash
|
Hash with the language codes as keys and an array of segments as value. |
unused_segment_ids
|
array
|
Array of segment identifiers that exist on Translation.io but are not present on the request
segment parameter. They are the segments that will be removed from Translation.io
if the same request is sent with the purge parameter.
|
Example
request{
"source_language": "en",
"target_languages": ["fr", "es"],
"segments": [segment_1, segment_2, ...]
}
Where segments only contain the source (no translation!), and look like this:
segment_1 Segment of type "key"{
"type": "key",
"key": "goodbye.message",
"source": "Goodbye world"
}
{
"type": "source",
"source": "Hello world"
}
The response will return the corresponding translated segments for each target language.
response{
"project": {
"name": "project_name",
"url" : "https://translation.io/owner_name/project_name"
},
"segments": {
"fr": [segment_1_fr, segment_2_fr, ...],
"es": [segment_1_es, segment_2_es, ...]
},
"unused_segment_ids": []
}
Note: an empty unused_segment_ids
assumes that every segment from Translation.io
is listed on the request.
If everything is translated on Translation.io, the segments in the response would be the following:
segment_1_fr Translated French segment of type "key"{
"type": "key",
"key": "goodbye.message",
"source": "Goodbye world",
"target": "Au revoir le monde"
}
{
"type": "key",
"key": "goodbye.message",
"source": "Goodbye world",
"target": "Adios al mundo"
}
{
"type": "source",
"source": "Hello world",
"target": "Bonjour le monde"
}
{
"type": "source",
"source": "Hello world",
"target": "Hola mundo"
}
Source Editions
Update your code with the latest source editions that have been made on the Translation.io interface (more info here and here).
This step is only needed for key segments. If you only want to manage source segments, you can safely ignore this HTTP call.
Always call this endpoint before synchronization to always be up to date with the latest source editions.
Endpoint
GET https://translation.io/api/v1/source_edits/pull(.json)Parameters
Name | Type | Description |
---|---|---|
timestamp
|
integer
|
Timestamp (Unix Time)
returned by the previous call (or 0 if it's the first call).
Note: Some HTTP libraries don't work well when GET contains a JSON body, so
timestamp is also accepted as a HTTP query parameter.
Example: 1734775216
|
Response
Name | Type | Description |
---|---|---|
project
|
hash
|
Project name and URL.
Note: should be displayed back to the user in the console.
Example:
|
source_edits
|
array
|
List of source editions created between timestamp parameter and now.
Note: Sorted by ascending creation date.
Example: see below. |
timestamp
|
integer
|
Updated timestamp (Unix Time)
to save at the application level (ex: in a .translation_io file).
This timestamp should be used for the next call.
Note:
.translation_io file should be added to the code
respository and will coevolve with each branch of the code.
|
Notes
- This endpoint is "read-only", it will not trigger any change on the backend.
Example
Let's assume the following source editions have been made in the project:
# | Old source | New source | Key | Edition timestamp |
---|---|---|---|---|
1 | Your login | Your username | user.subscription.login |
1700000000
2023-11-14 23:13:20 |
2 | Hi everybody | Hello everybody | home.welcome.intro |
1720000000
2024-07-03 11:46:40 |
3 | Term of service | Terms of service | footer.legal |
1730000000
2024-10-27 04:33:20 |
If the project timestamp stored in .translation_io
is 1710000000,
then the request should be:
{
"timestamp": 1710000000
}
The response will only contain source editions #2 and #3. Source edition #1 was created before the timestamp from the request and should already have been updated in the project.
response{
"project": {
"name": "project_name",
"url" : "https://translation.io/owner_name/project_name"
},
"source_edits": [
{
"key": "home.welcome.intro",
"old_source": "Hi everybody",
"new_source": "Hello everybody",
"created_at": 1720000000
},
{
"key": "footer.legal",
"old_source": "Term of service",
"new_source": "Terms of service",
"created_at": 1730000000
}
],
"timestamp": 1734782416
}