Language Manager
Project Demo
Report:
Byron Chen
Learning
  • Yii Framework
  • Backbone
  • RequireJS
  • Practice

Before learning...

M V C

M V C
  • 由 Trygve Reenskaug 提出
  • Xerox PARC - Smalltalk (1978)
  • 易於維護、擴充、重複利用
  • 概念適用於各種平台的中、大型專案
“MVC was conceived as a general solution to the problem of users controlling a large and complex data set.”
M V C - Flow
User Control Model View DB DB

M V C 超棒的...

PHP的開發者也來MVC...

2004 年
PHP MVC Framework
大量出現...

2004 年...????

PHP Intro
  • 由 Rasmus Lerdorf 在 1995 年開發的 Personal Home Page
  • 用 C 語言開發的 CGI 取代 Perl 的 Scripting Language
  • 從 C、Perl、Java、C++、Python 得到啟發
  • 1997 年改寫成 PHP 3,PHP: Hypertext Preprocessor
  • PHP 3 加入 Object ≒ value types
  • 2000 年改寫剖析器( Zend 引擎 ),成為 PHP 4
  • 2004 年 PHP 5 : New Object Model -> 物件導向編程語言

<?php
    print_r("Hello World");
?>

#!/usr/bin/env perl
print "Hello, world!\n";
 
“I don’t know how to stop it, there was never any intent to write a programming language ... I have absolutely no idea how to write a programming language, I just kept adding the next logical step on the way.”
PHP MVC
  • Laravel ( 2011 ~ )
  • Phalcon ( 2012 ~ )
  • Symfony ( 2005 ~ )
  • Yii ( 2008 ~ )
  • CodeIgniter ( 2006 ~ )
  • CakePHP ( 2005 ~ )
  • ...
2013 M V C
Yii
  • Control : 使用者操控頁面
  • Model : 連接 DB 與簡化 SQL
  • View : 樣板引擎
  • Library : 方便且多樣的資源庫
Yii - Flow
User
index.php
Control 1 Control 2 Control 3
Model 1 Model 1 Model 2 Model 2 Model 3 Model 3 Model 4 Model 4
View 1 View 1 View 2 View 2 View 3 View 3 View 4 View 4
Control 1 Control 1 Control 1 Control 1 Control 1 Control 1

M V C
讓 Server 端開發
變得簡單又方便

什麼??
Client 端也想用??

Client 端早就不像當年
秀秀畫面這麼簡單

A J A X
  • Asynchronous JavaScript and XML
  • 1996 年 Microsoft Internet Explorer 提出 iFrame 標籤
  • 1999 年 Mozilla、Safari、Opera 開始使用 XMLHttpRequest
  • 2004 年 Google 大量使用標準化的XMLHttpRequest在各服務中
  • 2005 年 Jesse James Garrett 對於這技術,提出 AJAX 名詞概念
    Javascript、Document Object Model、XML 的混合應用
  • 減少傳輸流量、增加單頁功能,網頁朝應用程式發展
“Ajax: A New Approach to Web Applications”
A J A X - Flow
Page 天氣:陰
( 2015 - 01 - 10)
天氣:晴
( 2015 - 01 - 11)
get Data (JS) XML
Set DOM (JS)
C?
M?
V?

A J A X 的流程
似乎很像 MVC

A J A X - M V C
  • AngularJS ( 2009 ~ )
  • Ember.js ( 2011 ~ )
  • JavaScriptMVC ( 2008 ~ )
  • Backbone.js ( 2010 ~ )
  • ...
Backbone.JS - Intro
  • 開發人 Jeremy Ashkenas
  • 輕量級的JS Framework (6.5kb)
  • 基於 underscore.js
  • 耦合性低
Backbone.JS - 功能
  • Model :定義資料
  • Collection :資料表集合
  • View :樣板引擎
  • Router :錨點記錄
Backbone.JS - Flow
User
Main
Get Func
Data
Collect
Set DOM
Backbone App
Router
Main2
#Page/1
root
MVC?
MVC?
MVP
M V P
Model–View–Presenter

