---- XUL::App - Jifty way of doing XUL ---- {{#x|XUL::App}} - {{#ci|Jifty}} way of doing {{#x|XUL}} XUL::App - {{#ci|Jifty}} 风格的 XUL 开发 ☺{{#author|Agent Zhang}}☺ {{#author|章亦春}} {{#date|2007.11}} ---- {{#kw|☼}} Let's {{#x|start}} with a {{#ci|hello world}} Firefox extension! 让我们从一个 {{#ci|hello world}} Firefox 扩展开始! ---- {{#v|$}} {{#ci|xulapp}} {{#x|app}} {{#cm|--name}} HelloWorld Creating new application HelloWorld Creating directory lib/ Creating directory lib/HelloWorld/ Writing file {{#x|lib/HelloWorld/App.pm}} Creating directory js/ Creating directory css/ Creating directory icons/ Creating directory icons/default/ Creating directory t/ Creating directory po/ Creating directory docs/ {{#v|$}} ---- {{#v|$}} {{#x|cd}} {{#cm|HelloWorld/}} {{#v|$}} {{#x|ls}} css docs icons js lib po t ---- {{#cm|☺}} Let's create a {{#ci|window view}} named {{#x|HelloWin}}! 让我们来创建一个 名叫 {{#x|HelloWin}} 的{{#ci|窗口视图}}! ---- {{#v|$}} {{#ci|xulapp}} {{#x|view}} {{#cm|--name}} HelloWin {{#cm|--type}} window Creating directory lib/HelloWorld/View/ Writing file {{#x|lib/HelloWorld/View/HelloWin.pm}} {{#v|$}} ---- {{#v|$}} {{#ci|wc}} -l {{#t|lib/HelloWorld/View/HelloWin.pm}} {{#x|26}} lib/HelloWorld/View/HelloWin.pm {{#v|$}} ---- {{#cm|☺}} Now let's {{#ci|register}} our HelloWin {{#x|view}} in our {{#x|HelloWorld::App}} module. 现在让我们在 HelloWorld::App 模块中 {{#ci|注册}}一下我们的 HelloWin 视图。 ---- {{#v|$}} {{#ci|vim}} {{#t|lib/HelloWorld/App.pm}} ---- {{#cm|# File lib/HelloWorld/App.pm}} {{#kw|package}} HelloWorld::App; {{#kw|our}} {{#v|$$VERSION}} = '0.01'; {{#kw|use}} XUL::App::Schema; {{#kw|use}} XUL::App schema { xpifile 'helloworld.xpi' => name is 'HelloWorld', id is 'helloworld@agentz.agentz-office', {{#cm|# FIXME}} version is '0.0.1', targets { Firefox => ['2.0' => '3.0a5'], {{#cm|# FIXME}} }, creator is 'The HelloWorld development team', developers are ['agentz'], contributors are []; homepageURL is 'http://helloworld.agentz.org', {{#v|...}} }; 1; ---- {{#cm|# File lib/HelloWorld/App.pm}} {{#kw|package}} HelloWorld::App; {{#kw|our}} {{#v|$$VERSION}} = '0.01'; {{#kw|use}} XUL::App::Schema; {{#kw|use}} XUL::App schema { {{#x|# Code that we added by hand:}} {{#x|xulfile 'hellowin.xul' => }} {{#x|generated from 'HelloWorld::View::HelloWin';}} xpifile 'helloworld.xpi' => name is 'HelloWorld', id is 'helloworld@agentz.agentz-office', {{#cm|# FIXME}} version is '0.0.1', targets { Firefox => ['2.0' => '3.0a5'], {{#cm|# FIXME}} }, creator is 'The HelloWorld development team', {{#v|...}} ---- {{#cm|☺}} Now let's add some {{#ci|contents}} to our HelloWin {{#x|view}} class. 现在让我们往 HelloWin {{#x|视图}}类中 添加一些{{#ci|内容}}。 ---- {{#v|$}} {{#ci|vim}} {{#t|lib/HelloWorld/View/HelloWin.pm}} ---- {{#cm|# File lib/HelloWorld/View/HelloWin.pm}} {{#kw|package}} HelloWorld::View::HelloWin; {{#kw|use base}} 'XUL::App::View::Base'; {{#kw|use}} Template::Declare::Tags 'XUL'; template main => {{#kw|sub}} { show 'header'; {{#cm|# from XUL::App::View::Base}} window { attr { id => \"helloworld-hellowin\", xmlns => {{#v|$::XUL_NAME_SPACE}}, title => 'HelloWorld', {{#v|...}} } } }; 1; ---- {{#cm|# File lib/HelloWorld/View/HelloWin.pm}} {{#kw|package}} HelloWorld::View::HelloWin; {{#kw|use base}} 'XUL::App::View::Base'; {{#kw|use}} Template::Declare::Tags 'XUL'; template main => {{#kw|sub}} { show 'header'; {{#cm|# from XUL::App::View::Base}} window { attr { id => \"helloworld-hellowin\", xmlns => {{#v|$::XUL_NAME_SPACE}}, title => 'HelloWorld', {{#v|...}} } {{#x|# Code that we added by hand:}} {{#x|label { "Hello, world!" } }} } {{#v|...}} ---- {{#tag|☆}} Hey, it {{#ci|runs}} now! 嘿,它现在已经能{{#x|跑}}起来了! ---- {{#cm|# Assuming the Firefox profile dev already exists:}} {{#v|$}} {{#ci|xulapp}} {{#x|debug}} hellowin.xul {{#kw|--profile}} dev Writing file hellowin.xul Registering extension hellowin in profile dev Setting configure variables in dev's prefs.js MOZ_NO_REMOTE=1 && firefox -jsconsole -P dev -chrome \\ chrome://helloworld/content/hellowin.xul ---- {{img src="#" width="0" height="0"}} {{img src="images/hellowin.png" width="427" height="178"}} ---- ☺ If the {{#x|dev}} profile does {{#ci|not}} exist, xulapp will automatically create one for you. 如果 dev 配置文件{{#ci|不存在}}的话,那么 xulapp 会 自动为你创建一个。 ---- {{#cm|# Assuming the Firefox profile dev does NOT exist:}} {{#v|$}} {{#ci|xulapp}} {{#x|debug}} hellowin.xul {{#kw|--profile}} dev Writing file hellowin.xul {{#x|Creating profile dev}} {{#x|Success: created profile 'dev'}} {{#x|Start Firefox temporarily to initialize the profile}} Registering extension hellowin in profile dev Setting configure variables in dev's prefs.js MOZ_NO_REMOTE=1 && firefox -jsconsole -P dev -chrome \\ chrome://helloworld/content/hellowin.xul ---- {{img src="images/hellowin.png" width="427" height="178"}} ---- {{#cm|♡}} It's time to {{#ci|bundle}} and {{#ci|distribute}} our toy! 到了{{#x|打包}}和{{#x|发布}}我们的玩具的时候了! ---- {{#v|$}} {{#ci|xulapp}} {{#x|bundle}} . Writing file hellowin.xul Writing bundle file {{#x|./helloworld.xpi}} {{#v|$}} ---- Our {{#kw|helloworld.xpi}} bundle {{#tag|➥}} + {{#cm|✓}} contains {{#ci|0}} {{#x|Perl}} + {{#cm|✓}} has {{#ci|0}} {{#x|dependencies}} (except Firefox itself) + {{#cm|✓}} runs happily {{#ci|everywhere}} (Win32, Linux, Mac, and etc.) ---- {{#cm|☺}} Put {{#x|helloworld.xpi}} onto an {{#ci|HTTP server}} and make sure the mime-type for .xpi is {{#x|application/x-xpinstall}}. 将 helloworld.xpi 放到一个 HTTP 服务器上, 并确保 .xpi 的 mime 类型为 application/x-xpinstall. ---- On the {{#ci|end user}} side... {{img src="images/install-xpi.png" width="835" height="580"}} ---- On the {{#ci|end user}} side... {{img src="images/chrome-url.png" width="834" height="582"}} ---- {{#kw|☹}} But requiring the {{#ci|end user}} to enter the {{#x|chrome URL}} to access our extension is {{#ci|NOT}} professional. 但是要求最终用户靠输入 {{#ci|chrome URL}} 来访问我们的扩展 是很{{#x|不专业}}的。 ---- {{#kw|☼}} Let's add a {{#x|button}} to Firefox's {{#ci|Tools menu}} for our baby then! 那么就让我们来为我们的东东 向 Firefox 工具菜单添加一个按钮吧! ---- {{#cm|☺}} First of all, create an {{#ci|overlay view}} named {{#x|Overlay}}! 让我们来创建一个 名叫 Overlay 的覆盖视图! ---- {{#v|$}} {{#ci|xulapp}} {{#x|view}} {{#cm|--name}} Overlay {{#cm|--type}} overlay Writing file {{#x|lib/HelloWorld/View/Overlay.pm}} ---- {{#v|$}} {{#ci|wc}} -l {{#t|lib/HelloWorld/View/Overlay.pm}} 27 lib/HelloWorld/View/Overlay.pm {{#v|$}} ---- {{#cm|☺}} Now let's {{#ci|register}} our Overlay {{#x|view}} in our {{#x|HelloWorld::App}} module. 现在让我们在 HelloWorld::App 模块中 {{#ci|注册}}一下我们的 Overlay 视图。 ---- {{#v|$}} {{#ci|vim}} {{#t|lib/HelloWorld/App.pm}} ---- {{#cm|# File lib/HelloWorld/App.pm}} {{#kw|package}} HelloWorld::App; {{#kw|our}} {{#v|$$VERSION}} = '0.01'; {{#kw|use}} XUL::App::Schema; {{#kw|use}} XUL::App schema { {{#cm|# Code that we added by hand:}} xulfile 'hellowin.xul' => generated from 'HelloWorld::View::HelloWin'; xpifile 'helloworld.xpi' => name is 'HelloWorld', id is 'helloworld@agentz.agentz-office', version is '0.0.1', {{#v|...}} ---- {{#cm|# File lib/HelloWorld/App.pm}} {{#kw|package}} HelloWorld::App; {{#kw|our}} {{#v|$$VERSION}} = '0.01'; {{#kw|use}} XUL::App::Schema; {{#kw|use}} XUL::App schema { {{#cm|# Code that we added by hand:}} {{#x|xulfile 'overlay.xul' => }} {{#x|generated from 'HelloWorld::View::Overlay',}} {{#x|overlays 'chrome://browser/content/browser.xul';}} xulfile 'hellowin.xul' => generated from 'HelloWorld::View::HelloWin'; xpifile 'helloworld.xpi' => name is 'HelloWorld', {{#v|...}} ---- {{#cm|☺}} Now let's add some {{#ci|contents}} to our {{#t|Overlay}} {{#x|view}} class. 现在让我们往 Overlay {{#x|视图}}类中 添加一些{{#ci|内容}}。 ---- {{#cm|# File lib/HelloWorld/View/Overlay.pm}} {{#v|...}} {{#kw|package}} HelloWorld::View::Overlay; {{#kw|use base}} 'XUL::App::View::Base'; {{#kw|use}} Template::Declare::Tags 'XUL'; template main => {{#kw|sub}} { show 'header'; {{#cm|# from XUL::App::View::Base}} overlay { attr { id => \"helloworld-overlay\", xmlns => {{#v|$::XUL_NAME_SPACE}}, } {{#cm|# Add your elements here...}} } }; 1; ---- {{#cm|# File lib/HelloWorld/View/Overlay.pm}} {{#v|...}} template main => {{#kw|sub}} { show 'header'; {{#cm|# from XUL::App::View::Base}} overlay { {{#v|...}} {{#cm|# Add your elements here...}} {{#x|menupopup {}} {{#x|attr { id => "menu_ToolsPopup" } }} {{#x|menuitem {}} {{#x|attr {}} {{#x|oncommand => "toOpenWindowByType(}} {{#x|'helloworld',}} {{#x|'chrome://helloworld/content/hellowin.xul')",}} {{#x|insertafter => }} {{#x|"javascriptConsole,devToolsSeparator",}} {{#x|label => "Hello World",}} {{#x|}}} {{#x|}}} {{#x|}}} {{#v|...}} ---- {{#tag|☆}} Job's {{#ci|done}}! Let's {{#x|test}} it! 工作{{#ci|完成}}了!让我们来{{#x|测试}}它! ---- {{#v|$}} {{#ci|xulapp}} {{#x|overlay}} {{#cm|--profile}} dev Writing file {{#x|overlay.xul}} Writing file hellowin.xul Registering extension hellowin in profile dev Setting configure variables in dev's prefs.js firefox -jsconsole -P dev ---- {{img src="images/hw-overlay.gif" width="640" height="466"}} ---- {{#cm|♡}} {{#ci|rebundle}} it! 重新{{#x|打包}}! ---- {{#v|$}} {{#ci|xulapp}} {{#x|bundle}} . Writing file overlay.xul Writing file hellowin.xul Writing bundle file {{#x|./helloworld.xpi}} {{#v|$}} ---- {{#kw|☼}} \"Hey, I want to support {{#ci|multiple languages}}, my users come from {{#x|different}} countries.\" 嘿,我想要支持{{#ci|多国语言}}, 我的用户来自{{#x|不同的}}国家. ---- {{#cm|☺}} {{#t|No problem,}} it's {{#ci|easy}} to implement with {{#x|XUL::App}}! {{#t|没问题,}} 使用 {{#x|XUL::App}} 来实现是{{#ci|很容易的}}! ---- {{#kw|☼}} Let's Add a {{#ci|zh-CN}} {{#x|locale}} to our extension! 让我们为我们的扩展 添加一个简体中文的 locale! ---- {{#cm|Step 1}} Change every occurrence of {{#ci|string literals}} {{#x|"..."}} in our view classes to the form {{#x|_("...")}} {{#cm|第一步}} 将我们的视图类中出现的 每一个字符串字面值 {{#x|"..."}} 都替换为 {{#x|_("...")}} 的形式。 ---- {{#cm|# File lib/HelloWorld/View/HelloWin.pm}} {{#kw|package}} HelloWorld::View::HelloWin; {{#kw|use base}} 'XUL::App::View::Base'; {{#kw|use}} Template::Declare::Tags 'XUL'; template main => {{#kw|sub}} { show 'header'; {{#cm|# from XUL::App::View::Base}} window { attr { id => \"helloworld-hellowin\", xmlns => {{#v|$::XUL_NAME_SPACE}}, title => 'HelloWorld', {{#v|...}} } {{#cm|# Code that we added by hand:}} label { \"Hello, world!\" } } {{#v|...}} ---- {{#cm|# File lib/HelloWorld/View/HelloWin.pm}} {{#kw|package}} HelloWorld::View::HelloWin; {{#kw|use base}} 'XUL::App::View::Base'; {{#kw|use}} Template::Declare::Tags 'XUL'; template main => {{#kw|sub}} { show 'header'; {{#cm|# from XUL::App::View::Base}} window { attr { id => \"helloworld-hellowin\", xmlns => {{#v|$::XUL_NAME_SPACE}}, title => {{#x|_('HelloWorld')}}, {{#v|...}} } {{#cm|# Code that we added by hand:}} label { {{#x|_("Hello, world!")}} } } {{#v|...}} ---- {{#cm|# File lib/HelloWorld/View/Overlay.pm}} {{#v|...}} {{#cm|# Add your elements here...}} menupopup { attr { id => \"menu_ToolsPopup\" } menuitem { attr { oncommand => \"toOpenWindowByType( 'helloworld', 'chrome://helloworld/content/hellowin.xul')\", insertafter => \"javascriptConsole,devToolsSeparator\", label => \"Hello World\", } } } {{#v|...}} ---- {{#cm|# File lib/HelloWorld/View/Overlay.pm}} {{#v|...}} {{#cm|# Add your elements here...}} menupopup { attr { id => \"menu_ToolsPopup\" } menuitem { attr { oncommand => \"toOpenWindowByType( 'helloworld', 'chrome://helloworld/content/hellowin.xul')\", insertafter => \"javascriptConsole,devToolsSeparator\", label => {{#x|_("Hello World")}}, } } } {{#v|...}} ---- {{#cm|Step 2}} Generate the {{#ci|PO file}} for {{#x|zh-cn}}. {{#cm|第二步}} 为我们的 {{#x|zh-cn}} 生成 {{#ci|PO 文件}}. ---- {{#v|$}} {{#ci|xulapp}} {{#x|po}} {{#cm|--lang}} zh-cn Write {{#x|po/zh-cn.po}} ---- {{#v|$}} {{#ci|wc}} -l {{#t|po/zh-cn.po}} {{#x|28}} po/zh-cn.po ---- {{#cm|Step 3}} Edit the {{#ci|PO file}} to do the {{#t|actual}} {{#x|translation}} {{#cm|第三步}} 编辑 {{#ci|PO 文件}}进行 实际的{{#x|翻译}}工作。 ---- {{#v|$}} {{#ci|vim}} po/zh-cn.po ---- {{#v|...}} \"MIME-Version: 1.0\\n\" \"Content-Type: text/plain; charset=CHARSET\\n\" \"Content-Transfer-Encoding: 8bit\\n\" {{#cm|#: lib/HelloWorld/View/Overlay.pm:28}} {{#kw|msgid}} \"Hello World\" {{#kw|msgstr}} \"\" {{#cm|#: lib/HelloWorld/View/HelloWin.pm:23}} {{#kw|msgid}} \"Hello, world!\" {{#kw|msgstr}} \"\" {{#cm|#: lib/HelloWorld/View/HelloWin.pm:17}} {{#kw|msgid}} \"HelloWorld\" {{#kw|msgstr}} \"\" ---- {{#v|...}} \"MIME-Version: 1.0\\n\" \"Content-Type: text/plain; charset={{#x|UTF-8}}\\n\" \"Content-Transfer-Encoding: 8bit\\n\" {{#cm|#: lib/HelloWorld/View/Overlay.pm:28}} {{#kw|msgid}} \"Hello World\" {{#kw|msgstr}} {{#x|"你好 世界"}} {{#cm|#: lib/HelloWorld/View/HelloWin.pm:23}} {{#kw|msgid}} \"Hello, world!\" {{#kw|msgstr}} {{#x|"你好,世界"}} {{#cm|#: lib/HelloWorld/View/HelloWin.pm:17}} {{#kw|msgid}} \"HelloWorld\" {{#kw|msgstr}} {{#x|"你好世界"}} ---- {{#kw|☺}} {{#x|Done}} and {{#x|done}}; let's {{#ci|test}} it! 搞定了,搞定了; 让我们{{#ci|测试}}一下! ---- {{#v|$}} {{#ci|xulapp}} {{#x|overlay}} {{#cm|--profile}} dev {{#cm|--lang}} {{#x|zh-cn}} Writing file {{#x|zh-CN.dtd}} Writing file overlay.xul Writing file hellowin.xul Registering extension hellowin in profile dev Setting configure variables in dev's prefs.js LANG=\"{{#x|zh-CN}}.UTF-8\" LC_CTYPE=\"{{#x|zh-CN}}.UTF-8\" firefox -jsconsole -P dev ---- {{img src="images/hw-zh-cn.gif" width="634" height="474"}} ---- {{#v|♡}} Let's create an {{#x|en-US}} locale as the {{#ci|fall back}}. 让我们创建一下 en-US 作为默认方式。 ---- {{#v|$}} {{#ci|xulapp}} {{#x|po}} {{#cm|--lang}} {{#x|en-us}} Write {{#x|po/en-us.po}} ---- {{#v|...}} \"MIME-Version: 1.0\\n\" \"Content-Type: text/plain; charset={{#x|UTF-8}}\\n\" \"Content-Transfer-Encoding: 8bit\\n\" {{#cm|#: lib/HelloWorld/View/Overlay.pm:28}} {{#kw|msgid}} \"Hello World\" {{#kw|msgstr}} {{#x|""}} {{#cm|#: lib/HelloWorld/View/HelloWin.pm:23}} {{#kw|msgid}} \"Hello, world!\" {{#kw|msgstr}} {{#x|""}} {{#cm|#: lib/HelloWorld/View/HelloWin.pm:17}} {{#kw|msgid}} \"HelloWorld\" {{#kw|msgstr}} {{#x|""}} ---- {{#v|☺}} Strings that're {{#ci|not}} translated are {{#x|kept intact}}. 没有被翻译的字符串会保持原样。 ---- {{#cm|☺}} Let's {{#ci|test}} the {{#x|en-US}} locale now. 现在让我们测试一下 en-US 语言支持。 ---- {{#v|$}} {{#ci|xulapp}} {{#x|overlay}} {{#cm|--profile}} dev {{#cm|--lang}} {{#x|en-us}} Writing file {{#x|en-US.dtd}} Writing file overlay.xul Writing file hellowin.xul Registering extension hellowin in profile dev Setting configure variables in dev's prefs.js LANG=\"{{#x|en-US}}.UTF-8\" LC_CTYPE=\"{{#x|en-US}}.UTF-8\" firefox -jsconsole -P dev ---- {{img src="images/hw-overlay.gif" width="640" height="466"}} ---- ---- {{#cm|☺}} We can add as {{#ci|many}} {{#x|locales}} as we wish ;) 我们想加多少种语言支持, 就可以加多少 ;) ---- What if our {{#x|view}} classes' .pm files {{#ci|change}}? Do we have to {{#ci|redo}} the whole translation? 如果我们{{#ci|改变}}了{{#x|视图类}}的 .pm 文件的话呢? 我们是否必须{{#ci|重做}}所有的翻译? ---- {{#v|$}} xulapp {{#x|po}} {{#ci|Updated}} po/zh-en.po {{#ci|Updated}} po/zh-cn.po ---- {{#cm|☺}} XUL::App will try its best to {{#ci|reuse}} the {{#x|existing}} translation items in the .po files. XUL::App 将会尽最大努力{{#ci|复用}} .po 文件中{{#x|已有的}}翻译条目。 ---- {{#cm|☺}} Now we can {{#ci|rebundle}} our baby and make both {{#x|Chinese}} and {{#x|US}} users happy. 现在我们可以{{#ci|重新打包}}我们的孩子 来让{{#x|中国}}用户和{{#x|美国}}用户同时开心。 ---- {{#v|$}} {{#ci|xulapp}} {{#x|bundle}} . Writing file overlay.xul Writing file hellowin.xul Writing file en-US.dtd Writing file zh-CN.dtd Writing bundle file {{#x|./helloworld.xpi}} {{#v|$}} ---- ♡ ---- ➥ ---- ☺ ----