Is the goal then to have a high enough rate limit to make this usable for global chat translation?
Can we get an eta on the text to speech and speech to text as this could have a huge implementation of creating multi lingual voice chat that automatically translates into your native language
The API documentation is very confusing
I tried inserting this module from Roblox (exactly as the docs have shown me) but why is “V2” empty? If I follow the documented steps for getting this working I error because this package is not working as intended. Could we also avoid using packages? They don’t play nicely with Rojo.
Edit: Someone on twitter has helped me out, thank you!!
The correct link for the OpenCloud module is here Open Cloud - Creator Store
If you follow the direction from the Docs page you find the wrong module. I also think it’s a security risk/bad UX to ask devs to find packages this way. Just link them directly please!
Could an example of a Lua Map that would be useful for this be added to the Documentation? Or shown here if possible please
Yeah, that eliminates the risk of developers/creators being warned/banned/terminated for bad user-submitted content.
Here’s an example of using a Lua map for very basic caching (this would replace the example server script):
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local oc = require(ServerScriptService.OpenCloud.V2)
-- Find at https://create.roblox.com/dashboard/creations in the overflow menu of an experience tile
local universeID = game.GameId
-- Create RemoteFunction
local remoteFunction = Instance.new("RemoteFunction")
remoteFunction.Name = "TranslateTextFunction"
remoteFunction.Parent = ReplicatedStorage
local translationCache = {}
remoteFunction.OnServerInvoke = function(player, text, locale, uni)
print(player.Name .. " requested translation for text: " .. text .. " to locale: " .. locale)
local possibleCachedTranslation = getCachedTranslation(text, locale)
if possibleCachedTranslation == nil then
print("cache miss, translating")
return translateText(text, locale)
end
return possibleCachedTranslation
end
function getCachedTranslation(text, locale)
if translationCache[text] == nil then
return nil
end
return translationCache[text][locale]
end
function addCachedTranslation(text, locale, translation)
local translationPerLocale = translationCache[text]
if translationPerLocale == nil then
local newTranslationMapping = {}
newTranslationMapping[locale] = translation
translationCache[text] = newTranslationMapping
else
translationPerLocale[locale] = translation
end
end
function translateText(text, locale)
-- Prepare the translation request
local request : oc.TranslateTextRequest = {
path = oc:UniversePath(universeID),
text = text,
-- target language codes supports a list of multiple locales to translate to.
-- Here we are passing just one language:
--The player locale retrieved in the local script
target_language_codes = {locale}
}
local result = oc:TranslateText(request)
print(result.ResponseCode)
if result.Error == nil then
addCachedTranslation(text, locale, result.Response.translations[locale]) -- @nogo's fix
return result.Response.translations[locale] -- Assuming translations[locale] contains the translated text
else
return "Error: " .. result.Error.message
end
end
Note that this doesn’t do any clean up for translations that are no longer needed, so in a large experience you might run into memory issues from translations accumulating in the cache without being deleted. An alternative would be to use a community caching library that implements cache size limits or write your own LRU cache.
Thanks! Do you know how many translations would be considered too much to have stored in the cache? As in, what should the cutoff be to start clearing the cache; 100 Translations, 1000 Translations? Or does it really depends on my use case, meaning there is no fixed number for this.
Also for anyone looking to copy and use this, make sure you link the addCachedTranslation function since it’s not linked in this example. Would look something like this addCachedTranslation(text, locale, result.Response.translations[locale])
in this block
if result.Error == nil then
addCachedTranslation(text, locale, result.Response.translations[locale]) -- ADD HERE
return result.Response.translations[locale] -- Assuming translations[locale] contains the translated text
else
return "Error: " .. result.Error.message
end
The limit would really depend on your use case since you’ll need to balance the memory usage vs. request savings against other memory intensive scripts in your experience (also depending on the content you’re translating, repeats may actually be rare). I would say 1000 translations is probably a good place to start, and you can use the memory profiler to tune the limit up or down!
Also thanks for noticing my mistake, I’ve updated the original sample with your fix
Hello! I think I’m either making a mistake or the API is having some trouble today. I’m running the same code that I ran last night - translating the same texts, but it seems to be erroring today. Here are all the details for the things I’m querying. I run this test from “Team Test” but I get the same issues if I publish and test from there as well.
>> Translating Text: We have a win Path: universes/4186319221 Target Languages: ▼ {
[1] = "en-us",
[2] = "es-es",
[3] = "ru-ru",
[4] = "pt-br",
[5] = "fr-fr",
[6] = "id-id",
[7] = "de-de",
[8] = "ja-jp",
[9] = "ko-kr"
}
{ "code": "INTERNAL", "message": "Unexpected error occurred while translating text" }
▼ {
["Error"] = ▼ {
["code"] = "INTERNAL",
["message"] = "Unexpected error occurred while translating text"
},
["ResponseCode"] = 500
}
TRANSLATION ERROR
>> Translating Text: Chat Path: universes/4186319221 Target Languages: ▼ {
[1] = "en-us",
[2] = "es-es",
[3] = "ru-ru",
[4] = "pt-br",
[5] = "fr-fr",
[6] = "id-id",
[7] = "de-de",
[8] = "ja-jp",
[9] = "ko-kr"
}
{ "code": "INTERNAL", "message": "Unexpected error occurred while translating text" }
▼ {
["Error"] = ▼ {
["code"] = "INTERNAL",
["message"] = "Unexpected error occurred while translating text"
},
["ResponseCode"] = 500
}
TRANSLATION ERROR
>> Translating Text: test Path: universes/4186319221 Target Languages: ▼ {
[1] = "en-us",
[2] = "es-es",
[3] = "ru-ru",
[4] = "pt-br",
[5] = "fr-fr",
[6] = "id-id",
[7] = "de-de",
[8] = "ja-jp",
[9] = "ko-kr"
}
{ "code": "INTERNAL", "message": "Unexpected error occurred while translating text" }
▼ {
["Error"] = ▼ {
["code"] = "INTERNAL",
["message"] = "Unexpected error occurred while translating text"
},
["ResponseCode"] = 500
}
TRANSLATION ERROR
I would really like to have more detailed errors on why things failed. Am I being throttled? Is the text filtered and that’s why it failed? Did roblox API have a fluke?
We use this in Clip It to translate newly uploaded clips - it’s hard for us to determine if we should re-attempt a failed translation or not - or if we need to ask the player to modify some text from their clip to pass filtering. It seems that the filter Roblox uses for chats is not the same one that’s used for translations, so we run into some issues there as well.
Also is there a per-second rate limit? It’s hard to tell what’s going on because of the ambiguous errors that translations give - but sometimes it feels like if I sent just 5 translations within 1-2s it will consistently fail.
Hey Genya!
We do try to forward the error in a transparent way where possible (e.g. text filter failures and whether or not the universe is throttled for requests). For throttling, you should get an error like the following:
{
"code": "RESOURCE_EXHAUSTED",
"message": "Rate limit exceeded, try again after: 00:00:42.9340000"
}
Source text that is moderated will return an empty response, I’ll work with the team to get this added to the documentation for clarity.
The rate limit is the advertised scaling per minute (e.g. max requests per minute per experience = 600 + 1.5 * # concurrent users), however it looks like there was an internal flood checker that wasn’t configured properly and was limiting requests. This has been fixed and it should resolve the errors you’ve been seeing. Let us know if you still have any problems!
Hello!
Thank you so much for the help here. Translation errors have dropped from tens of thousands per hour to just a dozen!
I did notice some odd behavior. Sometimes the translation trims emojis - is there anything that can be done about this? Notice how the emoji is missing in the translated text here.
If you would like to test the translation with this text for yourself:
Happy Halloween 👻🎃! p.s. free users now get 20 seconds of clip recording time, up from 20 seconds before!
(P.S. I LOVE THIS FEATURE!!! Great work everyone who worked on it, and thank you!! https://x.com/ScriptOnRoblox/status/1902876273917170163 )
Edit sorry one more thing but I’m starting to receive some complaints about players saying their clips are not being translated correctly. I have on example so far that seems to be completely missing the mark: я обожралась
which ChatGPT & Google translate tell me means “I overate” or “I ate too much”
The Roblox Translation API tells me it’s the opposite “I’m hungry”
Edit 2: I’m starting to see more users share their frustrations with translations modifying text improperly:
I’m currently using this in my game and its great. My only problem is that it does not support rich text. Will it support translating rich text while keeping the rich text modifications in the future?
Bug: Whether you translate text to english (Using the en-us
locale), result.Error
will be nil
, however it’s result (result.Response.translations[locale]
) is also nil
.
Recreation steps: Use en-us
locale and attempt to translate.
The following is a video showcasing (has audio explaining what’s happening) that it works with other languages, but not English: https://youtu.be/t7DuErsfI-A
Maybe I’m doing something wrong on my end? Not sure
Hey nogo!
For requests that don’t have a source_language_code
defined, we run language identification to try to detect what the locale is.
In this case, language detection is erroneously detecting that the source locale is en-us
(you can check this by inspecting the source_language_code
field on the response). Since same locale translations are not allowed (e.g. en-us
→ en-us
), an empty translation map is returned.
You can explicitly override this by passing in your own source_language_code
. We generally suggest passing in Player.LocaleId if possible.
For your example, if you pass in es-es
as your source_language_code
or use a longer source text that language identification recognizes as Spanish (like “hola mi amigos”) then you should be able to get an English translation.
Hey Genya!
Translation accuracy is definitely something we’re always working on improving. If you have any other examples of mistranslations, please send them my way and I can pass them along!