Client 端工作越來越重

載入套件越來越多...
管理套件越來越難...
網頁載入越來越慢...

Html原生的限制
困受般的同步循序載入

JS 原生載入問題
  • 從上到下 循序載入
  • 無法看出載入插件之間的相依性
  • 可能載入不需要用到的插件...
  • 浪費等待時間與 Client 端效能

由 JS 接管載入 JS ...

A M D
  • Asynchronous
    平行載入、管理插件關連性
  • Module
    模組化載入的套件、關注點分離降低複雜度
  • Definition
    定義與標準化結構
Require JS - Flow
Page
Require.JS
Config

require.config({
    baseUrl: baseUrl+ "/js/",
    path:{
        'jquery' :'jquery-1.11.2.min',
    },

    shim: {
        'jquery-1.11.2.min':{
           exports: '$'
        },
        'underscore':{
           exports: '_'
        },
        'backbone': {
            deps: ['underscore'],
            exports: 'Backbone'
        }
    }
});
jQuery underscore backbone

require.config({
    baseUrl: baseUrl+ "/js/",
    path:{
        'jquery' :'jquery-1.11.2.min',
    },

    shim: {
        'jquery-1.11.2.min':{
           exports: '$'
        },
        'underscore':{
           exports: '_'
        },
        'backbone': {
            deps: ['underscore'],
            exports: 'Backbone'
        }
    }
});

After Learning...

Do Something

語言檔管理平台
  • 不同平台的語言檔格式皆不相同
  • 統一單一平台編輯
  • 清楚方便的分類管理模式
  • 可輸出與輸入語言檔
國際化 與 在地化
  • Internationalization and Localization
  • i18N、L10N
  • 國際化:軟體可適用於任何地方
  • 在地化:讓軟體可更適合於特定地區使用
  • 讓 語言、文化、書寫習慣 與程式本身拖勾
語言檔 概念
Page
Menu
Selector en-US zh-TW ja-JP
strMainPage_Menu
Menu
選單
メニュー
Menu
選單
en-US
zh-TW
在工程師手上的 code 裡

語言檔掌握在工程師手上

什麼...又英文拼錯了?
(工程師 被打斷)

什麼...上次日文貼錯了?
(工程師 嘆氣)

不是 BUG 的 BUG
真的不該打斷
珍貴的工程師

語言檔 管理
Page Selector en-US zh-TW ja-JP
strMainPage_Menu
Menu
選單
メニュー
Menu
選單
メニュー
Menu
選單
メニュー
en-US
zh-TW
Manager
Menu
選單
メニュー

語言檔就該交由更專業的管理
( 工程師開心了~ )

分析需求

DB 分析
Platform
Classify
String
Content
Lang Member
Log
動作 需求
  • Lang : 新增 修改 刪除
  • Platform : 新增 修改 刪除
  • Classify : 新增 修改 刪除
  • String : 新增 修改 刪除
  • Member : 新增 修改 刪除
  • Generate : 匯入 匯出
Lang 動作
User add Update Lang
Platform 動作
User add Update Platform
Classify 動作
User add Update Classify
select
Platform
String 動作
User add Update String
select select
Platform Classify
Content
Lang
Member 動作
User add Update Member
select
Platform Lang
Generate 匯出動作
User select select
Platform Classify
select
Lang Export
Zip
Generate 匯入動作
User select Get
Platform Lang Pack
Import
解析語言檔 : PO
    • SourceMessages.c
    • en_US
      • messages.po
      • messages.mo
    • zh_TW
      • messages.po
      • messages.mo
    • ....
解析語言檔 : PO
  • 清除換行 保留關鍵訊息 replace("","\"\n\"");
  • 循行正規化檢查特定關鍵字串 msgid、msgstr
  • msgid為空的下一個msgstr中,使用正規劃找 Language: (?P[a-z]{2}[-_][a-zA-Z]{2})
  • 循行正規化檢查,將 msgctxt 當分類,將 msgid 當StringID,將 msgstr 當文字內容,三個變數滿足後存入資料庫

