I have a simple command that I want to localize : "Select 5 objects". I had thought that I might template this as : "Select %d %s", where the number and objects might be formatted later. But this raised the question of how do you appropriately pluralize a string?
When the object can have multiple plurals :
0 : no objects
1 : 1 object
2 or more : 2 objects
What is a good way to structure your string templates to accommodate for optional plurality? This is especially important for localization later, so I'm trying to be smart about it now.
Reading online, a lot of the top hits for localization best practices rely on Apple's NSLocalizedString library, which allow for string dictionaries with custom rules around the cardinality of the word. But is there a good way to handle this when all of my strings are loaded into a CSV? Roblox provides a TranslationService that comes with a Translator class that can format strings by keys. So I've structured my resources like this :
localization.csv
Key, Example, en, fr, es
Command.SelectTemplate, "Select 3 apples", "Select {total : number} {thing : string}", "Sélectionnez {total : number} {thing : string}" "Selecciona {total : number} {thing : string}"
Object.AppleZero, "there are zero apples", "apples", "pommes", "manzanas"
Object.AppleOne, "there is one apple", "apple", "pomme", "manzana"
Object.AppleFew, "there are a few apples", "apples", "pommes", "manzanas"
example Script
local LocalizationService = game:GetService("LocalizationService")
local LOCALE = "en"
local res, translator = pcall(function()
return LocalizationService:GetTranslatorForLocaleAsync(LOCALE)
end)
if not res then
warn(string.format("Failed to load the translator with error %s", tostring(translator)))
return
end
local function getSelectionString(numToSelect : number, objectKey : string)
-- TODO : FIND A BETTER WAY TO DO THIS
assert(numToSelect >= 0, "Cannot select a negative number")
local lookupKey
if numToSelect == 0 then
lookupKey = objectKey .. "Zero"
elseif numToSelect == 1 then
lookupKey = objectKey .. "One"
else
lookupKey = objectKey .. "Few"
end
local objectString = translator:FormatByKey(lookupKey)
local formattingArgs = {
total = numToSelect,
thing = objectString,
}
local commandString = translator:FormatByKey("Command.SelectTemplate", formattingArgs)
return commandString
end
-- Test it out
local objectKey = "Object.Apple"
print(getSelectionString(0, objectKey)) -- "Select 0 apples"
print(getSelectionString(1, objectKey)) -- "Select 1 apple"
print(getSelectionString(2, objectKey)) -- "Select 2 apples"
While this technically works, it requires defining multiple keys for every single object that may have plurality in other languages, and it assumes a very English way pluralizing objects. So to reiterate my question from earlier, what is a good way to structure string templates to accommodate for optional plurality?
One of the best ways of handling plurals is ICU message syntax. ICU means International Components for Unicode – a widely used set of C/C++, Java, and other libraries providing Unicode and globalization support for software and applications.
ICU format allows you to create user-friendly texts that combine the use of different plural, gender, date and time forms in one string, which will vary depending on who is the user. For example, “Sam sent you 2 messages” and “Emma sent you 1 message”.
For example:
You have {count, plural,
=0 {no apples}
one {one apple}
other {# apples}
}
You can store these strings in different file formats, even in CSV. Different languages have different pluralization rules and different numbers of plural forms. So translators should translate ICU strings according to the target language rules.
Also, there are some localization file formats that have native plurals support, for example, Android XML, iOS Strings.
Modern Localization Management platforms support the ICU syntax. For example, Crowdin. Here you can read more about ICU Message Syntax support in Crowdin. In addition - The Ultimate Guide to ICU Message Format.
Related
I got a simple code like:
table = {}
print(table.."hello")
then got a error like the title. I know i need to use tostring(table) to fix it . Why table or other types can't convert to string to concatenate a String automatically except number type ?
print(table) is available But print(table.."hello") is not .
Does lua have some rules?
Thanks you.
Why table or other types can't convert to string to concatenate a String automatically except number type?
This is a deliberate choice made by the Lua language designers. Strings and numbers are coerced: Every operation that expects a string will also accept a number and tostring it; every operation that expects a number will also accept a string and tonumber it.
Coercion is an operation applied to strings. Numbers will be tostringed. Any other type won't. For other primitive types like bools and nils this is somewhat questionable, since they can be converted to string without issue. For tables it's reasonable though since they are a reference type.
Unlike other languages which make such decisions for you, Lua is highly metaprogrammable: You can simply override the decision! In this case, metatables are the solution, specifically the __concat metamethod which gets called if concatenation (..) is applied to two values of which one has the metamethod (and is neither a string or number):
table = setmetatable({}, {
__concat = function(left, right)
if type(left) == "string" then
return left .. tostring(right)
end
return tostring(left) .. tostring(right)
end
})
print(table .. "hello") -- hellotable: 0x563eb139bea0
You could even extend this to primitive types (nils, booleans), some other reference types (functions, coroutines) using debug.setmetatable, but I'd advise against this.
The declaration of table = {} destroying the table library
The datatype table is not a string or number so concat with .. must fail
Try this instead...
mytab = {}
table.insert(mytab, "hello")
print(table.concat(mytab))
For the table library functions look: https://www.lua.org/manual/5.4/manual.html#6.6
I'm working on an app that shows currencies with large numbers.
To make it more user friendly it would be nice to show the numbers with letters, e.g:
$24,514,983,671 -> $24.5B // English
3,306,777.10 € -> 3,31 Mio. € // German
kr 23 000 000 -> kr 23 mill. // Norwegian
The numbers should have minimumSignificantDigits = 1 and maximumSignificantDigits = 3.
How should this be solved in Swift? Is there any library available?
Android has this solution
It seems to be based on swift-corelibs-foundation: Github, but I can't see how to use it in my app.
Will I have to make the logic myself with localized translations?
I have found an answer for using general abbreviations K/M/B for large numbers here: iOS convert large numbers to smaller format, but it does not solve the whole problem.
You will have to implement your own solution, but it is not hard. One way to go would be to create two dictionaries where the key will be the Locale identifier and the value the translation:
let millionTrans = [ "en_US" : "million", ...]
let billionTrans = [ "en_US': "billion", ...]
then get the Locale.current, find out if the amount is in the millions or billions and query the appropriate dictionary to get its value.
I have been looking around and have read a lot of different answers but none seems to answer my specific request.
I make watchfaces for Wear OS 2 with an app called ''WATCHMAKER'' witch uses LUA as language. I want to make a watch face with a special clock pointing to a number depending on a blood sugar value sent by an transmitter connected to the body.
The string values I want to parse follows this syntax:
<DECIMAL NUMBER> <ARROW> (<TIME>)
One example would be
5,6 -> (1m)
I want to extract the <DECIMAL NUMBER> part of the reading. In the above example, I want the value 5,6.
Every 5 minutes, the transmitter sends another reading, all of those informations change:
5,8 - (30 secondes)
Thank you so much
Say you have a string, in LUA, s="14,11 -> (something)" and you want this first number of the string to be parsed to a float so you can do maths on it.
s='9,6 -> (24m)'
-- Now we use so called regular expressions
-- to parse the string
new_s=string.match(s, '[0-9]+,[0-9]+')
-- news now has the number 9,6. Which is now parsed
-- however it's still a string and to be able to treat
-- it like a number, we have to do more:
-- But we have to switch the comma for a period
new_s=new_s:gsub(",",".")
-- Now s has "9.6" as string
-- now we convert it to a number
number = string.format('%.10g', tonumber(new_s))
print(number)
Now number contains the number 9.6.
Description
If you want to store FR typed double in a node, you can only do it using String value, because double values are displayed like this: 12,58 (instead of 12.58 for US type).
So, if you want to do some computing in a cypher query, it's hard to use FR types doubles since they are String, and String + String results in a concatenation, not a real addition.
Example
To check this problem, you can simply create two nodes:
CREATE (a:TestNode{value : "12,45"}), (b:TestNode{value : "15"})
RETURN (a.value + b.value) AS result
Solutions ?
The obvious solution would be to store everything as double in the base, and do the computing on Java side, but it requires too much refactoring, and the problem is that some values are stored as FR double (like 1254,7898), some are stored as UK double (like 12,789.45) and some as US double (those ones are stored using the primitive type double).
I'm looking for a pure Cypher solution, to avoid massive data and code refactoring.
Interesting question. You can handle the differently formatted doubles in a CASE expression. First, you convert it to a string. If it contains both . and , it's UK. Else if it contains , but not . it's FR. Else it should be a native double. You can then replace the , and convert to float.
MATCH (a:TestNode)
RETURN
CASE
// UK formatted, 12,3456.45
WHEN toString(a.value) =~ ".*\\..*" AND toString(a.value) =~ ".*\\,.*"
// remove , and convert to float
THEN toFloat(replace(toString(a.value), ',', ''))
// FR formatted 1234,23
WHEN toString(a.value) =~ ".*\\,.*" AND NOT toString(a.value) =~ ".*\\..*"
// replace, with . and convert to float
THEN toFloat(replace(toString(a.value), ',', '.'))
// else it should be a double
ELSE toFloat(a.value)
END
But this query can only RETURN the value as a float/double. I don't know how you can include the CASE thing in a more complex query.
My client has database of over 400,000 customers. Each customer is assigned a GUID. He wants me to select all the records, create a dynamic "short URL" which includes this GUID as a parameter. Then save this short url to a field on each clients record.
The first question I have is do any of the URL shortening sites allow you to programatically create short urls on the fly like this?
TinyUrl allow you to do it (not widely documented), for example:
http://tinyurl.com/api-create.php?url=http://www.stackoverflow.com/
becomes http://tinyurl.com/6fqmtu
So you could have
http://tinyurl.com/api-create.php?url=http://mysite.com/user/xxxx-xxxx-xxxx-xxxx
to http://tinyurl.com/64dva66.
The guid doesn't end up being that clear, but the URLs should be unique
Note that you'd have to pass this through an HTTPWebRequest and get the response.
You can use Google's URL shortner, they have an API.
Here is the docs for that: http://code.google.com/apis/urlshortener/v1/getting_started.html
This URL is not sufficiently short:?
http://www.clientsdomain.com/?customer=267E7DDD-8D01-4F38-A3D8-DCBAA2179609
NOTE: Personally I think your client is asking for something strange. By asking you to create a URL field on each customer record (which will be based on the Customer's GUID through a deterministic algorithm) he is in fact essentially asking you to denormalize the database.
The algorithm URL shortening sites use is very simple:
Store the URL and map it to it's sequence number.
Convert the sequence number (id) to a fixed-length string.
Using just six lowercase letter for the second step will give you many more (24^6) combinations that the current application needs, and there's nothing preventing the use of a larger sequence at some point in time. You can use shorter sequences if you allow for numbers and/or uppercase letters.
The algorithm for the conversion is a base conversion (like when converting to hex), padding with whatever symbol represents zero. This is some Python code for the conversion:
LOWER = [chr(x + ord('a')) for x in range(25)]
DIGITS = [chr(x + ord('0')) for x in range(10)]
MAP = DIGITS + LOWER
def i2text(i, l):
n = len(MAP)
result = ''
while i != 0:
c = i % n
result += MAP[c]
i //= n
padding = MAP[0]*l
return (padding+result)[-l:]
print i2text(0,4)
print i2text(1,4)
print i2text(12,4)
print i2text(36,4)
print i2text(400000,4)
print i2text(1600000,4)
Results:
0000
0001
000c
0011
kib9
4b21
Your URLs would then be of the form http://mydomain.com/myapp/short/kib9.