vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: AceLocale-2.2
3 Revision: $Rev: 15291 $
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
5 Inspired By: Ace 1.x by Turan (turan@gryphon.com)
6 Website: http://www.wowace.com/
7 Documentation: http://www.wowace.com/index.php/AceLocale-2.2
8 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2
9 Description: Localization library for addons to use to handle proper
10 localization and internationalization.
11 Dependencies: AceLibrary
12 ]]
13  
14 local MAJOR_VERSION = "AceLocale-2.2"
15 local MINOR_VERSION = "$Revision: 15291 $"
16  
17 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
18 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
19  
20 local AceLocale = {}
21  
22 local DEFAULT_LOCALE = "enUS"
23 local _G = getfenv(0)
24  
25 local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME
26  
27 local rawget = rawget
28 local rawset = rawset
29 local type = type
30  
31 local newRegistries = {}
32 local scheduleClear
33  
34 local lastSelf
35 local __index = function(self, key)
36 lastSelf = self
37 local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key]
38 rawset(self, key, value)
39 return value
40 end
41  
42 local __newindex = function(self, k, v)
43 if type(v) ~= "function" and type(k) ~= "table" then
44 AceLocale.error(self, "Cannot change the values of an AceLocale instance.")
45 end
46 rawset(self, k, v)
47 end
48  
49 local __tostring = function(self)
50 if type(rawget(self, 'GetLibraryVersion')) == "function" then
51 return self:GetLibraryVersion()
52 else
53 return "AceLocale(" .. self[NAME] .. ")"
54 end
55 end
56  
57 local function clearCache(self)
58 if not rawget(self, BASE_TRANSLATIONS) then
59 return
60 end
61  
62 local cache = self[BASE_TRANSLATIONS]
63 rawset(self, REVERSE_TRANSLATIONS, nil)
64  
65 for k in pairs(self) do
66 if rawget(cache, k) ~= nil then
67 self[k] = nil
68 end
69 end
70 rawset(self, 'tmp', true)
71 self.tmp = nil
72 end
73  
74 local function refixInstance(instance)
75 if getmetatable(instance) then
76 setmetatable(instance, nil)
77 end
78 local translations = instance[TRANSLATIONS]
79 if translations then
80 if getmetatable(translations) then
81 setmetatable(translations, nil)
82 end
83 local baseTranslations = instance[BASE_TRANSLATIONS]
84 if getmetatable(baseTranslations) then
85 setmetatable(baseTranslations, nil)
86 end
87 if translations == baseTranslations or instance[STRICTNESS] then
88 setmetatable(instance, {
89 __index = __index,
90 __newindex = __newindex,
91 __tostring = __tostring
92 })
93  
94 setmetatable(translations, {
95 __index = AceLocale.prototype
96 })
97 else
98 setmetatable(instance, {
99 __index = __index,
100 __newindex = __newindex,
101 __tostring = __tostring
102 })
103  
104 setmetatable(translations, {
105 __index = baseTranslations,
106 })
107  
108 setmetatable(baseTranslations, {
109 __index = AceLocale.prototype,
110 })
111 end
112 else
113 setmetatable(instance, {
114 __index = __index,
115 __newindex = __newindex,
116 __tostring = __tostring,
117 })
118 end
119 clearCache(instance)
120 newRegistries[instance] = true
121 scheduleClear()
122 return instance
123 end
124  
125 function AceLocale:new(name)
126 self:argCheck(name, 2, "string")
127  
128 if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then
129 return self.registry[name]
130 end
131  
132 AceLocale.registry[name] = refixInstance({
133 [STRICTNESS] = false,
134 [NAME] = name,
135 })
136 newRegistries[AceLocale.registry[name]] = true
137 return AceLocale.registry[name]
138 end
139  
140 AceLocale.prototype = { class = AceLocale }
141  
142 function AceLocale.prototype:EnableDebugging()
143 if rawget(self, BASE_TRANSLATIONS) then
144 AceLocale.error(self, "Cannot enable debugging after a translation has been registered.")
145 end
146 rawset(self, DEBUGGING, true)
147 end
148  
149 function AceLocale.prototype:EnableDynamicLocales(override)
150 AceLocale.argCheck(self, override, 2, "boolean", "nil")
151 if not override and rawget(self, BASE_TRANSLATIONS) then
152 AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.")
153 end
154 if not rawget(self, DYNAMIC_LOCALES) then
155 rawset(self, DYNAMIC_LOCALES, true)
156 if rawget(self, BASE_LOCALE) then
157 if not rawget(self, TRANSLATION_TABLES) then
158 rawset(self, TRANSLATION_TABLES, {})
159 end
160 self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS]
161 self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS]
162 end
163 end
164 end
165  
166 function AceLocale.prototype:RegisterTranslations(locale, func)
167 AceLocale.argCheck(self, locale, 2, "string")
168 AceLocale.argCheck(self, func, 3, "function")
169  
170 if locale == rawget(self, BASE_LOCALE) then
171 AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
172 end
173  
174 if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then
175 if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
176 if not rawget(self, TRANSLATION_TABLES) then
177 rawset(self, TRANSLATION_TABLES, {})
178 end
179 if self[TRANSLATION_TABLES][locale] then
180 AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
181 end
182 local t = func()
183 func = nil
184 if type(t) ~= "table" then
185 AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
186 end
187 self[TRANSLATION_TABLES][locale] = t
188 t = nil
189 end
190 func = nil
191 return
192 end
193 local t = func()
194 func = nil
195 if type(t) ~= "table" then
196 AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
197 end
198  
199 rawset(self, TRANSLATIONS, t)
200 if not rawget(self, BASE_TRANSLATIONS) then
201 rawset(self, BASE_TRANSLATIONS, t)
202 rawset(self, BASE_LOCALE, locale)
203 for key,value in pairs(t) do
204 if value == true then
205 t[key] = key
206 end
207 end
208 else
209 for key, value in pairs(self[TRANSLATIONS]) do
210 if not rawget(self[BASE_TRANSLATIONS], key) then
211 AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale)
212 end
213 if value == true then
214 AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale)
215 end
216 end
217 end
218 rawset(self, CURRENT_LOCALE, locale)
219 refixInstance(self)
220 if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
221 if not rawget(self, TRANSLATION_TABLES) then
222 rawset(self, TRANSLATION_TABLES, {})
223 end
224 self[TRANSLATION_TABLES][locale] = t
225 end
226 t = nil
227 end
228  
229 function AceLocale.prototype:SetLocale(locale)
230 AceLocale.argCheck(self, locale, 2, "string", "boolean")
231 if not rawget(self, DYNAMIC_LOCALES) then
232 AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.")
233 end
234 if not rawget(self, TRANSLATION_TABLES) then
235 AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.")
236 end
237 if locale == true then
238 locale = GetLocale()
239 if not self[TRANSLATION_TABLES][locale] then
240 locale = self[BASE_LOCALE]
241 end
242 end
243  
244 if self[CURRENT_LOCALE] == locale then
245 return
246 end
247  
248 if not self[TRANSLATION_TABLES][locale] then
249 AceLocale.error(self, "Locale %q not registered.", locale)
250 end
251  
252 self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale]
253 self[CURRENT_LOCALE] = locale
254 refixInstance(self)
255 end
256  
257 function AceLocale.prototype:GetLocale()
258 if not rawget(self, TRANSLATION_TABLES) then
259 AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.")
260 end
261 return self[CURRENT_LOCALE]
262 end
263  
264 local function iter(t, position)
265 return (next(t, position))
266 end
267  
268 function AceLocale.prototype:IterateAvailableLocales()
269 if not rawget(self, DYNAMIC_LOCALES) then
270 AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.")
271 end
272 if not rawget(self, TRANSLATION_TABLES) then
273 AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.")
274 end
275 return iter, self[TRANSLATION_TABLES], nil
276 end
277  
278 function AceLocale.prototype:HasLocale(locale)
279 if not rawget(self, DYNAMIC_LOCALES) then
280 AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.")
281 end
282 AceLocale.argCheck(self, locale, 2, "string")
283 return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil
284 end
285  
286 function AceLocale.prototype:SetStrictness(strict)
287 AceLocale.argCheck(self, strict, 2, "boolean")
288 local mt = getmetatable(self)
289 if not mt then
290 AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.")
291 end
292 if not rawget(self, TRANSLATIONS) then
293 AceLocale.error(self, "No translations registered.")
294 end
295 rawset(self, STRICTNESS, strict)
296 refixInstance(self)
297 end
298  
299 local function initReverse(self)
300 rawset(self, REVERSE_TRANSLATIONS, {})
301 local alpha = self[TRANSLATIONS]
302 local bravo = self[REVERSE_TRANSLATIONS]
303 for base, localized in pairs(alpha) do
304 bravo[localized] = base
305 end
306 end
307  
308 function AceLocale.prototype:GetTranslation(text)
309 AceLocale.argCheck(self, text, 1, "string", "number")
310 if not rawget(self, TRANSLATIONS) then
311 AceLocale.error(self, "No translations registered")
312 end
313 return self[text]
314 end
315  
316 function AceLocale.prototype:GetStrictTranslation(text)
317 AceLocale.argCheck(self, text, 1, "string", "number")
318 local x = rawget(self, TRANSLATIONS)
319 if not x then
320 AceLocale.error(self, "No translations registered")
321 end
322 local value = rawget(x, text)
323 if value == nil then
324 AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE])
325 end
326 return value
327 end
328  
329 function AceLocale.prototype:GetReverseTranslation(text)
330 local x = rawget(self, REVERSE_TRANSLATIONS)
331 if not x then
332 if not rawget(self, TRANSLATIONS) then
333 AceLocale.error(self, "No translations registered")
334 end
335 initReverse(self)
336 x = self[REVERSE_TRANSLATIONS]
337 end
338 local translation = x[text]
339 if not translation then
340 AceLocale.error(self, "Reverse translation for %q does not exist", text)
341 end
342 return translation
343 end
344  
345 function AceLocale.prototype:GetIterator()
346 local x = rawget(self, TRANSLATIONS)
347 if not x then
348 AceLocale.error(self, "No translations registered")
349 end
350 return next, x, nil
351 end
352  
353 function AceLocale.prototype:GetReverseIterator()
354 local x = rawget(self, REVERSE_TRANSLATIONS)
355 if not x then
356 if not rawget(self, TRANSLATIONS) then
357 AceLocale.error(self, "No translations registered")
358 end
359 initReverse(self)
360 x = self[REVERSE_TRANSLATIONS]
361 end
362 return next, x, nil
363 end
364  
365 function AceLocale.prototype:HasTranslation(text)
366 AceLocale.argCheck(self, text, 1, "string", "number")
367 local x = rawget(self, TRANSLATIONS)
368 if not x then
369 AceLocale.error(self, "No translations registered")
370 end
371 return rawget(x, text) and true
372 end
373  
374 function AceLocale.prototype:HasReverseTranslation(text)
375 local x = rawget(self, REVERSE_TRANSLATIONS)
376 if not x then
377 if not rawget(self, TRANSLATIONS) then
378 AceLocale.error(self, "No translations registered")
379 end
380 initReverse(self)
381 x = self[REVERSE_TRANSLATIONS]
382 end
383 return x[text] and true
384 end
385  
386 function AceLocale.prototype:Debug()
387 if not rawget(self, DEBUGGING) then
388 return
389 end
390 local words = {}
391 local locales = {"enUS", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"}
392 local localizations = {}
393 DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---")
394 for _,locale in ipairs(locales) do
395 if not self[TRANSLATION_TABLES][locale] then
396 DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale))
397 else
398 localizations[locale] = self[TRANSLATION_TABLES][locale]
399 end
400 end
401 local localeDebug = {}
402 for locale, localization in pairs(localizations) do
403 localeDebug[locale] = {}
404 for word in pairs(localization) do
405 if type(localization[word]) == "table" then
406 if type(words[word]) ~= "table" then
407 words[word] = {}
408 end
409 for bit in pairs(localization[word]) do
410 if type(localization[word][bit]) == "string" then
411 words[word][bit] = true
412 end
413 end
414 elseif type(localization[word]) == "string" then
415 words[word] = true
416 end
417 end
418 end
419 for word in pairs(words) do
420 if type(words[word]) == "table" then
421 for bit in pairs(words[word]) do
422 for locale, localization in pairs(localizations) do
423 if not rawget(localization, word) or not localization[word][bit] then
424 localeDebug[locale][word .. "::" .. bit] = true
425 end
426 end
427 end
428 else
429 for locale, localization in pairs(localizations) do
430 if not rawget(localization, word) then
431 localeDebug[locale][word] = true
432 end
433 end
434 end
435 end
436 for locale, t in pairs(localeDebug) do
437 if not next(t) then
438 DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale))
439 else
440 DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale))
441 for word in pairs(t) do
442 DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word))
443 end
444 end
445 end
446 DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---")
447 end
448  
449 setmetatable(AceLocale.prototype, {
450 __index = function(self, k)
451 if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later.
452 AceLocale.error(lastSelf or self, "Translation %q does not exist.", k)
453 end
454 return nil
455 end
456 })
457  
458 local function activate(self, oldLib, oldDeactivate)
459 AceLocale = self
460  
461 self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
462 self.registry = oldLib and oldLib.registry or {}
463 self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {}
464 self.DEBUGGING = oldLib and oldLib.DEBUGGING or {}
465 self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {}
466 self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {}
467 self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {}
468 self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {}
469 self.STRICTNESS = oldLib and oldLib.STRICTNESS or {}
470 self.NAME = oldLib and oldLib.NAME or {}
471 self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {}
472 self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {}
473  
474 BASE_TRANSLATIONS = self.BASE_TRANSLATIONS
475 DEBUGGING = self.DEBUGGING
476 TRANSLATIONS = self.TRANSLATIONS
477 BASE_LOCALE = self.BASE_LOCALE
478 TRANSLATION_TABLES = self.TRANSLATION_TABLES
479 REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS
480 STRICTNESS = self.STRICTNESS
481 NAME = self.NAME
482 DYNAMIC_LOCALES = self.DYNAMIC_LOCALES
483 CURRENT_LOCALE = self.CURRENT_LOCALE
484  
485  
486 local GetTime = GetTime
487 local timeUntilClear = GetTime() + 5
488 scheduleClear = function()
489 if next(newRegistries) then
490 self.frame:Show()
491 timeUntilClear = GetTime() + 5
492 end
493 end
494  
495 if not self.registry then
496 self.registry = {}
497 else
498 for name, instance in pairs(self.registry) do
499 local name = name
500 local mt = getmetatable(instance)
501 setmetatable(instance, nil)
502 instance[NAME] = name
503 local strict
504 if instance[STRICTNESS] ~= nil then
505 strict = instance[STRICTNESS]
506 elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then
507 if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then
508 strict = true
509 end
510 end
511 instance[STRICTNESS] = strict and true or false
512 refixInstance(instance)
513 end
514 end
515  
516 self.frame:SetScript("OnEvent", scheduleClear)
517 self.frame:SetScript("OnUpdate", function() -- (this, elapsed)
518 if timeUntilClear - GetTime() <= 0 then
519 self.frame:Hide()
520 for k in pairs(newRegistries) do
521 clearCache(k)
522 newRegistries[k] = nil
523 k = nil
524 end
525 end
526 end)
527 self.frame:UnregisterAllEvents()
528 self.frame:RegisterEvent("ADDON_LOADED")
529 self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
530 self.frame:Show()
531  
532 if oldDeactivate then
533 oldDeactivate(oldLib)
534 end
535 end
536  
537 AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)