textdomain( "gggolife" );
pgettext("gggolife", "Sign up");
pgettext("gggolife", "About");
pgettext("gggolife", "Privacy");
pgettext("gggolife", "Terms");
pgettext("gggolife", "Feedback");
pgettext("gggolife", "Copyright");
pgettext("gggolife", "GOYOURLIFE INC.");
pgettext("gggolife", "Sign in GoLife Account");
pgettext("gggolife", "Log in GoLife");
pgettext("gggolife", "Email");
pgettext("gggolife", "Password");
pgettext("gggolife", "Login");
pgettext("gggolife", "Login with Facebook");
pgettext("gggolife", "Login with QQ");
pgettext("gggolife", "Login with Weibo");
pgettext("gggolife", "Login with WeChat");
pgettext("gggolife", "Login with social network accounts");
pgettext("gggolife", " is now using GoLife");
pgettext("gggolife", "Stay healthy for those you love");
pgettext("gggolife", "GOLiFE - Smart wearables, healthy life");
pgettext("gggolife", "Keep me logged in");
pgettext("gggolife", "Forget Password?");
pgettext("gggolife", "Invalid account or password");
pgettext("gggolife", "Login with Facebook last time?");
pgettext("gggolife", "You attempt to login several times; Forget password?");
SourceMessages.c

msgid ""
msgstr ""
"Project-Id-Version: anyway2fun\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-12-24 17:04+0800\n"
"PO-Revision-Date: 2014-12-24 17:04+0800\n"
"Last-Translator: \n"
"Language-Team:  dev@anyway2fun.com\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: .\n"
"X-Poedit-SourceCharset: iso-8859-1\n"
"X-Poedit-Bookmarks: 0,-1,-1,-1,-1,-1,-1,-1,-1,-1\n"
"X-Generator: Poedit 1.6.7\n"
"X-Poedit-SearchPath-0: ..\n"

#: ../SourceMessages.c:2
msgctxt "gggolife"
msgid "Sign up"
msgstr "注册"

#: ../SourceMessages.c:3
msgctxt "gggolife"
msgid "About"
msgstr "关于 GOLiFE"

...
...
...
...

#: ../SourceMessages.c:66
msgctxt "gggolife"
msgid ""
"Verification succeeds.
You are ready to use all GOLiFE services now! " "Enjoy and have fun.

The page will be redirected after 3 seconds." msgstr "" "认证成功。
你现在可以使用所有 GOLiFE 服务,快开始吧!

此页面" "在 3 秒后会自动转址。"
Messages.po

msgid ""
msgstr ""
"Project-Id-Version: anyway2fun\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-12-24 17:04+0800\n"
"PO-Revision-Date: 2014-12-24 17:04+0800\n"
"Last-Translator: \n"
"Language-Team:  dev@anyway2fun.com\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: .\n"
"X-Poedit-SourceCharset: iso-8859-1\n"
"X-Poedit-Bookmarks: 0,-1,-1,-1,-1,-1,-1,-1,-1,-1\n"
"X-Generator: Poedit 1.6.7\n"
"X-Poedit-SearchPath-0: ..\n"

#: ../SourceMessages.c:2
msgctxt "gggolife"
msgid "Sign up"
msgstr "注册"

#: ../SourceMessages.c:3
msgctxt "gggolife"
msgid "About"
msgstr "关于 GOLiFE"

...
...
...
...

#: ../SourceMessages.c:66
msgctxt "gggolife"
msgid ""
"Verification succeeds.
You are ready to use all GOLiFE services now! " "Enjoy and have fun.

The page will be redirected after 3 seconds." msgstr "" "认证成功。
你现在可以使用所有 GOLiFE 服务,快开始吧!

此页面" "在 3 秒后会自动转址。"
Messages.po

