mirror of
https://gitee.com/layui/layui.git
synced 2025-10-07 23:24:32 +08:00
feat(i18n): 国际化支持 (#2698)
Some checks failed
Issue Close Require / issue-close-require (push) Has been cancelled
Some checks failed
Issue Close Require / issue-close-require (push) Has been cancelled
* wip(i18n): laydate 国际化 * wip(i18n): colorpicker 国际化 * wip(i18n): laypage 国际化 * fix * update code * wip(i18n): 修改国际化消息对象结构 * wip(i18n): update * wip(i18n): code 国际化 * wip(i18n): dropdown 国际化 * wip(i18n): flow 国际化 * wip(i18n): form 国际化 * wip(i18n): layer 国际化 * wip(i18n): table 国际化 * wip(i18n): transfer 国际化 * wip(i18n): tree 国际化 * wip(i18n): treeTable 控制台提示统一为英文 * wip(i18n): upload 国际化 * wip(i18n): util 国际化 * wip(i18n): update code * wip(i18n): update code * wip(i18n): fix * wip(i18n): 优化 $t 代码细节 * wip(i18n): 修复 laydate lang * wip(i18n): 改进 upload i18n key * wip(i18n): 修复打包后 laydate 和 layer 异常 * wip(i18n): 移除国际化消息中的 `lay` 命名空间 * refactor(i18n): 改进国际化支持 * wip(i18n): 修复 table text.none 切换 locale 无效问题 * style(laydate): 优化逗号格式 * chore(laydate): 优化部分提示 * chore(i18n): 优化演示中部分国际化消息 * refactor: 剔除 laydate 单独版的判断逻辑 为接下来全面支持国际化做铺垫 * wip(i18n): 为 laydate 重新添加完整国际化的支持 * i18n(laydate): 优化 lang() 方法中的逻辑 * chore(util): 删除未使用的代码 * chore(i18n): 优化注释 * docs(i18n): 新增国际化文档(beta) note: 由于时间关系,本次提交仅为初版,该文档尚未完成 * wip(docs): 完善 i18n 文档 * fix(i18n): 修复 laypage 变量定义前使用 * wip(i18n): 转义翻译结果 * fix(i18n): 修复 table 排序 key 无效 * wip(i18n): 优化获取对象中指定路径值的性能 * wip(i18n): 删除 $t 可变长参数重载 * chore(i18n): 删除不必要的注释 * refactor(i18n): laydate 国际化方案迁移至 i18n.$t (#2745) * wip(i18n): 改进 laydate i18nMsg key * update code * wip(i18n): 改进 laydate 面板中的日期格式处理 * wip(i18n): 改进 util.toDateString meridiem 遵循 CLDR day periods 标准 * update code * wip(docs): 优化 i18n 文档示例 * docs(i18n): 优化正式文档 * docs(i18n): 优化部分文案 * docs(i18n): 优化示例 --------- Co-authored-by: 贤心 <3277200+sentsim@users.noreply.github.com>
This commit is contained in:
786
docs/i18n/detail/demo.md
Normal file
786
docs/i18n/detail/demo.md
Normal file
@@ -0,0 +1,786 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>i18n 演示 - Layui</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="{{= d.layui[2].cdn.css }}" rel="stylesheet">
|
||||
</head>
|
||||
<body class="layui-padding-3">
|
||||
<div id="root"></div>{{!
|
||||
<template id="template">
|
||||
{{ const i18n = layui.i18n; }}
|
||||
<div class="layui-form">
|
||||
<div class="layui-inline">
|
||||
<strong>{{= i18n.$t('custom.switchLanguage') }}: </strong>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<select id="change-locale" lay-filter="change-locale">
|
||||
<option value="zh-CN">简体中文</option>
|
||||
<option value="en">English</option>
|
||||
<option value="zh-HK">繁體中文</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>README</legend>
|
||||
<div class="layui-field-box layui-text" id="tpl-test">
|
||||
<p>{{= i18n.$t('custom.readme.description') }}</p>
|
||||
<ul>
|
||||
<li><strong>locale</strong>: <span style="color:red">{{= i18n.config.locale }}</span></li>
|
||||
<li><strong>Date</strong>: {{= new Date().toLocaleDateString(i18n.config.locale) }}</li>
|
||||
<li><strong>Hello</strong>: {{= i18n.$t('custom.readme.hello') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>code</legend>
|
||||
<div class="layui-field-box">
|
||||
<pre id="demo-code" class="layui-code" lay-options="{}">
|
||||
code content
|
||||
</pre>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>colorpicker</legend>
|
||||
<div class="layui-field-box">
|
||||
<div id="demo-colorpicker"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>dropdown</legend>
|
||||
<div class="layui-field-box">
|
||||
<button id="demo-dropdown" class="layui-btn demo-dropdown-base">
|
||||
<span>Dropdown</span>
|
||||
<i class="layui-icon layui-icon-down layui-font-12"></i>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>flow</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="flow-demo" id="demo-flow"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>form</legend>
|
||||
<div class="layui-field-box">
|
||||
<form class="layui-form" action="">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">{{= i18n.$t('custom.form.required') }}</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="username" lay-verify="required" lay-vertype="alert" placeholder="{{= i18n.$t('custom.form.placeholder') }}" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">{{= i18n.$t('custom.form.phone') }}</label>
|
||||
<div class="layui-input-inline layui-input-wrap">
|
||||
<input type="tel" name="phone" lay-verify="phone" autocomplete="off" value="123456" lay-affix="clear"
|
||||
class="layui-input demo-phone">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">{{= i18n.$t('custom.form.email') }}</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="email" value="123.com" lay-verify="email" autocomplete="off"
|
||||
class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">{{= i18n.$t('custom.form.date') }}</label>
|
||||
<div class="layui-input-inline layui-input-wrap">
|
||||
<div class="layui-input-prefix">
|
||||
<i class="layui-icon layui-icon-date"></i>
|
||||
</div>
|
||||
<input type="text" name="date" value="2077" id="date" lay-verify="date" placeholder="yyyy-MM-dd"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">{{= i18n.$t('custom.form.select') }}</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="interest" lay-filter="aihao" lay-search>
|
||||
<option value=""></option>
|
||||
<option value="0">AAA</option>
|
||||
<option value="1" selected>BBB</option>
|
||||
<option value="2">CCC</option>
|
||||
<option value="3">DDD</option>
|
||||
<option value="4">EEE</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button type="submit" class="layui-btn" lay-submit lay-filter="demo1">
|
||||
{{= i18n.$t('custom.form.submit') }}
|
||||
</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">
|
||||
{{= i18n.$t('custom.form.reset') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>laydate</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="layui-inline">
|
||||
<input class="layui-input" id="demo-laydate" />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>layer</legend>
|
||||
<div class="layui-field-box">
|
||||
<button type="button" class="layui-btn layui-btn-primary" lay-on="alert">Alert</button>
|
||||
<button type="button" class="layui-btn layui-btn-primary" lay-on="prompt">Prompt</button>
|
||||
<button type="button" class="layui-btn layui-btn-primary" lay-on="photos">Photos</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>laypage</legend>
|
||||
<div class="layui-field-box">
|
||||
<div id="demo-laypage-all"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>table</legend>
|
||||
<div class="layui-field-box">
|
||||
<table class="layui-hide" id="demo-table" lay-filter="test"></table>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>transfer</legend>
|
||||
<div class="layui-field-box">
|
||||
<div id="demo-transfer"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>tree</legend>
|
||||
<div class="layui-field-box">
|
||||
<div id="demo-tree"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>upload</legend>
|
||||
<div class="layui-field-box">
|
||||
<button type="button" class="layui-btn" id="demo-upload">
|
||||
<i class="layui-icon layui-icon-upload"></i> Upload
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>utils</legend>
|
||||
<div class="layui-field-box">
|
||||
<label>
|
||||
timeAgo: <input id="demo-time-ago-picker" type="datetime-local" /> <span id="demo-time-ago-display"></span>
|
||||
</label>
|
||||
<br>
|
||||
<label>
|
||||
toDateString: <div id="demo-toDateString"></div>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>!}}
|
||||
|
||||
<script>
|
||||
// 配置 Layui 组件语言包
|
||||
window.LAYUI_GLOBAL = {
|
||||
i18n: {
|
||||
locale: localStorage.getItem('layui-i18n-local-test') || 'zh-CN', // 当前语言环境
|
||||
messages: { // 扩展其他语言包
|
||||
// English
|
||||
'en': {
|
||||
code: {
|
||||
copy: 'Copy Code',
|
||||
copied: 'Copied',
|
||||
copyError: 'Copy Failed',
|
||||
maximize: 'Maximize',
|
||||
restore: 'Restore',
|
||||
preview: 'Open Preview in New Window'
|
||||
},
|
||||
colorpicker: {
|
||||
clear: 'Clear',
|
||||
confirm: 'OK'
|
||||
},
|
||||
dropdown: {
|
||||
noData: 'No Data'
|
||||
},
|
||||
flow: {
|
||||
loadMore: 'Load More',
|
||||
noMore: 'No More Data'
|
||||
},
|
||||
form: {
|
||||
select: {
|
||||
noData: 'No Data',
|
||||
noMatch: 'No Matching Data',
|
||||
placeholder: 'Please Select'
|
||||
},
|
||||
validateMessages: {
|
||||
required: 'This field is required',
|
||||
phone: 'Invalid phone number format',
|
||||
email: 'Invalid email format',
|
||||
url: 'Invalid URL format',
|
||||
number: 'Numbers only',
|
||||
date: 'Invalid date format',
|
||||
identity: 'Invalid ID number format'
|
||||
},
|
||||
verifyErrorPromptTitle: 'Notice'
|
||||
},
|
||||
laydate: {
|
||||
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
weeks: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
|
||||
time: ['Hour', 'Minute', 'Second'],
|
||||
literal: {
|
||||
year: ''
|
||||
},
|
||||
selectDate: 'Select Date',
|
||||
selectTime: 'Select Time',
|
||||
startTime: 'Start Time',
|
||||
endTime: 'End Time',
|
||||
tools: {
|
||||
confirm: 'Confirm',
|
||||
clear: 'Clear',
|
||||
now: 'Now',
|
||||
reset: 'Reset'
|
||||
},
|
||||
rangeOrderPrompt: 'End time cannot be less than start Time\nPlease re-select',
|
||||
invalidDatePrompt: 'Invalid date\n',
|
||||
formatErrorPrompt: 'Date format is invalid\nMust follow the format:\n{format}\n',
|
||||
autoResetPrompt: 'It has been reset',
|
||||
preview: 'The selected result'
|
||||
},
|
||||
layer: {
|
||||
confirm: 'OK',
|
||||
cancel: 'Cancel',
|
||||
defaultTitle: 'Info',
|
||||
prompt: {
|
||||
InputLengthPrompt: 'Maximum {length} characters'
|
||||
},
|
||||
photos: {
|
||||
noData: 'No Image',
|
||||
tools: {
|
||||
rotate: 'Rotate',
|
||||
scaleX: 'Flip Horizontally',
|
||||
zoomIn: 'Zoom In',
|
||||
zoomOut: 'Zoom Out',
|
||||
reset: 'Reset',
|
||||
close: 'Close'
|
||||
},
|
||||
viewPicture: 'View Picture',
|
||||
urlError: {
|
||||
prompt: 'Image URL is invalid, \nContinue to next one?',
|
||||
confirm: 'Next',
|
||||
cancel: 'Cancel'
|
||||
}
|
||||
}
|
||||
},
|
||||
laypage: {
|
||||
prev: 'Prev',
|
||||
next: 'Next',
|
||||
first: 'First',
|
||||
last: 'Last',
|
||||
total: 'Total {total} items',
|
||||
pagesize: '/page',
|
||||
goto: 'Go to',
|
||||
page: 'page',
|
||||
confirm: 'Confirm'
|
||||
},
|
||||
table: {
|
||||
sort: {
|
||||
asc: 'Ascending',
|
||||
desc: 'Descending'
|
||||
},
|
||||
noData: 'No Data',
|
||||
tools: {
|
||||
filter: {
|
||||
title: 'Filter Columns'
|
||||
},
|
||||
export: {
|
||||
title: 'Export',
|
||||
noDataPrompt: 'No data in the table',
|
||||
compatPrompt: 'Export is not supported in IE. Please use Chrome or another modern browser.',
|
||||
csvText: 'Export CSV File'
|
||||
},
|
||||
print: {
|
||||
title: 'Print',
|
||||
noDataPrompt: 'No data in the table'
|
||||
}
|
||||
},
|
||||
dataFormatError: 'Returned data is invalid. The correct success status code should be: "{statusName}": {statusCode}',
|
||||
xhrError: 'Request Error: {msg}'
|
||||
},
|
||||
transfer: {
|
||||
noData: 'No Data',
|
||||
noMatch: 'No Match',
|
||||
title: ['List 1', 'List 2'],
|
||||
searchPlaceholder: 'Search by Keyword'
|
||||
},
|
||||
tree: {
|
||||
defaultNodeName: 'Unnamed',
|
||||
noData: 'No Data',
|
||||
deleteNodePrompt: 'Are you sure you want to delete the node "{name}"?'
|
||||
},
|
||||
upload: {
|
||||
fileType: {
|
||||
file: 'File',
|
||||
image: 'Image',
|
||||
video: 'Video',
|
||||
audio: 'Audio'
|
||||
},
|
||||
validateMessages: {
|
||||
fileExtensionError: 'Unsupported format in selected {fileType}',
|
||||
filesOverLengthLimit: 'Maximum {length} files allowed at once',
|
||||
currentFilesLength: 'You have selected {length} files',
|
||||
fileOverSizeLimit: 'File size must not exceed {size}'
|
||||
},
|
||||
chooseText: '{length} files'
|
||||
},
|
||||
util: {
|
||||
timeAgo: {
|
||||
days: '{days} days ago',
|
||||
hours: '{hours} hours ago',
|
||||
minutes: '{minutes} minutes ago',
|
||||
future: 'In the future',
|
||||
justNow: 'Just now'
|
||||
},
|
||||
toDateString: {
|
||||
meridiem: function (hours, minutes) {
|
||||
return hours < 12 ? 'AM' : 'PM';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 繁體中文
|
||||
'zh-HK': {
|
||||
code: {
|
||||
copy: '複製代碼',
|
||||
copied: '已複製',
|
||||
copyError: '複製失敗',
|
||||
maximize: '最大化顯示',
|
||||
restore: '還原顯示',
|
||||
preview: '在新視窗預覽'
|
||||
},
|
||||
colorpicker: {
|
||||
clear: '清除',
|
||||
confirm: '確定'
|
||||
},
|
||||
dropdown: {
|
||||
noData: '暫無資料'
|
||||
},
|
||||
flow: {
|
||||
loadMore: '載入更多',
|
||||
noMore: '沒有更多了'
|
||||
},
|
||||
form: {
|
||||
select: {
|
||||
noData: '暫無資料',
|
||||
noMatch: '無匹配資料',
|
||||
placeholder: '請選擇'
|
||||
},
|
||||
validateMessages: {
|
||||
required: '必填項不能為空',
|
||||
phone: '手機號碼格式不正確',
|
||||
email: '電郵格式不正確',
|
||||
url: '連結格式不正確',
|
||||
number: '只能填寫數字',
|
||||
date: '日期格式不正確',
|
||||
identity: '身份證號碼格式不正確'
|
||||
},
|
||||
verifyErrorPromptTitle: '提示'
|
||||
},
|
||||
laydate: {
|
||||
months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
weeks: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
time: ['時', '分', '秒'],
|
||||
literal: {
|
||||
year: '年'
|
||||
},
|
||||
selectDate: '選擇日期',
|
||||
selectTime: '選擇時間',
|
||||
startTime: '開始時間',
|
||||
endTime: '結束時間',
|
||||
tools: {
|
||||
confirm: '確定',
|
||||
clear: '清除',
|
||||
now: '現在',
|
||||
reset: '重設'
|
||||
},
|
||||
rangeOrderPrompt: '結束時間不能早於開始時間\n請重新選擇',
|
||||
invalidDatePrompt: '不在有效日期或時間範圍內\n',
|
||||
formatErrorPrompt: '日期格式不合法\n必須遵循:\n{format}\n',
|
||||
autoResetPrompt: '已自動重設',
|
||||
preview: '當前選中的結果'
|
||||
},
|
||||
layer: {
|
||||
confirm: '確定',
|
||||
cancel: '取消',
|
||||
defaultTitle: '資訊',
|
||||
prompt: {
|
||||
InputLengthPrompt: '最多輸入 {length} 個字符'
|
||||
},
|
||||
photos: {
|
||||
noData: '沒有圖片',
|
||||
tools:{
|
||||
rotate: '旋轉',
|
||||
scaleX: '水平變換',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '縮小',
|
||||
reset: '還原',
|
||||
close: '關閉'
|
||||
},
|
||||
viewPicture: '查看原圖',
|
||||
urlError: {
|
||||
prompt: '當前圖片地址異常,\n是否繼續查看下一張?',
|
||||
confirm: '下一張',
|
||||
cancel: '不看了'
|
||||
}
|
||||
}
|
||||
},
|
||||
laypage: {
|
||||
prev: '上一頁',
|
||||
next: '下一頁',
|
||||
first: '首頁',
|
||||
last: '尾頁',
|
||||
total: '共 {total} 條',
|
||||
pagesize: '條/頁',
|
||||
goto: '到第',
|
||||
page: '頁',
|
||||
confirm: '確定'
|
||||
},
|
||||
table: {
|
||||
sort: {
|
||||
asc: '升序',
|
||||
desc: '降序'
|
||||
},
|
||||
noData: '無資料',
|
||||
tools:{
|
||||
filter: {
|
||||
title: '篩選列'
|
||||
},
|
||||
export: {
|
||||
title: '匯出',
|
||||
noDataPrompt: '當前表格無資料',
|
||||
compatPrompt: '匯出功能不支援 IE,請用 Chrome 等高級瀏覽器匯出',
|
||||
csvText : '匯出 CSV 檔案'
|
||||
},
|
||||
print: {
|
||||
title: '列印',
|
||||
noDataPrompt: '當前表格無資料'
|
||||
}
|
||||
},
|
||||
dataFormatError: '返回的資料不符合規範,正確的成功狀態碼應為:"{statusName}": {statusCode}',
|
||||
xhrError: '請求異常,錯誤提示:{msg}'
|
||||
},
|
||||
transfer: {
|
||||
noData: '無資料',
|
||||
noMatch: '無匹配資料',
|
||||
title: ['列表一', '列表二'],
|
||||
searchPlaceholder: '關鍵詞搜尋'
|
||||
},
|
||||
tree: {
|
||||
defaultNodeName: '未命名',
|
||||
noData: '無資料',
|
||||
deleteNodePrompt: '確認刪除"{name}"節點嗎?'
|
||||
},
|
||||
upload: {
|
||||
fileType: {
|
||||
file: '檔案',
|
||||
image: '圖片',
|
||||
video: '影片',
|
||||
audio: '音訊'
|
||||
},
|
||||
validateMessages: {
|
||||
fileExtensionError: '選擇的{fileType}中包含不支援的格式',
|
||||
filesOverLengthLimit: '同時最多只能上傳: {length} 個檔案',
|
||||
currentFilesLength: '您當前已經選擇了: {length} 個檔案',
|
||||
fileOverSizeLimit: '檔案大小不能超過 {size}'
|
||||
},
|
||||
chooseText: '{length} 個檔案'
|
||||
},
|
||||
util: {
|
||||
timeAgo: {
|
||||
days: '{days} 天前',
|
||||
hours: '{hours} 小時前',
|
||||
minutes: '{minutes} 分鐘前',
|
||||
future: '未來',
|
||||
justNow: '剛剛'
|
||||
},
|
||||
toDateString: {
|
||||
meridiem: function(hours, minutes){
|
||||
var hm = hours * 100 + minutes;
|
||||
if (hm < 500) {
|
||||
return '凌晨';
|
||||
} else if (hm < 800) {
|
||||
return '早上';
|
||||
} else if (hm < 1200) {
|
||||
return '上午';
|
||||
} else if (hm < 1300) {
|
||||
return '中午';
|
||||
} else if (hm < 1900) {
|
||||
return '下午';
|
||||
}
|
||||
return '晚上';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="{{= d.layui[2].cdn.js }}"></script>
|
||||
<script>
|
||||
layui.use(async function () {
|
||||
const {
|
||||
$, colorpicker, dropdown, flow, form,
|
||||
i18n, laydate, laypage, laytpl, layer,
|
||||
table, transfer, tree, upload, util
|
||||
} = layui;
|
||||
|
||||
/**
|
||||
* 业务 i18n 配置
|
||||
* 说明:此处仅为了让演示的「页面内容」与「Layui 组件」语言保持一致。实际使用时通常只需指定 Layui 组件语言环境,而页面内容的语言建议由您的项目本身进行管理。
|
||||
*/
|
||||
i18n.set({
|
||||
messages: {
|
||||
'zh-CN': {
|
||||
custom: {
|
||||
switchLanguage: '切换语言',
|
||||
readme: {
|
||||
description: 'layui.i18n.$t 是私有方法(未文档化),此处仅用于演示',
|
||||
hello: '你好',
|
||||
},
|
||||
form: {
|
||||
required: '验证必填项',
|
||||
phone: '验证手机号',
|
||||
email: '验证邮箱',
|
||||
date: '验证日期',
|
||||
select: '选择框',
|
||||
submit: '立即提交',
|
||||
reset: '重置',
|
||||
placeholder: '请输入'
|
||||
}
|
||||
}
|
||||
},
|
||||
'en': {
|
||||
custom: {
|
||||
switchLanguage: 'Switch Language',
|
||||
readme: {
|
||||
description: 'layui.i18n.$t is a private method (undocumented), used here for demonstration only.',
|
||||
hello: 'Hello',
|
||||
},
|
||||
form: {
|
||||
required: 'Required',
|
||||
phone: 'Telephone',
|
||||
email: 'Email',
|
||||
date: 'Date',
|
||||
select: 'Select',
|
||||
submit: 'Submit',
|
||||
reset: 'Reset',
|
||||
placeholder: 'Please enter'
|
||||
}
|
||||
}
|
||||
},
|
||||
'zh-HK': {
|
||||
custom: {
|
||||
switchLanguage: '切換語言',
|
||||
readme: {
|
||||
description: 'layui.i18n.$t 是私有方法(未文檔化),此處僅用於示範',
|
||||
hello: '你好',
|
||||
},
|
||||
form: {
|
||||
required: '驗證必填項',
|
||||
phone: '驗證手機號',
|
||||
email: '驗證郵箱',
|
||||
date: '驗證日期',
|
||||
select: '選擇框',
|
||||
submit: '立即提交',
|
||||
reset: '重置',
|
||||
placeholder: '請輸入'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 渲染页面模板
|
||||
const template = $('#template').html();
|
||||
const html = laytpl(template, { tagStyle: 'modern' }).render();
|
||||
$('#root').html(html);
|
||||
|
||||
|
||||
/**
|
||||
* 组件示例
|
||||
*/
|
||||
|
||||
// code
|
||||
layui.code({
|
||||
elem: "#demo-code",
|
||||
preview: true,
|
||||
tools: ['copy', 'full', 'window'],
|
||||
header: true,
|
||||
lang: 'html',
|
||||
langMarker: true,
|
||||
});
|
||||
|
||||
// colorpicker
|
||||
colorpicker.render({
|
||||
elem: "#demo-colorpicker",
|
||||
});
|
||||
|
||||
// dropdown
|
||||
dropdown.render({
|
||||
elem: "#demo-dropdown",
|
||||
});
|
||||
|
||||
// flow
|
||||
flow.load({
|
||||
elem: '#demo-flow',
|
||||
scrollElem: '#demo-flow',
|
||||
done: function (page, next) {
|
||||
// 模拟数据插入
|
||||
setTimeout(function () {
|
||||
var lis = [];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
lis.push('<li>' + ((page - 1) * 3 + i + 1) + '</li>')
|
||||
}
|
||||
next(lis.join(''), page < 2);
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
||||
// form
|
||||
form.on('submit(demo1)', function (data) {
|
||||
var field = data.field;
|
||||
// 显示填写结果,仅作演示用
|
||||
layer.alert(JSON.stringify(field));
|
||||
return false;
|
||||
});
|
||||
|
||||
// laydate
|
||||
laydate.render({
|
||||
elem: "#demo-laydate",
|
||||
type: "datetime",
|
||||
calendar: true
|
||||
});
|
||||
|
||||
// layer
|
||||
util.on({
|
||||
alert: function () {
|
||||
layer.alert("Hello world");
|
||||
},
|
||||
prompt: function () {
|
||||
layer.prompt({ formType: 1, maxlength: 3 }, function (value, index) {
|
||||
layer.close(index);
|
||||
});
|
||||
},
|
||||
photos: function () {
|
||||
layer.photos({
|
||||
photos: {
|
||||
"title": "Photos Demo",
|
||||
"start": 0,
|
||||
"data": [
|
||||
{
|
||||
"alt": "layer",
|
||||
"pid": 1,
|
||||
"src": "https://unpkg.com/outeres@0.1.1/demo/layer.png",
|
||||
},
|
||||
{
|
||||
"alt": "error",
|
||||
"pid": 3,
|
||||
"src": "error.png",
|
||||
},
|
||||
{
|
||||
"alt": "universe",
|
||||
"pid": 5,
|
||||
"src": "https://unpkg.com/outeres@0.1.1/demo/outer-space.jpg",
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
// laypage
|
||||
laypage.render({
|
||||
elem: "demo-laypage-all",
|
||||
count: 100,
|
||||
layout: ["count", "prev", "page", "next", "limit", "refresh", "skip"],
|
||||
});
|
||||
|
||||
// table
|
||||
table.render({
|
||||
elem: '#demo-table',
|
||||
cols: [[{ field: 'test', title: '1', sort: true }, { field: 'test2', title: '2', sort: true }]],
|
||||
data: new Array(0),
|
||||
toolbar: 'default',
|
||||
defaultToolbar: ['filter', 'exports', 'print'],
|
||||
height: 'full',
|
||||
page: true,
|
||||
text: {
|
||||
// none: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
// transfer
|
||||
transfer.render({
|
||||
elem: '#demo-transfer',
|
||||
data: [
|
||||
{ "value": "1", "title": "Item 1" },
|
||||
{ "value": "2", "title": "Item 2" },
|
||||
{ "value": "3", "title": "Item 3" },
|
||||
],
|
||||
showSearch: true
|
||||
});
|
||||
|
||||
// tree
|
||||
tree.render({
|
||||
elem: '#demo-tree',
|
||||
data: [{ title: 'Item 1', id: 1, children: [{ title: 'Item 1-1', id: 2 }] }],
|
||||
edit: ['add', 'update', 'del']
|
||||
});
|
||||
|
||||
// upload
|
||||
upload.render({
|
||||
elem: '#demo-upload',
|
||||
url: '', // 实际使用时改成您自己的上传接口即可。
|
||||
multiple: true,
|
||||
accept: 'file',
|
||||
number: 1
|
||||
});
|
||||
|
||||
// util
|
||||
$('#demo-time-ago-picker').on('change', function(){
|
||||
$('#demo-time-ago-display').html(
|
||||
util.timeAgo(this.value)
|
||||
);
|
||||
})
|
||||
|
||||
$('#demo-toDateString').html(
|
||||
util.toDateString('2023-01-01 11:35:25', 'yyyy-MM-dd HH:mm:ss A')
|
||||
+ '<br>'
|
||||
+ util.toDateString('2023-01-01 18:35:25', 'yyyy-MM-dd HH:mm:ss A')
|
||||
)
|
||||
|
||||
// 演示:切换语言
|
||||
$("#change-locale").val(i18n.config.locale);
|
||||
form.render('select').on("select(change-locale)", function (elem) {
|
||||
// 记录语言,并重载页面(推荐)
|
||||
localStorage.setItem('layui-i18n-local-test', elem.value);
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("body").css("opacity", 1);
|
||||
console.log(i18n.config)
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
183
docs/i18n/detail/options.md
Normal file
183
docs/i18n/detail/options.md
Normal file
@@ -0,0 +1,183 @@
|
||||
<pre class="layui-code" lay-options="{style: 'height: 525px;', layout: ['code'], tools: []}">
|
||||
<textarea>
|
||||
i18n.set({
|
||||
locale: 'zh-CN', // 设置语言环境
|
||||
messages: { // 语言包
|
||||
'zh-CN': { // 简体中文语言包(内置)
|
||||
code: {
|
||||
copy: '复制代码',
|
||||
copied: '已复制',
|
||||
copyError: '复制失败',
|
||||
maximize: '最大化显示',
|
||||
restore: '还原显示',
|
||||
preview: '在新窗口预览'
|
||||
},
|
||||
colorpicker: {
|
||||
clear: '清除',
|
||||
confirm: '确定'
|
||||
},
|
||||
dropdown: {
|
||||
noData: '暂无数据'
|
||||
},
|
||||
flow: {
|
||||
loadMore: '加载更多',
|
||||
noMore: '没有更多了'
|
||||
},
|
||||
form: {
|
||||
select: {
|
||||
noData: '暂无数据',
|
||||
noMatch: '无匹配数据',
|
||||
placeholder: '请选择'
|
||||
},
|
||||
validateMessages: {
|
||||
required: '必填项不能为空',
|
||||
phone: '手机号格式不正确',
|
||||
email: '邮箱格式不正确',
|
||||
url: '链接格式不正确',
|
||||
number: '只能填写数字',
|
||||
date: '日期格式不正确',
|
||||
identity: '身份证号格式不正确'
|
||||
},
|
||||
verifyErrorPromptTitle: '提示'
|
||||
},
|
||||
laydate: {
|
||||
months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
weeks: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
time: ['时', '分', '秒'],
|
||||
literal: {
|
||||
year: '年'
|
||||
},
|
||||
selectDate: '选择日期',
|
||||
selectTime: '选择时间',
|
||||
startTime: '开始时间',
|
||||
endTime: '结束时间',
|
||||
tools: {
|
||||
confirm: '确定',
|
||||
clear: '清空',
|
||||
now: '现在',
|
||||
reset: '重置'
|
||||
},
|
||||
rangeOrderPrompt: '结束时间不能早于开始时间\n请重新选择',
|
||||
invalidDatePrompt: '不在有效日期或时间范围内\n',
|
||||
formatErrorPrompt: '日期格式不合法\n必须遵循:\n{format}\n',
|
||||
autoResetPrompt: '已自动重置',
|
||||
preview: '当前选中的结果'
|
||||
},
|
||||
layer: {
|
||||
confirm: '确定',
|
||||
cancel: '取消',
|
||||
defaultTitle: '信息',
|
||||
prompt: {
|
||||
InputLengthPrompt: '最多输入 {length} 个字符'
|
||||
},
|
||||
photos: {
|
||||
noData: '没有图片',
|
||||
tools:{
|
||||
rotate: '旋转',
|
||||
scaleX: '水平变换',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '缩小',
|
||||
reset: '还原',
|
||||
close: '关闭'
|
||||
},
|
||||
viewPicture: '查看原图',
|
||||
urlError: {
|
||||
prompt: '当前图片地址异常,\n是否继续查看下一张?',
|
||||
confirm: '下一张',
|
||||
cancel: '不看了'
|
||||
}
|
||||
}
|
||||
},
|
||||
laypage: {
|
||||
prev: '上一页',
|
||||
next: '下一页',
|
||||
first: '首页',
|
||||
last: '尾页',
|
||||
total: '共 {total} 条',
|
||||
pagesize: '条/页',
|
||||
goto: '到第',
|
||||
page: '页',
|
||||
confirm: '确定'
|
||||
},
|
||||
table: {
|
||||
sort: {
|
||||
asc: '升序',
|
||||
desc: '降序'
|
||||
},
|
||||
noData: '暂无数据',
|
||||
tools:{
|
||||
filter: {
|
||||
title: '筛选列'
|
||||
},
|
||||
export: {
|
||||
title: '导出',
|
||||
noDataPrompt: '当前表格无数据',
|
||||
compatPrompt: '导出功能不支持 IE,请用 Chrome 等高级浏览器导出',
|
||||
csvText : '导出 CSV 文件'
|
||||
},
|
||||
print: {
|
||||
title: '打印',
|
||||
noDataPrompt: '当前表格无数据'
|
||||
}
|
||||
},
|
||||
dataFormatError: '返回的数据不符合规范,正确的成功状态码应为:"{statusName}": {statusCode}',
|
||||
xhrError: '请求异常,错误提示:{msg}'
|
||||
},
|
||||
transfer: {
|
||||
noData: '暂无数据',
|
||||
noMatch: '无匹配数据',
|
||||
title: ['列表一', '列表二'],
|
||||
searchPlaceholder: '关键词搜索'
|
||||
},
|
||||
tree: {
|
||||
defaultNodeName: '未命名',
|
||||
noData: '暂无数据',
|
||||
deleteNodePrompt: '确认删除"{name}"节点吗?'
|
||||
},
|
||||
upload: {
|
||||
fileType: {
|
||||
file: '文件',
|
||||
image: '图片',
|
||||
video: '视频',
|
||||
audio: '音频'
|
||||
},
|
||||
validateMessages: {
|
||||
fileExtensionError: '选择的{fileType}中包含不支持的格式',
|
||||
filesOverLengthLimit: '同时最多只能上传: {length} 个文件',
|
||||
currentFilesLength: '当前已经选择了: {length} 个文件',
|
||||
fileOverSizeLimit: '文件大小不能超过 {size}'
|
||||
},
|
||||
chooseText: '{length} 个文件'
|
||||
},
|
||||
util: {
|
||||
timeAgo: {
|
||||
days: '{days} 天前',
|
||||
hours: '{hours} 小时前',
|
||||
minutes: '{minutes} 分钟前',
|
||||
future: '未来',
|
||||
justNow: '刚刚'
|
||||
},
|
||||
toDateString: {
|
||||
// https://www.unicode.org/cldr/charts/47/supplemental/day_periods.html
|
||||
meridiem: function(hours, minutes){
|
||||
var hm = hours * 100 + minutes;
|
||||
if (hm < 500) {
|
||||
return '凌晨';
|
||||
} else if (hm < 800) {
|
||||
return '早上';
|
||||
} else if (hm < 1200) {
|
||||
return '上午';
|
||||
} else if (hm < 1300) {
|
||||
return '中午';
|
||||
} else if (hm < 1900) {
|
||||
return '下午';
|
||||
}
|
||||
return '晚上';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</textarea>
|
||||
</pre>
|
114
docs/i18n/index.md
Normal file
114
docs/i18n/index.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: 国际化 i18n
|
||||
toc: true
|
||||
---
|
||||
|
||||
# 国际化 <sup>2.12+</sup>
|
||||
|
||||
> `i18n` 是 2.12 版本新增的国际化模块,用于为 Layui 各组件实现多语言支持。
|
||||
|
||||
<h2 id="examples" lay-toc="{}" style="margin-bottom: 0;">完整演示</h2>
|
||||
|
||||
为了避免语言包配置冗长而影响示例源代码的查看,此处只演示「简体中文 / English / 繁體中文」语言环境,你可以点击该示例头部的「切换语言」选择框查看 Layui 组件在不同语言环境中的显示效果。
|
||||
|
||||
<div class="ws-docs-showcase"></div>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: 'iframe', text: {preview: 'Preview'}, style: 'height: 560px;', layout: ['preview', 'code'], tools: ['full','window']}">
|
||||
<textarea>
|
||||
{{- d.include("/i18n/detail/demo.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h2 id="api" lay-toc="{hot: true}">API</h2>
|
||||
|
||||
| API | 描述 |
|
||||
| --- | --- |
|
||||
| var i18n = layui.i18n | 获得 `i18n` 模块。|
|
||||
| [i18n.set(options)](#set) | 设置语言环境及语言包。|
|
||||
|
||||
<h3 id="set" lay-toc="{level: 2}">配置方式</h3>
|
||||
|
||||
i18n 支持两种配置方式,你可以根据实际应用场景选择任一方式。
|
||||
|
||||
#### 1. 通过 `i18n.set()` 方法配置
|
||||
|
||||
`i18n.set(options)`
|
||||
|
||||
- 参数 `options` : 基础属性选项。[#详见语言包选项](#options)
|
||||
|
||||
```js
|
||||
layui.use(function() {
|
||||
var i18n = layui.i18n;
|
||||
|
||||
// 设置语言
|
||||
i18n.set({
|
||||
locale: 'zh-CN', // 当前语言环境。zh-CN 为内置简体中文语言包
|
||||
messages: { // 扩展其他语言包
|
||||
'en': {},
|
||||
'zh-HK': {},
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
🔔 请注意:如果你的页面有用到 Layui 组件的自动渲染(如 table 模板配置渲染方式),因为执行顺序的问题,组件在自动渲染时可能无法读取到 `i18n.set()` 的配置信息,此时建议采用下述 `LAYUI_GLOBAL.i18n` 全局配置。
|
||||
|
||||
#### 2. 通过 `LAYUI_GLOBAL.i18n` 全局配置(推荐)
|
||||
|
||||
由于 i18n 配置与组件渲染存在执行顺序问题,为了确保 i18n 配置始终在组件渲染之前生效,更推荐采用该全局配置方式。
|
||||
|
||||
```html
|
||||
<script>
|
||||
// 全局配置应放在 layui.js 引入之前的位置
|
||||
window.LAYUI_GLOBAL = {
|
||||
i18n: { // 选项同 i18n.set(options)
|
||||
locale: 'zh-CN', // 当前语言环境
|
||||
messages: { // 扩展其他语言包
|
||||
'en': {},
|
||||
'zh-HK': {},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="/layui/layui.js"></script>
|
||||
```
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2}">语言包选项</h3>
|
||||
|
||||
i18n 默认采用简体中文(`zh-CN`)语言环境,以下为各组件消息文本对应的选项:
|
||||
|
||||
<div>
|
||||
{{- d.include("/i18n/detail/options.md") }}
|
||||
</div>
|
||||
|
||||
|
||||
基于上述选项,还可以扩展更多语言包,如:
|
||||
|
||||
```js
|
||||
i18n.set({
|
||||
locale: 'en', // 当前语言环境
|
||||
messages: { // 扩展更多语言包
|
||||
'en': { // 通用英语
|
||||
code: {
|
||||
copy: 'Copy Code',
|
||||
copied: 'Copied',
|
||||
// ……
|
||||
},
|
||||
// ……
|
||||
},
|
||||
'fr': {}, // 通用法语
|
||||
'zh-HK': {}, // 繁体中文
|
||||
// …… // 更多语言
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
为了节省时间,也可以借助「**第三方提供并维护**的 Layui 多语言 AI 翻译工具」直接生成不同语言的消息文本,如:
|
||||
|
||||
| 翻译工具 | 提供者 |
|
||||
| --- | --- |
|
||||
| <a href="https://gitee.com/mail_osc/translate/tree/master/extend/layui-i18n-object-translate" target="_blank">https://gitee.com/mail_osc/translate/tree/master/extend/layui-i18n-object-translate</a> | <a href="https://github.com/xnx3" target="_blank">@xnx3</a> |
|
||||
|
||||
## 💖 心语
|
||||
|
||||
i18n 模块是在众多开发者强烈的需求呼声中,由 Layui 核心 Contributor [@Sight-wcg](https://github.com/Sight-wcg) 完成,该模块通过简练的设计,为 Layui 组件实现了多语言的无缝接入,并且兼容了一些原本自带简单多语言或消息配置的组件,Layui 2.x 版本也因此具备国际化能力。
|
Reference in New Issue
Block a user