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: 13951 $
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: 13951 $"
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 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("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(override, 2, "boolean", "nil")
151 if not override and rawget(self, BASE_TRANSLATIONS) then
152 AceLocale:error("Cannot enable dynamic locales after a translation has been registered.")
153 end
154 rawset(self, DYNAMIC_LOCALES, true)
155 end
156  
157 function AceLocale.prototype:RegisterTranslations(locale, func)
158 AceLocale.argCheck(self, locale, 2, "string")
159 AceLocale.argCheck(self, func, 3, "function")
160  
161 if locale == rawget(self, BASE_LOCALE) then
162 AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
163 end
164  
165 if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then
166 if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
167 if self[TRANSLATION_TABLES][locale] then
168 AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
169 end
170 local t = func()
171 func = nil
172 if type(t) ~= "table" then
173 AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
174 end
175 self[TRANSLATION_TABLES][locale] = t
176 t = nil
177 end
178 func = nil
179 return
180 end
181 local t = func()
182 func = nil
183 if type(t) ~= "table" then
184 AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
185 end
186  
187 rawset(self, TRANSLATIONS, t)
188 if not rawget(self, BASE_TRANSLATIONS) then
189 rawset(self, BASE_TRANSLATIONS, t)
190 rawset(self, BASE_LOCALE, locale)
191 for key,value in pairs(t) do
192 if value == true then
193 t[key] = key
194 end
195 end
196 else
197 for key, value in pairs(self[TRANSLATIONS]) do
198 if not rawget(self[BASE_TRANSLATIONS], key) then
199 AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale)
200 end
201 if value == true then
202 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)
203 end
204 end
205 end
206 rawset(self, CURRENT_LOCALE, locale)
207 refixInstance(self)
208 if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
209 if not rawget(self, TRANSLATION_TABLES) then
210 rawset(self, TRANSLATION_TABLES, {})
211 end
212 self[TRANSLATION_TABLES][locale] = t
213 end
214 t = nil
215 end
216  
217 function AceLocale.prototype:SetLocale(locale)
218 AceLocale.argCheck(self, locale, 2, "string", "boolean")
219 if not rawget(self, DYNAMIC_LOCALES) then
220 AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.")
221 end
222 if not rawget(self, TRANSLATION_TABLES) then
223 AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.")
224 end
225 if locale == true then
226 locale = GetLocale()
227 if not self[TRANSLATION_TABLES][locale] then
228 locale = self[BASE_LOCALE]
229 end
230 end
231  
232 if self[CURRENT_LOCALE] == locale then
233 return
234 end
235  
236 if not self[TRANSLATION_TABLES][locale] then
237 AceLocale.error(self, "Locale %q not registered.", locale)
238 end
239  
240 self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale]
241 self[CURRENT_LOCALE] = locale
242 refixInstance(self)
243 end
244  
245 function AceLocale.prototype:GetLocale()
246 if not rawget(self, TRANSLATION_TABLES) then
247 AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.")
248 end
249 return self[CURRENT_LOCALE]
250 end
251  
252 local function iter(t, position)
253 return (next(t, position))
254 end
255  
256 function AceLocale.prototype:IterateAvailableLocales()
257 if not rawget(self, DYNAMIC_LOCALES) then
258 AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.")
259 end
260 if not rawget(self, TRANSLATION_TABLES) then
261 AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.")
262 end
263 return iter, self[TRANSLATION_TABLES], nil
264 end
265  
266 function AceLocale.prototype:SetStrictness(strict)
267 AceLocale.argCheck(self, strict, 2, "boolean")
268 local mt = getmetatable(self)
269 if not mt then
270 AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.")
271 end
272 if not rawget(self, TRANSLATIONS) then
273 AceLocale.error(self, "No translations registered.")
274 end
275 rawset(self, STRICTNESS, strict)
276 refixInstance(self)
277 end
278  
279 local function initReverse(self)
280 rawset(self, REVERSE_TRANSLATIONS, {})
281 local alpha = self[TRANSLATIONS]
282 local bravo = self[REVERSE_TRANSLATIONS]
283 for base, localized in pairs(alpha) do
284 bravo[localized] = base
285 end
286 end
287  
288 function AceLocale.prototype:GetTranslation(text)
289 AceLocale.argCheck(self, text, 1, "string", "number")
290 if not rawget(self, TRANSLATIONS) then
291 AceLocale.error(self, "No translations registered")
292 end
293 return self[text]
294 end
295  
296 function AceLocale.prototype:GetStrictTranslation(text)
297 AceLocale.argCheck(self, text, 1, "string", "number")
298 local x = rawget(self, TRANSLATIONS)
299 if not x then
300 AceLocale.error(self, "No translations registered")
301 end
302 local value = rawget(x, text)
303 if value == nil then
304 AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE])
305 end
306 return value
307 end
308  
309 function AceLocale.prototype:GetReverseTranslation(text)
310 local x = rawget(self, REVERSE_TRANSLATIONS)
311 if not x then
312 if not rawget(self, TRANSLATIONS) then
313 AceLocale.error(self, "No translations registered")
314 end
315 initReverse(self)
316 x = self[REVERSE_TRANSLATIONS]
317 end
318 local translation = x[text]
319 if not translation then
320 AceLocale.error(self, "Reverse translation for %q does not exist", text)
321 end
322 return translation
323 end
324  
325 function AceLocale.prototype:GetIterator()
326 local x = rawget(self, TRANSLATIONS)
327 if not x then
328 AceLocale.error(self, "No translations registered")
329 end
330 return next, x, nil
331 end
332  
333 function AceLocale.prototype:GetReverseIterator()
334 local x = rawget(self, REVERSE_TRANSLATIONS)
335 if not x then
336 if not rawget(self, TRANSLATIONS) then
337 AceLocale.error(self, "No translations registered")
338 end
339 initReverse(self)
340 x = self[REVERSE_TRANSLATIONS]
341 end
342 return next, x, nil
343 end
344  
345 function AceLocale.prototype:HasTranslation(text)
346 AceLocale.argCheck(self, text, 1, "string", "number")
347 local x = rawget(self, TRANSLATIONS)
348 if not x then
349 AceLocale.error(self, "No translations registered")
350 end
351 return rawget(x, text) and true
352 end
353  
354 function AceLocale.prototype:HasReverseTranslation(text)
355 local x = rawget(self, REVERSE_TRANSLATIONS)
356 if not x then
357 if not rawget(self, TRANSLATIONS) then
358 AceLocale.error(self, "No translations registered")
359 end
360 initReverse(self)
361 x = self[REVERSE_TRANSLATIONS]
362 end
363 return x[text] and true
364 end
365  
366 function AceLocale.prototype:Debug()
367 if not rawget(self, DEBUGGING) then
368 return
369 end
370 local words = {}
371 local locales = {"enUS", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"}
372 local localizations = {}
373 DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---")
374 for _,locale in ipairs(locales) do
375 if not self[TRANSLATION_TABLES][locale] then
376 DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale))
377 else
378 localizations[locale] = self[TRANSLATION_TABLES][locale]
379 end
380 end
381 local localeDebug = {}
382 for locale, localization in pairs(localizations) do
383 localeDebug[locale] = {}
384 for word in pairs(localization) do
385 if type(localization[word]) == "table" then
386 if type(words[word]) ~= "table" then
387 words[word] = {}
388 end
389 for bit in pairs(localization[word]) do
390 if type(localization[word][bit]) == "string" then
391 words[word][bit] = true
392 end
393 end
394 elseif type(localization[word]) == "string" then
395 words[word] = true
396 end
397 end
398 end
399 for word in pairs(words) do
400 if type(words[word]) == "table" then
401 for bit in pairs(words[word]) do
402 for locale, localization in pairs(localizations) do
403 if not rawget(localization, word) or not localization[word][bit] then
404 localeDebug[locale][word .. "::" .. bit] = true
405 end
406 end
407 end
408 else
409 for locale, localization in pairs(localizations) do
410 if not rawget(localization, word) then
411 localeDebug[locale][word] = true
412 end
413 end
414 end
415 end
416 for locale, t in pairs(localeDebug) do
417 if not next(t) then
418 DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale))
419 else
420 DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale))
421 for word in pairs(t) do
422 DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word))
423 end
424 end
425 end
426 DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---")
427 end
428  
429 setmetatable(AceLocale.prototype, {
430 __index = function(self, k)
431 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.
432 AceLocale.error(lastSelf or self, "Translation %q does not exist.", k)
433 end
434 return nil
435 end
436 })
437  
438 local function activate(self, oldLib, oldDeactivate)
439 AceLocale = self
440  
441 self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
442 self.registry = oldLib and oldLib.registry or {}
443 self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {}
444 self.DEBUGGING = oldLib and oldLib.DEBUGGING or {}
445 self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {}
446 self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {}
447 self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {}
448 self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {}
449 self.STRICTNESS = oldLib and oldLib.STRICTNESS or {}
450 self.NAME = oldLib and oldLib.NAME or {}
451 self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {}
452 self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {}
453  
454 BASE_TRANSLATIONS = self.BASE_TRANSLATIONS
455 DEBUGGING = self.DEBUGGING
456 TRANSLATIONS = self.TRANSLATIONS
457 BASE_LOCALE = self.BASE_LOCALE
458 TRANSLATION_TABLES = self.TRANSLATION_TABLES
459 REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS
460 STRICTNESS = self.STRICTNESS
461 NAME = self.NAME
462 DYNAMIC_LOCALES = self.DYNAMIC_LOCALES
463 CURRENT_LOCALE = self.CURRENT_LOCALE
464  
465 if not self.registry then
466 self.registry = {}
467 else
468 for name, instance in pairs(self.registry) do
469 local name = name
470 local mt = getmetatable(instance)
471 setmetatable(instance, nil)
472 instance[NAME] = name
473 local strict
474 if instance[STRICTNESS] ~= nil then
475 strict = instance[STRICTNESS]
476 elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then
477 if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then
478 strict = true
479 end
480 end
481 instance[STRICTNESS] = strict and true or false
482 refixInstance(instance)
483 end
484 end
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 self.frame:SetScript("OnEvent", scheduleClear)
495 self.frame:SetScript("OnUpdate", function() -- (this, elapsed)
496 if timeUntilClear - GetTime() <= 0 then
497 self.frame:Hide()
498 for k in pairs(newRegistries) do
499 clearCache(k)
500 newRegistries[k] = nil
501 k = nil
502 end
503 end
504 end)
505 self.frame:UnregisterAllEvents()
506 self.frame:RegisterEvent("ADDON_LOADED")
507 self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
508 self.frame:Show()
509  
510 if oldDeactivate then
511 oldDeactivate(oldLib)
512 end
513 end
514  
515 AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)