msgid ""
msgstr "Project-Id-Version: anyway2fun\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2014-12-24 17:04+0800\nPO-Revision-Date: 2014-12-24 17:04+0800\nLast-Translator: \nLanguage-Team:  dev@anyway2fun.com\nLanguage: zh_CN\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nX-Poedit-KeywordsList: _;gettext;gettext_noop\nX-Poedit-Basepath: .\nX-Poedit-SourceCharset: iso-8859-1\nX-Poedit-Bookmarks: 0,-1,-1,-1,-1,-1,-1,-1,-1,-1\nX-Generator: Poedit 1.6.7\nX-Poedit-SearchPath-0: ..\n"

#: ../SourceMessages.c:2
msgctxt "gggolife"
msgid "Sign up"
msgstr "注册"

#: ../SourceMessages.c:3
msgctxt "gggolife"
msgid "About"
msgstr "关于 GOLiFE"

...
...
...
...

#: ../SourceMessages.c:66
msgctxt "gggolife"
msgid "Verification succeeds.
You are ready to use all GOLiFE services now! Enjoy and have fun.

The page will be redirected after 3 seconds." msgstr "认证成功。
你现在可以使用所有 GOLiFE 服务,快开始吧!

此页面在 3 秒后会自动转址。"
Messages.po

msgid ""
msgstr "Project-Id-Version: anyway2fun\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2014-12-24 17:04+0800\nPO-Revision-Date: 2014-12-24 17:04+0800\nLast-Translator: \nLanguage-Team:  dev@anyway2fun.com\nLanguage: zh_CN\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nX-Poedit-KeywordsList: _;gettext;gettext_noop\nX-Poedit-Basepath: .\nX-Poedit-SourceCharset: iso-8859-1\nX-Poedit-Bookmarks: 0,-1,-1,-1,-1,-1,-1,-1,-1,-1\nX-Generator: Poedit 1.6.7\nX-Poedit-SearchPath-0: ..\n"

#: ../SourceMessages.c:2
msgctxt "gggolife"
msgid "Sign up"
msgstr "注册"

#: ../SourceMessages.c:3
msgctxt "gggolife"
msgid "About"
msgstr "关于 GOLiFE"

...
...
...
...

#: ../SourceMessages.c:66
msgctxt "gggolife"
msgid "Verification succeeds.
You are ready to use all GOLiFE services now! Enjoy and have fun.

The page will be redirected after 3 seconds." msgstr "认证成功。
你现在可以使用所有 GOLiFE 服务,快开始吧!

此页面在 3 秒后会自动转址。"
Messages.po

msgid ""
msgstr "Project-Id-Version: anyway2fun\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2014-12-24 17:04+0800\nPO-Revision-Date: 2014-12-24 17:04+0800\nLast-Translator: \nLanguage-Team:  dev@anyway2fun.com\nLanguage: zh_CN\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nX-Poedit-KeywordsList: _;gettext;gettext_noop\nX-Poedit-Basepath: .\nX-Poedit-SourceCharset: iso-8859-1\nX-Poedit-Bookmarks: 0,-1,-1,-1,-1,-1,-1,-1,-1,-1\nX-Generator: Poedit 1.6.7\nX-Poedit-SearchPath-0: ..\n"

#: ../SourceMessages.c:2
msgctxt "gggolife"
msgid "Sign up"
msgstr "注册"

#: ../SourceMessages.c:3
msgctxt "gggolife"
msgid "About"
msgstr "关于 GOLiFE"

...
...
...
...

#: ../SourceMessages.c:66
msgctxt "gggolife"
msgid "Verification succeeds.
You are ready to use all GOLiFE services now! Enjoy and have fun.

The page will be redirected after 3 seconds." msgstr "认证成功。
你现在可以使用所有 GOLiFE 服务,快开始吧!

此页面在 3 秒后会自动转址。"
Messages.po
解析語言檔 : PO
  • 無需 SourceMessages.c 即可做到輸入PO
  • 可從 PO 中得到 語系、分類、StringID、內容
  • 輸出時一定要產生 SourceMessages.c,這是轉 mo 檔的必要檔案
