feat(i18n): 中英文框架(命名空间字典+语言切换)+profile 接入参考+注册3个新页面

这个提交包含在:
Guoguo
2026-07-18 20:07:13 -07:00
父节点 cc5159ebb7
当前提交 d8eb6677c6
修改 11 个文件,包含 431 行新增38 行删除
+120
查看文件
@@ -0,0 +1,120 @@
// Lightweight i18n for WeChat Mini Program (no official i18n API).
//
// Usage in a page:
// var i18n = require('../../i18n/index')
// onShow: function () { i18n.bind(this) } // injects `i18n` + `locale` into page data
// WXML: {{i18n.common.loading}} / {{i18n.profile.title}}
// JS strings: i18n.t('common.loading')
//
// Locale is persisted in storage; switching locale notifies listeners and the
// convention is that every page re-runs i18n.bind(this) in onShow, so tab pages
// pick up the new language the next time they are shown.
var locales = require('./locales/index')
var STORAGE_KEY = 'app_locale'
var DEFAULT_LOCALE = 'zh'
var SUPPORTED = ['zh', 'en']
var LOCALE_NAMES = { zh: '中文', en: 'English' }
var _listeners = []
var _current = null
function getSystemLocale() {
try {
var sys = wx.getSystemInfoSync()
var lang = (sys.language || '').toLowerCase()
return lang.indexOf('en') === 0 ? 'en' : 'zh'
} catch (e) {
return DEFAULT_LOCALE
}
}
function getLocale() {
if (_current) return _current
var saved = ''
try { saved = wx.getStorageSync(STORAGE_KEY) } catch (e) {}
_current = SUPPORTED.indexOf(saved) !== -1 ? saved : getSystemLocale()
return _current
}
function setLocale(locale) {
if (SUPPORTED.indexOf(locale) === -1 || locale === _current) return
_current = locale
try { wx.setStorageSync(STORAGE_KEY, locale) } catch (e) {}
applyTabBar()
for (var i = 0; i < _listeners.length; i++) {
try { _listeners[i](locale) } catch (e) {}
}
}
function onChange(fn) { if (typeof fn === 'function') _listeners.push(fn) }
function offChange(fn) {
_listeners = _listeners.filter(function (f) { return f !== fn })
}
function getDict() {
var loc = getLocale()
return locales[loc] || locales[DEFAULT_LOCALE]
}
function _lookup(dict, key) {
if (!dict) return undefined
var parts = key.split('.')
var cur = dict
for (var i = 0; i < parts.length; i++) {
if (cur == null) return undefined
cur = cur[parts[i]]
}
return cur
}
// t('profile.title', {name: 'x'}) — falls back to zh, then to the raw key.
function t(key, params) {
var loc = getLocale()
var val = _lookup(locales[loc], key)
if (val === undefined) val = _lookup(locales[DEFAULT_LOCALE], key)
if (typeof val !== 'string') return val === undefined ? key : val
if (params) {
val = val.replace(/\{(\w+)\}/g, function (m, k) {
return params[k] !== undefined ? params[k] : m
})
}
return val
}
// Inject the full dictionary + current locale into a page/component's data.
function bind(page) {
if (page && typeof page.setData === 'function') {
page.setData({ i18n: getDict(), locale: getLocale() })
}
}
// Refresh the bottom tabBar labels to the current locale.
function applyTabBar() {
var d = getDict()
var tab = (d && d.common && d.common.tab) || {}
var items = [
{ index: 0, text: tab.home },
{ index: 1, text: tab.records },
{ index: 2, text: tab.mine }
]
items.forEach(function (it) {
if (!it.text) return
wx.setTabBarItem({ index: it.index, text: it.text, fail: function () {} })
})
}
module.exports = {
SUPPORTED: SUPPORTED,
LOCALE_NAMES: LOCALE_NAMES,
getLocale: getLocale,
setLocale: setLocale,
getSystemLocale: getSystemLocale,
onChange: onChange,
offChange: offChange,
getDict: getDict,
bind: bind,
applyTabBar: applyTabBar,
t: t
}
+67
查看文件
@@ -0,0 +1,67 @@
// Shared strings used across many pages.
module.exports = {
zh: {
tab: { home: '首页', records: '记录', mine: '我的' },
loading: '加载中...',
confirm: '确定',
cancel: '取消',
save: '保存',
saving: '保存中...',
back: '返回',
retry: '重试',
close: '关闭',
done: '完成',
start: '开始',
stop: '停止',
unknown: '未知',
saveSuccess: '保存成功',
saveFailed: '保存失败',
networkError: '网络异常,请重试',
deviceNotConnected: '设备未连接',
regions: {
right: '右区',
left: '左区',
top: '上区',
middle: '中区',
bottom: '下区'
},
wavelengths: {
red: '红光',
infrared: '红外光',
uv: '紫外光',
yellow: '黄光'
}
},
en: {
tab: { home: 'Home', records: 'Records', mine: 'Me' },
loading: 'Loading...',
confirm: 'OK',
cancel: 'Cancel',
save: 'Save',
saving: 'Saving...',
back: 'Back',
retry: 'Retry',
close: 'Close',
done: 'Done',
start: 'Start',
stop: 'Stop',
unknown: 'Unknown',
saveSuccess: 'Saved',
saveFailed: 'Save failed',
networkError: 'Network error, please retry',
deviceNotConnected: 'Device not connected',
regions: {
right: 'Right',
left: 'Left',
top: 'Top',
middle: 'Center',
bottom: 'Bottom'
},
wavelengths: {
red: 'Red',
infrared: 'Infrared',
uv: 'UV',
yellow: 'Yellow'
}
}
}
+27
查看文件
@@ -0,0 +1,27 @@
// Merges per-namespace dictionaries into { zh: {...}, en: {...} }.
// Each namespace file exports { zh: {...}, en: {...} } and owns its own strings,
// so parallel work on different features never touches the same file.
var common = require('./common')
var profile = require('./profile')
var lightInfo = require('./lightInfo')
var manual = require('./manual')
var report = require('./report')
var namespaces = {
common: common,
profile: profile,
lightInfo: lightInfo,
manual: manual,
report: report
}
var zh = {}
var en = {}
for (var ns in namespaces) {
if (!namespaces.hasOwnProperty(ns)) continue
zh[ns] = namespaces[ns].zh || {}
en[ns] = namespaces[ns].en || {}
}
module.exports = { zh: zh, en: en }
+6
查看文件
@@ -0,0 +1,6 @@
// Strings for the light-therapy efficacy intro page (pages/light-info).
// Owned by the light-info feature. Keep zh/en keys in sync.
module.exports = {
zh: {},
en: {}
}
+6
查看文件
@@ -0,0 +1,6 @@
// Strings for the manual treatment (direct wavelength/function select) page
// (pages/manual-treatment). Owned by the manual-mode feature. Keep zh/en in sync.
module.exports = {
zh: {},
en: {}
}
+69
查看文件
@@ -0,0 +1,69 @@
// "我的" page strings + language switcher.
module.exports = {
zh: {
notLoggedIn: '未登录',
editProfile: '编辑资料',
editProfileTitle: '编辑资料',
changeAvatar: '点击更换头像',
nickname: '昵称',
nicknamePlaceholder: '请输入昵称',
nicknameRequired: '昵称不能为空',
smartMode: '智能模式',
smartModeOpen: '已开放',
free: '免费',
inUse: '使用中',
remainDays: '剩余 {days}天',
notSubscribed: '未订阅',
goSubscribe: '去订阅 ',
menuDevice: '我的设备',
bound: '已绑定',
unbound: '未绑定',
menuHistory: '使用记录',
menuSubscription: '订阅管理',
menuHelp: '使用帮助',
menuContact: '联系我们',
menuLightInfo: '光疗功效介绍',
language: '语言 / Language',
logout: '退出登录',
logoutConfirmTitle: '退出登录',
logoutConfirmText: '确定要退出登录吗?',
unbindConfirmTitle: '确认解绑',
unbindConfirmText: '解绑后将无法使用该设备,确定要解绑吗?',
unbindAction: '解绑当前设备',
unbindDone: '已解绑',
unbindFailed: '解绑失败'
},
en: {
notLoggedIn: 'Not logged in',
editProfile: 'Edit',
editProfileTitle: 'Edit Profile',
changeAvatar: 'Tap to change avatar',
nickname: 'Nickname',
nicknamePlaceholder: 'Enter nickname',
nicknameRequired: 'Nickname is required',
smartMode: 'Smart Mode',
smartModeOpen: 'Available',
free: 'Free',
inUse: 'Active',
remainDays: '{days} days left',
notSubscribed: 'Not subscribed',
goSubscribe: 'Subscribe ',
menuDevice: 'My Device',
bound: 'Bound',
unbound: 'Not bound',
menuHistory: 'Usage Records',
menuSubscription: 'Subscription',
menuHelp: 'Help',
menuContact: 'Contact Us',
menuLightInfo: 'Light Therapy Guide',
language: '语言 / Language',
logout: 'Log Out',
logoutConfirmTitle: 'Log Out',
logoutConfirmText: 'Are you sure you want to log out?',
unbindConfirmTitle: 'Confirm Unbind',
unbindConfirmText: 'You will not be able to use this device after unbinding. Continue?',
unbindAction: 'Unbind current device',
unbindDone: 'Unbound',
unbindFailed: 'Unbind failed'
}
}
+57
查看文件
@@ -0,0 +1,57 @@
// Strings for the scan report page (pages/scan-report).
module.exports = {
zh: {
title: '肌肤扫描报告',
subtitle: '基于多光谱反射的肤质倾向参考',
scannedAt: '扫描时间',
overallTitle: '整体倾向',
regionTitle: '分区分析',
recommendTitle: '推荐护理',
noProblem: '未见明显倾向',
startTreatment: '开始推荐护理',
manualInstead: '我自己选',
disclaimer: '本报告依据光反射信号给出肤质倾向参考,不构成医疗诊断。实际护理请结合自身情况。',
conceptNotice: '当前诊断依据尚在标定中,结果仅供参考',
level: { low: '轻度', mid: '中度', high: '明显' },
problems: {
aging: '光老化 / 暗沉',
acne: '痘痘 / 出油',
pigment: '色素 / 暗黄',
deep: '深层循环 / 疲劳'
},
regionNames: {
right: '右脸',
left: '左脸',
top: '额头',
middle: '鼻区',
bottom: '下巴'
}
},
en: {
title: 'Skin Scan Report',
subtitle: 'Skin-tendency reference from multispectral reflection',
scannedAt: 'Scanned at',
overallTitle: 'Overall Tendency',
regionTitle: 'Zone Analysis',
recommendTitle: 'Recommended Care',
noProblem: 'No notable tendency',
startTreatment: 'Start Recommended Care',
manualInstead: 'Choose Myself',
disclaimer: 'This report gives a skin-tendency reference from light-reflection signals and is not a medical diagnosis. Please consider your own condition.',
conceptNotice: 'Diagnosis thresholds are still being calibrated; results are for reference only',
level: { low: 'Mild', mid: 'Moderate', high: 'Notable' },
problems: {
aging: 'Photoaging / Dullness',
acne: 'Acne / Oiliness',
pigment: 'Pigment / Sallowness',
deep: 'Deep circulation / Fatigue'
},
regionNames: {
right: 'Right cheek',
left: 'Left cheek',
top: 'Forehead',
middle: 'Nose',
bottom: 'Chin'
}
}
}