From 7361c012db1c8a825e9b9229ae7c9dc908de10e1 Mon Sep 17 00:00:00 2001 From: Qwreey Date: Fri, 17 Feb 2023 09:25:39 +0900 Subject: [PATCH] feat: Add lang module --- src/class.lua | 30 ++++++-- src/init.lua | 6 +- src/lang.lua | 173 ++++++++++++++++++++++++++++++++++++++++-- src/signal.lua | 2 +- src/types.lua | 108 +++++++++++++++++++++++++- testing/init.lua | 4 +- testing/test/init.lua | 2 +- testing/tween.lua | 17 ++++- 8 files changed, 323 insertions(+), 19 deletions(-) diff --git a/src/class.lua b/src/class.lua index 88d5ba8..7296e73 100644 --- a/src/class.lua +++ b/src/class.lua @@ -25,6 +25,8 @@ function module.init(shared) local parseStyles = style.ParseStyles local advancedTween = shared.Tween ---@type quad_module_tween local round = shared.Round ---@type quad_module_round + local signal = shared.Signal ---@type quad_module_signal + local bindable = signal.Bindable local function InstanceNewWithName(classname,parent,name) local item = InstanceNew(classname,parent) @@ -114,6 +116,7 @@ function module.init(shared) if ok then return err end return nil end + new.PcallGetProperty = PcallGetProperty local function ProcessQuadProperty(processedProperty,iprop,holder,item,className,index,value) if processedProperty[index] then @@ -341,15 +344,21 @@ function module.init(shared) end function this:GetPropertyChangedSignal(propertyName) - local signal = self.__propertyChangedSignals[propertyName] - if not signal then - signal = {} - self.__propertyChangedSignals[propertyName] = signal + local thisSignal = self.__propertyChangedSignals[propertyName] + if thisSignal then + return thisSignal end + thisSignal = bindable.New() + self.__propertyChangedSignals[propertyName] = thisSignal + return thisSignal end function this:EmitPropertyChangedSignal(propertyName,value) - + local thisSignal = self.__propertyChangedSignals[propertyName] + if not thisSignal then + return + end + thisSignal:Fire(value) end --- re-render object (if some props chaged, call this to render again) @@ -453,9 +462,18 @@ function module.init(shared) if this.UpdateTriggers[k] then self:Update() end + + -- call PropertyChangedSignal + if self.__propertyChangedSignals[k] and self[k] ~= v then + self:EmitPropertyChangedSignal(k,v) + end end - this.Getter = {} + this.Getter = { + Parent = function(self) + return rawget(self,"__parent") + end + } this.Setter = { Parent = function(self,parent,object) rawset(self,"__parent",parent) diff --git a/src/init.lua b/src/init.lua index a746eff..7c6f548 100644 --- a/src/init.lua +++ b/src/init.lua @@ -1,8 +1,12 @@ ---@class quad local module = {} +if not Instance then + print("Instance is not exist, Using custom Instance and print result as string . . .") +end + local IS_TEST = true -local VER = "1.14" .. (IS_TEST and "B" or "") +local VER = "2.14" .. (IS_TEST and "B" or "") if IS_TEST then warn("You are using BETA version of quad now. Many features may change in the future and unstable. AS USING BETA VERSION OF QUAD, YOU SHOULD KNOW IT WILL BUGGY SOMETIME. still on development - you can ignore this message if have no issue. if you have issue on using quad, please report issue on github. VERSION: "..VER) end diff --git a/src/lang.lua b/src/lang.lua index 50a3274..eb09c42 100644 --- a/src/lang.lua +++ b/src/lang.lua @@ -1,11 +1,174 @@ local module = {} -function module.init(shared) - ---@class quad_module_lang - local new = {} +local insert = table.insert +local gsub = string.gsub - return new +function module.init(shared) + ---@class quad_module_lang + local new = {} + local warn = shared.warn + local store = shared.Store + local class = shared.Class + local PcallGetProperty = class.PcallGetProperty + + new.Locales = { + ["English"] = "en_us"; + ["Spanish"] = "es_es"; + ["French"] = "fr_fr"; + ["Indonesian"] = "id_id"; + ["Italian"] = "it_it"; + ["Japanese"] = "ja_jp"; + ["Korean"] = "ko_kr"; + ["Russian"] = "ru_ru"; + ["Thai"] = "th_th"; + ["Turkish"] = "tr_tr"; + ["Vietnamese"] = "vi_vn"; + ["Portuguese"] = "pt_br"; + ["German"] = "de_de"; + ["ChineseSimplified"] = "zh_cn"; + ["ChineseTraditional"] = "zh_tw"; + ["Bulgarian"] = "bg_bg"; + ["Bengali"] = "bn_bd"; + ["Czech"] = "cs_cz"; + ["Danish"] = "da_dk"; + ["Greek"] = "el_gr"; + ["Estonian"] = "et_ee"; + ["Finnish"] = "fi_fi"; + ["Hindi"] = "hi_in"; + ["Croatian"] = "hr_hr"; + ["Hungarian"] = "hu_hu"; + ["Georgian"] = "ka_ge"; + ["Kazakh"] = "kk_kz"; + ["Khmer"] = "km_kh"; + ["Lithuanian"] = "lt_lt"; + ["Latvian"] = "lv_lv"; + ["Malay"] = "ms_my"; + ["Burmese"] = "my_mm"; + ["Bokmal"] = "nb_no"; + ["Dutch"] = "nl_nl"; + ["Filipino"] = "fil_ph"; + ["Polish"] = "pl_pl"; + ["Romanian"] = "ro_ro"; + ["Ukrainian"] = "uk_ua"; + ["Sinhala"] = "si_lk"; + ["Slovak"] = "sk_sk"; + ["Slovenian"] = "sl_sl"; + ["Albanian"] = "sq_al"; + ["Bosnian"] = "bs_ba"; + ["Serbian"] = "sr_rs"; + ["Swedish"] = "sv_se"; + ["Arabic"] = "ar_001"; + } + new.Lang = game:GetService("LocalizationService").RobloxLocaleId + new.Default = new.Locales.English + + local lang = {} + lang.__index = lang + function lang.New(id,handlers) + local this = {handlers=handlers,id=id,store=store.New(),index=1} + lang[id] = this + setmetatable(this,lang) + end + + -- read options, and make string and save into store for updating + function lang.Update(valueStore,handler,index,options) + if not index then + error(("Index must be a number, but got %s"):format(type(index))) + end + index = tostring(index) + + -- parsing options + local parsedOptions = {} + for optionName,option in pairs(options) do + local quadType = PcallGetProperty(option,"__type") + local optionType = type(option) + local formatted + if quadType == "quad_register" then + local default = option.dvalue + local with = option.wfunc + local tstore = option.store + local rawKey = option.key + local value = tstore[rawKey] + if value == nil then + value = default + elseif with then + value = with(tstore,value,rawKey) + end + formatted = value + elseif optionType == "number" then + if option%1 == 0 then + -- int + formatted = ("%d"):format(option) + else + -- float + formatted = tostring(option) + end + else + formatted = tostring(option) + end + parsedOptions[optionName] = formatted + end + + -- set store value + local handlerType = type(handler) + if handlerType == "function" then + valueStore[index] = handler(parsedOptions) + elseif handlerType == "string" then + valueStore[index] = gsub(handler,"{(.-)}",parsedOptions) + end + end + local Update = lang.Update + + function lang:Format(options) + -- find handler + local handler = self.handlers[new.Lang] + if not handler then + handler = self.handlers[new.Default] + end + if not handler then + warn(("Langauge handle %s is not found on Localization %s"):format(new.Default,self.id)) + end + local index = self.index + self.index = index + 1 + + -- setup updater + for _,option in pairs(options) do + local quadType = PcallGetProperty(option,"__type") + -- if it is register, setup updater + if quadType == "quad_register" then + local function regFn() + -- !HOLD IT SELF TO PREVENT THIS REGISTER BEGIN REMOVED FROM MEMORY + Update(self.store,handler,index,options) + end + option:Register(regFn) + insert(self.store.__keep,regFn) + end + end + + -- update once + Update(self.store,handler,index,options) + + -- return resiter + return self.store(tostring(index)) + end + + function new.New(id,handlers) + return lang.New(id,handlers) + end + + setmetatable(new,{ + __call = function(_,id) + return function (options) + local data = lang[id] + if not data then + error(("Localization %s is not definded"):format(id)) + end + return data:Format(options) + end + end; + }) + + return new end return module - diff --git a/src/signal.lua b/src/signal.lua index e8f2a6e..cf40d91 100644 --- a/src/signal.lua +++ b/src/signal.lua @@ -142,7 +142,7 @@ function module.init(shared) local self = {waitting={},onceConnection={},connection={}} setmetatable(self,signal) end - new.Signal = signal + new.Bindable = signal return new end diff --git a/src/types.lua b/src/types.lua index 76df3c3..60eaca8 100644 --- a/src/types.lua +++ b/src/types.lua @@ -187,7 +187,113 @@ export type module_round = { ------------------- -- module_signal ------------------- -export type module_lang = {} +export type langOptions = { + [string]: register|string|number|any; +} +export type langHandler = { + [string]: string|(langOptions:langOptions)->string; +} +export type Locale = string +export type module_lang = { + New: (id:string,handlers:{ + en_us: langHandler; + es_es: langHandler; + fr_fr: langHandler; + id_id: langHandler; + it_it: langHandler; + ja_jp: langHandler; + ko_kr: langHandler; + ru_ru: langHandler; + th_th: langHandler; + tr_tr: langHandler; + vi_vn: langHandler; + pt_br: langHandler; + de_de: langHandler; + zh_cn: langHandler; + zh_tw: langHandler; + bg_bg: langHandler; + bn_bd: langHandler; + cs_cz: langHandler; + da_dk: langHandler; + el_gr: langHandler; + et_ee: langHandler; + fi_fi: langHandler; + hi_in: langHandler; + hr_hr: langHandler; + hu_hu: langHandler; + ka_ge: langHandler; + kk_kz: langHandler; + km_kh: langHandler; + lt_lt: langHandler; + lv_lv: langHandler; + ms_my: langHandler; + my_mm: langHandler; + nb_no: langHandler; + nl_nl: langHandler; + fil_ph: langHandler; + pl_pl: langHandler; + ro_ro: langHandler; + uk_ua: langHandler; + si_lk: langHandler; + sk_sk: langHandler; + sl_sl: langHandler; + sq_al: langHandler; + bs_ba: langHandler; + sr_rs: langHandler; + sv_se: langHandler; + ar_001: langHandler; + })->(); + Locales: { + English: Locale & "en_us"; + Spanish: Locale & "es_es"; + French: Locale & "fr_fr"; + Indonesian: Locale & "id_id"; + Italian: Locale & "it_it"; + Japanese: Locale & "ja_jp"; + Korean: Locale & "ko_kr"; + Russian: Locale & "ru_ru"; + Thai: Locale & "th_th"; + Turkish: Locale & "tr_tr"; + Vietnamese: Locale & "vi_vn"; + Portuguese: Locale & "pt_br"; + German: Locale & "de_de"; + ChineseSimplified: Locale & "zh_cn"; + ChineseTraditional: Locale & "zh_tw"; + Bulgarian: Locale & "bg_bg"; + Bengali: Locale & "bn_bd"; + Czech: Locale & "cs_cz"; + Danish: Locale & "da_dk"; + Greek: Locale & "el_gr"; + Estonian: Locale & "et_ee"; + Finnish: Locale & "fi_fi"; + Hindi: Locale & "hi_in"; + Croatian: Locale & "hr_hr"; + Hungarian: Locale & "hu_hu"; + Georgian: Locale & "ka_ge"; + Kazakh: Locale & "kk_kz"; + Khmer: Locale & "km_kh"; + Lithuanian: Locale & "lt_lt"; + Latvian: Locale & "lv_lv"; + Malay: Locale & "ms_my"; + Burmese: Locale & "my_mm"; + Bokmal: Locale & "nb_no"; + Dutch: Locale & "nl_nl"; + Filipino: Locale & "fil_ph"; + Polish: Locale & "pl_pl"; + Romanian: Locale & "ro_ro"; + Ukrainian: Locale & "uk_ua"; + Sinhala: Locale & "si_lk"; + Slovak: Locale & "sk_sk"; + Slovenian: Locale & "sl_sl"; + Albanian: Locale & "sq_al"; + Bosnian: Locale & "bs_ba"; + Serbian: Locale & "sr_rs"; + Swedish: Locale & "sv_se"; + Arabic: Locale & "ar_001"; + }; + Lang: Locale; + Default: Locale; +} & (id:string)->(langOptions)->register; ------------------- -- module diff --git a/testing/init.lua b/testing/init.lua index 26599d6..debd271 100644 --- a/testing/init.lua +++ b/testing/init.lua @@ -44,8 +44,8 @@ end function module.init() ---@module Quad.src.types - local types = require(script.Parent.Quad.types) - local quad = (require(script.Parent.Quad) :: types.module).Init("ui") + local types = require(script.Quad.types) + local quad = (require(script.Quad) :: types.module).Init("ui") local Round,Class,Mount,Store,Event,Tween,Style,Signal = quad.Round,quad.Class,quad.Mount,quad.Store,quad.Event,quad.Tween,quad.Style,quad.Signal diff --git a/testing/test/init.lua b/testing/test/init.lua index b7d4a6c..3d1232b 100644 --- a/testing/test/init.lua +++ b/testing/test/init.lua @@ -41,7 +41,7 @@ return { track = function () reload() - + wait() if not _G.uichangetracker then local tracker = require(script.tracker) local changeTracker = tracker.new(script.Parent) diff --git a/testing/tween.lua b/testing/tween.lua index 356113c..10ec75b 100644 --- a/testing/tween.lua +++ b/testing/tween.lua @@ -4,8 +4,8 @@ local module = {} ---@module Quad.src.types local types = require(script.Parent.Quad.types) local quad = (require(script.Parent.Quad) :: types.module).Init("ui") -local Round,Class,Mount,Store,Event,Tween,Style -= quad.Round,quad.Class,quad.Mount,quad.Store,quad.Event,quad.Tween,quad.Style +local Round,Class,Mount,Store,Event,Tween,Style,Lang += quad.Round,quad.Class,quad.Mount,quad.Store,quad.Event,quad.Tween,quad.Style,quad.Lang local Global = Store.GetStore "global" @@ -26,15 +26,22 @@ Style "tweenTestFrame" { BackgroundColor3 = Color3.fromRGB(0, 0, 0); } +Lang.New("tweenCount",{ + [Lang.Locales.Korean] = "{count}번 실행했어요"; + [Lang.Locales.English] = "Ran {count} times"; +}) + -- Create GUI local tweenTest = Class.Extend() function tweenTest:Init(Prop) + Prop:Default("Count",0) Prop:Default("Position",UDim2.fromOffset(50,0)) local on = false task.spawn(function() while task.wait(2) do on = not on + Prop.Count = Prop.Count + 1 Prop.Position = on and UDim2.new(1,-50-60,0,0) or UDim2.fromOffset(50,0) end end) @@ -42,6 +49,12 @@ end function tweenTest:Render(Prop) return Frame { + TextLabel { + Size = UDim2.new(1,0,0,30); + Text = Lang "tweenCount" { + count = Prop "Count":With(function(_,value) return value*2 end); + }; + }; Size = UDim2.fromScale(1,1); TextLabel "tweenTestFrame" { Text = "Linear";