解析語言檔 : JS
    • message.js
    • friends.js
    • zh-tw
      • message.js
      • friends.js
    • zh-cn
      • message.js
      • friends.js
    • ...
解析語言檔 : JS
  • 檢查第一筆資訊 Key 是否為 root, 如果是 取得root內的值當做即將處理的字串資訊,如果不是直接取出資料處理
  • 清除不必要換行
  • 正規化取出StringID、內容,針對內容型態做不同的儲存方式

// default locale 'root': this is for en_us
define({
    'root': {
        'profileNameLengthError': 'Please set your name between ' + GOOSE_MIN_LEN_FULLNAME + ' and ' + GOOSE_MAX_LEN_FULLNAME + ' in length of characters',
        'profileTagLengthError': 'Please set your tag information between ' + PROFILE_MIN_LEN_TAG + ' and ' + PROFILE_MAX_LEN_TAG + ' in length of characters',
        'profileAddrLengthError': 'Please set your address between ' + PROFILE_MIN_LEN_ADDRESS + ' and ' + PROFILE_MAX_LEN_ADDRESS + ' in length of characters',
        'profileSaveSuccess': 'Profile saved.',
        'profileSaveError': 'Failed to save. Please try again.',
        'passwordLengthError': 'Please set your new password between ' + GOOSE_MIN_LEN_PASSWORD + ' and ' + GOOSE_MAX_LEN_PASSWORD + ' in length of characters',
        'passwordConfirmError': 'Your new password is different',
        'passwordUnauthorizedError': 'Your old password is wrong',
        'passwordSaveSuccess': 'Password updated',
        'resendVerificationMailDone': function (email) {
                return 'Verification email has been sent to '+ email +'. If you request to resend several times, please make sure that you get the latest one and click the link in it.'
            },
        'unknownError': 'Unknown error, please try again',
        'settingsSaved': 'Settings saved',
        'confirm': 'Confirm',
        'addFriend': 'Add as friend',
        'removeFriend': 'Remove friend',
        'ignoreFriend': 'Reject',
        'friendshipWaitingForSubject': 'Invitation sent',
        'loginRequired': 'Please log in',
        'addFriendLoginRequired': 'Please log in before adding friend.',
        'Login': 'Login',
        'uploadYourProfilePicture': 'Upload Your Profile Picture',
        'selectYourOwnProfilePicture': 'Select your own profile picture',
        'upload': 'Upload',
        'save': 'Save',
        'back': 'Back',
        'avatarSupportFileType': 'Avatar only support the file type of jpg, png and gif'
    },
    'zh-tw': true,
    'zh-cn': true,
    'ja-jp': true,
});
                                    
message.js

define({
    'profileNameLengthError': '全名欄位必須介於 ' + GOOSE_MIN_LEN_FULLNAME + ' 到 ' + GOOSE_MAX_LEN_FULLNAME + ' 字元之間',
    'profileTagLengthError': '標籤欄位必須介於 ' + PROFILE_MIN_LEN_TAG + ' 到 ' + PROFILE_MAX_LEN_TAG + ' 字元之間',
    'profileAddrLengthError': '地址欄位必須介於 ' + PROFILE_MIN_LEN_ADDRESS + ' 到 ' + PROFILE_MAX_LEN_ADDRESS + ' 字元之間',
    'profileSaveSuccess': '個人資料儲存成功',
    'profileSaveError': '儲存失敗,請再試一次',
    'passwordLengthError': '密碼必須介於 ' + GOOSE_MIN_LEN_PASSWORD + ' 到 ' + GOOSE_MAX_LEN_PASSWORD + ' 字元之間',
    'passwordConfirmError': '請確認輸入的新密碼正確',
    'passwordUnauthorizedError': '舊密碼錯誤,請輸入正確的密碼',
    'passwordSaveSuccess': '密碼更新完成',
    'resendVerificationMailDone': function (email) {
        return '認證信已發送至 '+ email +'。如果你重發認證信好幾次,請確定自己已經收到最新一封並點擊其中的連結。'
    },
    'unknownError': '未知錯誤,請再試一次',
    'settingsSaved': '設定儲存成功',
    'confirm': '確認',
    'addFriend': '加為好友',
    'removeFriend': '移除好友',
    'ignoreFriend': '拒絕',
    'friendshipWaitingForSubject': '交友邀請已送出',
    'loginRequired': '請登入系統',
    'addFriendLoginRequired': '要加好友請先登入',
    'Login': '登入',
    'uploadYourProfilePicture': '更新你的大頭貼',
    'selectYourOwnProfilePicture': '選擇你的大頭貼檔案',
    'upload': '上傳',
    'save': '儲存',
    'back': '上一步',
    'avatarSupportFileType': '頭像只支援 jpg, png 和 gif 的格式'
});
                                    
