Back to Home

ESO Lua File v101044

libraries/globals/localization.lua

[◄ back to folders ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
ESO_NumberFormats = {}
do
    local g_strArgs = {}
    function zo_strformat(formatString, ...)
        internalassert(formatString ~= nil, "no format string passed to zo_strformat")
        ZO_ClearNumericallyIndexedTable(g_strArgs)
        for i = 1, select("#", ...) do
            local currentArg = select(i, ...)
            local currentArgType = type(currentArg)
            if currentArgType == "number" then
                local str = ""
                local numFmt = "d"
                local num, frac = zo_decimalsplit(currentArg)
                
                local width = 0
                local digits = 1
                local unsigned = false
                if ESO_NumberFormats[formatString] ~= nil and ESO_NumberFormats[formatString][i] ~= nil then
                    width = ESO_NumberFormats[formatString][i].width or width
                    digits = ESO_NumberFormats[formatString][i].digits or digits
                    unsigned = ESO_NumberFormats[formatString][i].unsigned or unsigned
                end
                if width > 0 then
                    str = string.format("0%d", width)
                end
                if frac ~= 0 then
                    numFmt = "f"
                    str = str..string.format(".%d", digits)
                elseif unsigned == true then
                    numFmt = "u"
                end
                str = string.format("%%%s%s", str, numFmt)
                g_strArgs[i] = string.format(str, currentArg)
            elseif currentArgType == "string" then
                g_strArgs[i] = currentArg
            else
                internalassert(false, string.format("Argument %d with invalid type passed to zo_strformat: %s", i, currentArgType))
                g_strArgs[i] = ""
            end
        end
        if type(formatString) == "number" then
            formatString = GetString(formatString)
        end
        return LocalizeString(formatString, unpack(g_strArgs))
    end
end
do
    -- zo_strformat elegantly handles the case where we pass in a param as the "formatter" (e.g.: collectible descriptions).
    -- However, in order to avoid having each string generate its own cache table, the ZO_CachedStrFormat function need to be explicitely told "I have no formatter"
    -- so it can add all of them to one table. This cuts down on overhead, with the downside that it loses slight parity with zo_strformat.
    -- However, the fact that we do this whole no param thing at all is exploiting a quirk in the grammar to get around a bug in the grammar anyway so
    -- it's a relatively rare scenario
    ZO_CACHED_STR_FORMAT_NO_FORMATTER = ""
    local g_onlyStoreOneByFormatter = { }
    function ZO_SetCachedStrFormatterOnlyStoreOne(formatter)
        internalassert(formatter ~= ZO_CACHED_STR_FORMAT_NO_FORMATTER)
        g_onlyStoreOneByFormatter[formatter] = true
    end
    local g_cachedStringsByFormatter = 
    {
        [ZO_CACHED_STR_FORMAT_NO_FORMATTER] = {} --Used for strings that need to run through grammar without a formatter
    }
    function ZO_CachedStrFormat(formatter, ...)
        formatter = formatter or ZO_CACHED_STR_FORMAT_NO_FORMATTER
        local formatterCache = g_cachedStringsByFormatter[formatter]
        if not formatterCache then
            formatterCache = {}
            g_cachedStringsByFormatter[formatter] = formatterCache
        end
        
        local cachedString
        if formatter == ZO_CACHED_STR_FORMAT_NO_FORMATTER then
            --"No formatter" only works with 1 param
            local rawString = ...
            local hashKey = HashString(rawString)
            cachedString = formatterCache[hashKey]
            if not cachedString then
                cachedString = zo_strformat(rawString)
                formatterCache[hashKey] = cachedString
            end
        else
            local concatParams = table.concat({ ... })
            local hashKey = HashString(concatParams)
            cachedString = formatterCache[hashKey]
            if not cachedString then
                cachedString = zo_strformat(formatter, ...)
                if g_onlyStoreOneByFormatter[formatter] then
                    local existingKey = next(formatterCache)
                    if existingKey then
                        formatterCache[existingKey] = nil
                    end
                end
                formatterCache[hashKey] = cachedString
            end
        end
        return cachedString
    end
    function ZO_ResetCachedStrFormat(formatter)
        if g_cachedStringsByFormatter[formatter] then
            g_cachedStringsByFormatter[formatter] = nil
        end
    end
end
function zo_strtrim(str)
    -- The extra parentheses are used to discard the additional return value (which is the total number of matches)
    return(zo_strgsub(str, "^%s*(.-)%s*$", "%1"))
end
do
    -- Before passing to a formatting function, use english separators. zo_strformat() (with a <<f:1>> or <<F:1>>) and ZO_FastFormatDecimalNumber() will automatically replace these separators with language-appropriate ones.
    local ENGLISH_DIGIT_GROUP_REPLACER = ","
    local ENGLISH_DIGIT_GROUP_DECIMAL_REPLACER = "."
    local DIGIT_GROUP_REPLACER_THRESHOLD = zo_pow(10, GetDigitGroupingSize())
    
    function ZO_CommaDelimitNumber(amount)
        if amount < DIGIT_GROUP_REPLACER_THRESHOLD then
            return tostring(amount)
        end
        return FormatIntegerWithDigitGrouping(amount, ENGLISH_DIGIT_GROUP_REPLACER, GetDigitGroupingSize())
    end
    function ZO_CommaDelimitDecimalNumber(amount)
        -- Guards against negative 0 as a displayed numeric value
        if amount == 0 then
            amount = 0
        end
        if amount < DIGIT_GROUP_REPLACER_THRESHOLD then
            -- No commas needed
            return tostring(amount)
        end
        local wholeAmount = zo_floor(amount)
        if wholeAmount == amount then
            -- This is an integer, safe to pass to ZO_CommaDelimitNumber
            return ZO_CommaDelimitNumber(amount)
        end
        -- Comma delimit whole part and then concatenate the decimal part as-is
        local amountString = tostring(amount)
        local LITERAL_MATCH = true
        local decimalSeparatorIndex = zo_strfind(amountString, ENGLISH_DIGIT_GROUP_DECIMAL_REPLACER, 1, LITERAL_MATCH)
        local decimalPartString = zo_strsub(amountString, decimalSeparatorIndex)
        return ZO_CommaDelimitNumber(wholeAmount)..decimalPartString
    end
    -- This is a replacement for zo_strformat(SI_NUMBER_FORMAT, decimalNumberString) that avoids the slow call to grammar.
    -- decimalNumberString should be a number string that contains ASCII digits, commas, periods, and/or a negative sign. It can have a non number suffix, so we can continue to support abbreviations, but it really shouldn't contain any more than that.
    -- The return value should not be used as a component of larger format strings, and an amount string should not be run through it more than once.
    local ENGLISH_DIGIT_SEPARATOR_PATTERN = "[,.]"
    local ENGLISH_DIGIT_SEPARATOR_TO_LOCALIZED_SEPARATOR =
    {
        [ENGLISH_DIGIT_GROUP_REPLACER] = GetString(SI_DIGIT_GROUP_SEPARATOR),
        [ENGLISH_DIGIT_GROUP_DECIMAL_REPLACER] = GetString(SI_DIGIT_DECIMAL_SEPARATOR),
    }
    function ZO_FastFormatDecimalNumber(decimalNumberString)
        local firstNonNumberCharacter = zo_strfind(decimalNumberString, "[^%d%.,-]")
        if firstNonNumberCharacter then
            local numberPrefix = zo_strsub(decimalNumberString, 1, firstNonNumberCharacter - 1)
            local nonNumberSuffix = zo_strsub(decimalNumberString, firstNonNumberCharacter, -1)
            return zo_strgsub(numberPrefix, ENGLISH_DIGIT_SEPARATOR_PATTERN, ENGLISH_DIGIT_SEPARATOR_TO_LOCALIZED_SEPARATOR) .. nonNumberSuffix
        else
            return zo_strgsub(decimalNumberString, ENGLISH_DIGIT_SEPARATOR_PATTERN, ENGLISH_DIGIT_SEPARATOR_TO_LOCALIZED_SEPARATOR)
        end
    end
end
do
    local NON_DIGIT = "%D"
    function ZO_CountDigitsInNumber(amount)
        local amountString = tostring(amount)
        if amountString then
            amountString = zo_strgsub(amountString, NON_DIGIT, "")
            return ZoUTF8StringLength(amountString)
        end
        return 0
    end
end
function ZO_GenerateDelimiterSeparatedListWithCustomFinalDelimiter(argumentTable, delimiter, finalDelimiter, finalDelimiterIfListLengthIsTwo)
    if argumentTable ~= nil and #argumentTable > 0 then
        local numArguments = #argumentTable
        -- If there's only one item in the list, the string is just the first item
        if numArguments == 1 then
            return argumentTable[1]
        else
            -- loop through the first through the second to last element adding the delimiter in between
            -- don't add the last since we will use a different separator for it
            local listString = table.concat(argumentTable, delimiter, 1, numArguments - 1)
            -- add the last element of the array to the list using finalDelimiter or finalDelimiterIfListLengthIsTwo as appropriate
            local finalSeparator = finalDelimiter
            if numArguments == 2 and finalDelimiterIfListLengthIsTwo then
                finalSeparator = finalDelimiterIfListLengthIsTwo
            end
            listString = string.format('%s%s%s', listString, finalSeparator, argumentTable[numArguments])
            return listString
        end
    else
        return ""
    end
end
function ZO_GenerateCommaSeparatedListWithAnd(argumentTable)
    return ZO_GenerateDelimiterSeparatedListWithCustomFinalDelimiter(argumentTable, GetString(SI_LIST_COMMA_SEPARATOR), GetString(SI_LIST_COMMA_AND_SEPARATOR), GetString(SI_LIST_AND_SEPARATOR))
end
function ZO_GenerateCommaSeparatedListWithOr(argumentTable)
    return ZO_GenerateDelimiterSeparatedListWithCustomFinalDelimiter(argumentTable, GetString(SI_LIST_COMMA_SEPARATOR), GetString(SI_LIST_COMMA_OR_SEPARATOR), GetString(SI_LIST_OR_SEPARATOR))
end
function ZO_GenerateDelimiterSeparatedList(argumentTable, delimiter)
    if argumentTable ~= nil then
        return table.concat(argumentTable, delimiter)
    else
        return ""
    end
end
function ZO_GenerateCommaSeparatedListWithoutAnd(argumentTable)
    return ZO_GenerateDelimiterSeparatedList(argumentTable, GetString(SI_LIST_COMMA_SEPARATOR))
end
function ZO_GenerateSpaceSeparatedList(argumentTable)
    return ZO_GenerateDelimiterSeparatedList(argumentTable, GetString(SI_LIST_SPACE_SEPARATOR))
end
function ZO_GenerateNewlineSeparatedList(argumentTable)
    return ZO_GenerateDelimiterSeparatedList(argumentTable, "\n")
end
function ZO_GenerateParagraphSeparatedList(argumentTable)
    return ZO_GenerateDelimiterSeparatedList(argumentTable, "\n\n")
end
function ZO_FormatFraction(numerator, denominator)
    return string.format("%d/%d", numerator, denominator)
end
-- Takes a given text and generates a string with the text broken into segments of
-- the specified length and separated by the given delimiter
-- For example a text of "12345678" with a space delimiter and a segment length of 4
-- would yield "1234 5678"
function ZO_GenerateDelimiterSegmentedString(text, segmentLength, delimiter)
    local stringLength = zo_strlen(text)
    local textSegments = {}
    for i = 1, stringLength, segmentLength do
        table.insert(textSegments, zo_strsub(text, i, i + segmentLength - 1))
    end
    local formattedText = table.concat(textSegments, delimiter)
    return formattedText
end