zh-tw/message.js

define({
    'profileNameLengthError': '全名欄位必須介於 ' + GOOSE_MIN_LEN_FULLNAME + ' 到 ' + GOOSE_MAX_LEN_FULLNAME + ' 字元之間',
    'profileTagLengthError': '標籤欄位必須介於 ' + PROFILE_MIN_LEN_TAG + ' 到 ' + PROFILE_MAX_LEN_TAG + ' 字元之間',
    'profileAddrLengthError': '地址欄位必須介於 ' + PROFILE_MIN_LEN_ADDRESS + ' 到 ' + PROFILE_MAX_LEN_ADDRESS + ' 字元之間',
    'profileSaveSuccess': '個人資料儲存成功',
    'profileSaveError': '儲存失敗,請再試一次',
    'passwordLengthError': '密碼必須介於 ' + GOOSE_MIN_LEN_PASSWORD + ' 到 ' + GOOSE_MAX_LEN_PASSWORD + ' 字元之間',
    'passwordConfirmError': '請確認輸入的新密碼正確',
    'passwordUnauthorizedError': '舊密碼錯誤,請輸入正確的密碼',
    'passwordSaveSuccess': '密碼更新完成',
    'resendVerificationMailDone': function (email) {
        return '認證信已發送至 '+ email +'。如果你重發認證信好幾次,請確定自己已經收到最新一封並點擊其中的連結。'
    },
    'unknownError': '未知錯誤,請再試一次',
    'settingsSaved': '設定儲存成功',
    'confirm': '確認',
    'addFriend': '加為好友',
    'removeFriend': '移除好友',
    'ignoreFriend': '拒絕',
    'friendshipWaitingForSubject': '交友邀請已送出',
    'loginRequired': '請登入系統',
    'addFriendLoginRequired': '要加好友請先登入',
    'Login': '登入',
    'uploadYourProfilePicture': '更新你的大頭貼',
    'selectYourOwnProfilePicture': '選擇你的大頭貼檔案',
    'upload': '上傳',
    'save': '儲存',
    'back': '上一步',
    'avatarSupportFileType': '頭像只支援 jpg, png 和 gif 的格式'
});
                                    
zh-tw/message.js

define({
    'profileNameLengthError': '全名欄位必須介於 ' + GOOSE_MIN_LEN_FULLNAME + ' 到 ' + GOOSE_MAX_LEN_FULLNAME + ' 字元之間',
    'profileTagLengthError': '標籤欄位必須介於 ' + PROFILE_MIN_LEN_TAG + ' 到 ' + PROFILE_MAX_LEN_TAG + ' 字元之間',
    'profileAddrLengthError': '地址欄位必須介於 ' + PROFILE_MIN_LEN_ADDRESS + ' 到 ' + PROFILE_MAX_LEN_ADDRESS + ' 字元之間',
    'profileSaveSuccess': '個人資料儲存成功',
    'profileSaveError': '儲存失敗,請再試一次',
    'passwordLengthError': '密碼必須介於 ' + GOOSE_MIN_LEN_PASSWORD + ' 到 ' + GOOSE_MAX_LEN_PASSWORD + ' 字元之間',
    'passwordConfirmError': '請確認輸入的新密碼正確',
    'passwordUnauthorizedError': '舊密碼錯誤,請輸入正確的密碼',
    'passwordSaveSuccess': '密碼更新完成',
    'resendVerificationMailDone': function (email) {return '認證信已發送至 '+ email +'。如果你重發認證信好幾次,請確定自己已經收到最新一封並點擊其中的連結。'},
    'unknownError': '未知錯誤,請再試一次',
    'settingsSaved': '設定儲存成功',
    'confirm': '確認',
    'addFriend': '加為好友',
    'removeFriend': '移除好友',
    'ignoreFriend': '拒絕',
    'friendshipWaitingForSubject': '交友邀請已送出',
    'loginRequired': '請登入系統',
    'addFriendLoginRequired': '要加好友請先登入',
    'Login': '登入',
    'uploadYourProfilePicture': '更新你的大頭貼',
    'selectYourOwnProfilePicture': '選擇你的大頭貼檔案',
    'upload': '上傳',
    'save': '儲存',
    'back': '上一步',
    'avatarSupportFileType': '頭像只支援 jpg, png 和 gif 的格式'
});
                                    
zh-tw/message.js
解析語言檔 : JS
  • 有兩種不同的結構檔案
  • 可從 JS 中得到 StringID、內容,從上傳的檔名得到 分類,無法取得 語系
  • 輸出時要有一個基礎語言當做root檔,通常為英文
解析語言檔 結論
  • 語言檔必須未編譯過的文字檔
  • 無法製作通用型的 解析器 與 輸出器
  • 無法從語言檔中得到完整資訊
  • 定義基礎語言為英文
  • 檢查衝突
Generate 匯入動作
User select Get
Platform Lang Pack
Import
Parser
Server Parser
Edit
Improve
String Content
Generate 匯入介面流程
Import
StringMainPage_Menu : en-us : Menu
StringMainPage_Login : en-us : Login
StringMainPage_Login : en-us : Logout
StringMainPage_Login : en-us : Log in
StringMainPage_Login : en-us : Log in
StringMainPage_Login : en-us : Logout
StringMainPage_Login : en-us : Log in
Parser and Check
Check
Save

輸出輸入流程完成

等等...剛剛誰改了我東西...

誰亂加平台跟分類啦...

哪個人把英文改的
這麼語意不通順

大家都可以編輯修改
好像這樣有點糟糕

權限 機制
  • 需要有彈性的賦予使用者不同的權限
  • 根據使用者去限制動作 跟 取得到的東西
  • 隨時變更、隨時生效
權限 : 角色
  • 管理員 Admin
    String, Classify, Language, Platform, Generate, Member, Setting
  • 員工 Staff
    String, Classify, Generate, Setting
  • 翻譯 Translator
    String, Setting
  • 鎖定 Lock
    none
權限 : 存取
  • 賦予單一使用者不同的存取權
  • 減少使用者看到雜亂不需要的資訊
  • 保護資料避免不相干的人錯誤修改
  • 限制 Platform 存取
  • 限制 Language 存取
會員與角色
  • Role-based access control ( RBAC )
  • Yii : WebUser, AcceessControl
會員與角色 - Flow
User Web User
get Role
Role Permission
Control (Page)
Access Rules Action Method Action Error
Action Method Action Error
Control

    public function accessRules(){
            return array(
                array('allow',  // allow all users to perform 'index' and 'view' actions
                    'actions'=>array('Index','Export','ExportToFile','GetClassify','Import','ParserFile','CheckStingInfo','ImportStingInfo'),
                    'roles'=>array( Yii::app()->params["Role"]['admin'],
                                    Yii::app()->params["Role"]['staff']  ),
                ),

                array('allow',  // allow all users to perform 'index' and 'view' actions
                    'actions'=>array('Error',),
                    'users'=>array('*'),
                ),

                array('deny',  // deny all users
                    'users'=>array('*'),
                ),
            );
        }
                                        
Menu List

L i v e
D e m o
T i m e

Q & A