Преглед на файлове

Merge pull request #230 from yeungalan/claude/sweet-dirac-NFvvQ

Clock localization
Alan Yeung преди 2 седмици
родител
ревизия
0565ad1d25
променени са 2 файла, в които са добавени 761 реда и са изтрити 40 реда
  1. 89 40
      src/web/Clock/index.html
  2. 672 0
      src/web/Clock/locale/clock.json

+ 89 - 40
src/web/Clock/index.html

@@ -6,6 +6,7 @@
     <title>Clock</title>
     <script src="../script/jquery.min.js"></script>
     <script src="../script/ao_module.js"></script>
+    <script src="../script/applocale.js"></script>
     <style>
         *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
 
@@ -406,19 +407,19 @@
     <div class="tab-bar">
         <button class="tab-btn active" id="tab-clock-btn" onclick="switchTab('clock')">
             <img class="theme-icon" data-icon="clock" src="img/icons/clock_black.svg">
-            Clock
+            <span locale="clock/tab-clock">Clock</span>
         </button>
         <button class="tab-btn" id="tab-world-btn" onclick="switchTab('world')">
             <img class="theme-icon" data-icon="globe" src="img/icons/globe_black.svg">
-            World
+            <span locale="clock/tab-world">World</span>
         </button>
         <button class="tab-btn" id="tab-countdown-btn" onclick="switchTab('countdown')">
             <img class="theme-icon" data-icon="countdown" src="img/icons/countdown_black.svg">
-            Countdown
+            <span locale="clock/tab-countdown">Countdown</span>
         </button>
         <button class="tab-btn" id="tab-stopwatch-btn" onclick="switchTab('stopwatch')">
             <img class="theme-icon" data-icon="timer" src="img/icons/timer_black.svg">
-            Stopwatch
+            <span locale="clock/tab-stopwatch">Stopwatch</span>
         </button>
     </div>
 
@@ -455,19 +456,19 @@
 
         <!-- Toolbar -->
         <div class="wc-toolbar">
-            <span class="wc-section-lbl">Other Clocks</span>
+            <span class="wc-section-lbl" locale="clock/other-clocks">Other Clocks</span>
             <button class="wc-add-btn" onclick="tzPickerShow()">
                 <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round">
                     <line x1="12" y1="4" x2="12" y2="20"/><line x1="4" y1="12" x2="20" y2="12"/>
                 </svg>
-                Add City
+                <span locale="clock/add-city">Add City</span>
             </button>
         </div>
 
         <!-- Empty state -->
         <div id="wc-empty" style="display:none;">
             <img class="theme-icon" data-icon="globe" src="img/icons/globe_black.svg" width="44" height="44">
-            No clocks yet.<br>Tap <b>+ Add City</b> to get started.
+            <span locale="clock/no-clocks-empty">No clocks yet.<br>Tap <b>+ Add City</b> to get started.</span>
         </div>
 
         <!-- Clock list -->
@@ -496,11 +497,11 @@
             <span></span>
             <button class="cd-arrow" id="cd-dn-s" onclick="cdAdjust('s',-1)">▼</button>
 
-            <div class="cd-lbl">Hours</div>
+            <div class="cd-lbl" locale="clock/cd-hours">Hours</div>
             <span></span>
-            <div class="cd-lbl">Minutes</div>
+            <div class="cd-lbl" locale="clock/cd-minutes">Minutes</div>
             <span></span>
-            <div class="cd-lbl">Seconds</div>
+            <div class="cd-lbl" locale="clock/cd-seconds">Seconds</div>
         </div>
 
         <div class="cd-progress-wrap">
@@ -528,7 +529,7 @@
             <span id="sw-hm">00:00</span><span class="sw-dim">:</span><span id="sw-s">00</span><span class="sw-dim sw-cs">.<span id="sw-cs-val">00</span></span>
         </div>
 
-        <div class="sw-status" id="sw-status">Ready</div>
+        <div class="sw-status" id="sw-status" locale="clock/status-ready">Ready</div>
 
         <div class="ctrl-row">
             <button id="sw-btn-toggle" class="ctrl-btn btn-play" onclick="swToggle()" title="Start / Pause">
@@ -551,7 +552,7 @@
 <div id="tz-overlay" onclick="if(event.target===this)tzPickerHide()">
     <div class="tz-sheet">
         <div class="tz-sheet-header">
-            <span class="tz-sheet-title">Choose a City</span>
+            <span class="tz-sheet-title" locale="clock/tz-sheet-title">Choose a City</span>
             <button class="tz-sheet-close" onclick="tzPickerHide()">✕</button>
         </div>
         <input class="tz-search" id="tz-search" type="search" placeholder="Search city or region…" oninput="tzFilter(this.value)">
@@ -563,10 +564,34 @@
 // ════════════════════════════════════════════════════════════════════════
 //  SETUP
 // ════════════════════════════════════════════════════════════════════════
-ao_module_setWindowTitle('Clock');
 ao_module_setFixedWindowSize();
 ao_module_setWindowSize(420, 450);
 
+function getStr(key, fallback) {
+    return (typeof applocale !== 'undefined' && applocale) ? applocale.getString(key, fallback) : fallback;
+}
+
+function getCityDisplayName(englishName) {
+    var key = 'clock/city-' + englishName.toLowerCase().replace(/ /g, '-');
+    return getStr(key, englishName);
+}
+
+// Returns BCP-47 locale string for date/time formatting based on the user's
+// arozos language preference, falling back to the browser's locale.
+function getDateLocale() {
+    var lang = (localStorage.getItem('global_language') || 'default').toLowerCase();
+    if (lang === 'default' || lang === '') return navigator.language;
+    var map = {
+        'en-us': 'en-US',
+        'zh-cn': 'zh-CN',
+        'zh-tw': 'zh-TW',
+        'zh-hk': 'zh-HK',
+        'ja-jp': 'ja-JP',
+        'ko-kr': 'ko-KR'
+    };
+    return map[lang] || lang;
+}
+
 function pad(n){ return n < 10 ? '0'+n : String(n); }
 function htmlEsc(s){
     return s.replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
@@ -615,8 +640,13 @@ function switchTab(id){
         document.getElementById('tab-'+t+'-btn').classList.toggle('active', t === id);
         document.getElementById('panel-'+t).classList.toggle('active', t === id);
     });
-    var labels = { clock:'Clock', world:'World Clock', countdown:'Countdown', stopwatch:'Stopwatch' };
-    ao_module_setWindowTitle('Clock — ' + labels[id]);
+    var labels = {
+        clock:     getStr('clock/wintitle-main',      'Clock'),
+        world:     getStr('clock/wintitle-world',     'World Clock'),
+        countdown: getStr('clock/wintitle-countdown', 'Countdown'),
+        stopwatch: getStr('clock/wintitle-stopwatch', 'Stopwatch')
+    };
+    ao_module_setWindowTitle(getStr('clock/wintitle-main', 'Clock') + ' — ' + labels[id]);
 }
 
 // ════════════════════════════════════════════════════════════════════════
@@ -628,9 +658,9 @@ function updateDigitalClock(){
     document.getElementById('dc-m').textContent    = pad(now.getMinutes());
     document.getElementById('dc-s').textContent    = pad(now.getSeconds());
     document.getElementById('dc-day').textContent  =
-        now.toLocaleDateString('en-US', { weekday: 'long' });
+        now.toLocaleDateString(getDateLocale(), { weekday: 'long' });
     document.getElementById('dc-date').textContent =
-        now.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
+        now.toLocaleDateString(getDateLocale(), { month: 'long', day: 'numeric', year: 'numeric' });
 }
 
 // ════════════════════════════════════════════════════════════════════════
@@ -656,18 +686,20 @@ function updateLocalClock(){
     document.getElementById('lc-sec').textContent  = ':' + pad(s);
     // Date
     document.getElementById('lc-date').textContent =
-        now.toLocaleDateString('en-US', { weekday:'short', month:'short', day:'numeric' });
+        now.toLocaleDateString(getDateLocale(), { weekday:'short', month:'short', day:'numeric' });
 }
 
 function initLocalClock(){
     var city = getLocalTzCity();
-    document.getElementById('lc-label').textContent = city;
+    document.getElementById('lc-label').textContent = getCityDisplayName(city);
     try {
         var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
         var offFmt = new Intl.DateTimeFormat('en', { timeZone: tz, timeZoneName: 'shortOffset' });
         var offset = '';
         offFmt.formatToParts(new Date()).forEach(function(x){ if (x.type==='timeZoneName') offset = x.value; });
-        document.getElementById('lc-tz').textContent = offset + '  ·  ' + tz.split('/')[0];
+        var tzRegion = tz.split('/')[0];
+        var tzRegionLabel = getStr('clock/region-' + tzRegion.toLowerCase(), tzRegion);
+        document.getElementById('lc-tz').textContent = offset + '  ·  ' + tzRegionLabel;
     } catch(e){}
     updateLocalClock();
 }
@@ -773,14 +805,14 @@ function wcRender(){
     listEl.innerHTML = wcClocks.map(function(c, i){
         return '<div class="wc-card">' +
             '<div class="wc-card-left">' +
-                '<div class="wc-city">'+htmlEsc(c.city)+'</div>' +
+                '<div class="wc-city">'+htmlEsc(getCityDisplayName(c.city))+'</div>' +
                 '<div class="wc-meta" id="wc-meta-'+i+'"></div>' +
             '</div>' +
             '<div class="wc-time-block">' +
                 '<span class="wc-time" id="wc-hm-'+i+'">--:--</span>' +
                 '<span class="wc-secs" id="wc-sec-'+i+'"></span>' +
             '</div>' +
-            '<button class="wc-remove" onclick="wcRemove('+i+')" title="Remove">×</button>' +
+            '<button class="wc-remove" onclick="wcRemove('+i+')" title="'+getStr('clock/btn-remove','Remove')+'">×</button>' +
         '</div>';
     }).join('');
     wcRefresh();
@@ -805,7 +837,8 @@ function wcRefresh(){
             var tzMidnight = new Date(now.toLocaleString('en-US', { timeZone: c.tz }));
             tzMidnight.setHours(0,0,0,0);
             var dd = Math.round((tzMidnight - localMidnight) / 86400000);
-            var dayLabel = dd > 0 ? ' · +'+dd+'d' : (dd < 0 ? ' · '+dd+'d' : '');
+            var dSuffix = getStr('clock/day-suffix', 'd');
+            var dayLabel = dd > 0 ? ' · +'+dd+dSuffix : (dd < 0 ? ' · '+dd+dSuffix : '');
 
             var hmEl   = document.getElementById('wc-hm-'+i);
             var secEl  = document.getElementById('wc-sec-'+i);
@@ -848,7 +881,11 @@ function tzPickerHide(){ document.getElementById('tz-overlay').classList.remove(
 function tzFilter(q){
     q = q.toLowerCase().trim();
     tzFiltered = q
-        ? ALL_CITIES.filter(function(c){ return c[0].toLowerCase().includes(q) || c[2].toLowerCase().includes(q); })
+        ? ALL_CITIES.filter(function(c){
+            return c[0].toLowerCase().includes(q) ||
+                   c[2].toLowerCase().includes(q) ||
+                   getCityDisplayName(c[0]).toLowerCase().includes(q);
+          })
         : ALL_CITIES.slice();
     tzRenderList();
 }
@@ -865,7 +902,7 @@ function tzGetOffset(tz){
 function tzRenderList(){
     var listEl = document.getElementById('tz-list');
     if (!tzFiltered.length){
-        listEl.innerHTML = '<div class="tz-noresults">No results</div>';
+        listEl.innerHTML = '<div class="tz-noresults">'+getStr('clock/no-results','No results')+'</div>';
         return;
     }
     var ORDER = ['Americas','Europe','Asia','Africa','Australia','Pacific','UTC'];
@@ -874,10 +911,10 @@ function tzRenderList(){
     var html = '';
     ORDER.forEach(function(r){
         if (!groups[r] || !groups[r].length) return;
-        html += '<div class="tz-region-lbl">'+r+'</div>';
+        html += '<div class="tz-region-lbl">'+getStr('clock/region-'+r.toLowerCase().replace(/ /g,'-'), r)+'</div>';
         groups[r].forEach(function(c){
             html += '<div class="tz-item" data-city="'+htmlEsc(c[0])+'" data-tz="'+htmlEsc(c[1])+'">' +
-                '<span class="tz-item-name">'+htmlEsc(c[0])+'</span>' +
+                '<span class="tz-item-name">'+htmlEsc(getCityDisplayName(c[0]))+'</span>' +
                 '<span class="tz-item-offset">'+tzGetOffset(c[1])+'</span>' +
             '</div>';
         });
@@ -925,7 +962,7 @@ function cdStart(){
     document.getElementById('cd-btn-pause').disabled = false;
     document.getElementById('cd-progress').style.background = 'var(--accent)';
     cdTimer = setInterval(cdTick, 200);
-    ao_module_setWindowTitle('Clock — Countdown');
+    ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + '' + getStr('clock/wintitle-countdown','Countdown'));
 }
 
 function cdTick(){
@@ -962,7 +999,7 @@ function cdPause(){
     cdArrows(false);
     document.getElementById('cd-btn-play').disabled  = false;
     document.getElementById('cd-btn-pause').disabled = true;
-    ao_module_setWindowTitle('Clock — Countdown (Paused)');
+    ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + '' + getStr('clock/wintitle-countdown-paused','Countdown (Paused)'));
 }
 
 function cdReset(){
@@ -975,11 +1012,11 @@ function cdReset(){
     document.getElementById('cd-progress').style.background = 'var(--accent)';
     ['cd-h','cd-m','cd-s'].forEach(function(id){ document.getElementById(id).classList.remove('alarm'); });
     if (cdAlarm){ cdAlarm.pause(); cdAlarm=null; }
-    ao_module_setWindowTitle('Clock — Countdown');
+    ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + '' + getStr('clock/wintitle-countdown','Countdown'));
 }
 
 function cdFireAlarm(){
-    ao_module_setWindowTitle("Clock — ⏰ Time's up!");
+    ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + ' — ' + getStr('clock/wintitle-times-up',"⏰ Time's up!"));
     try {
         cdAlarm = new Audio('sound/imuslab_theme.mp3');
         cdAlarm.volume = 0.3;
@@ -1005,16 +1042,16 @@ function swToggle(){
         document.getElementById('sw-ico-play').style.display  = '';
         document.getElementById('sw-ico-pause').style.display = 'none';
         document.getElementById('sw-btn-toggle').className    = 'ctrl-btn btn-play';
-        document.getElementById('sw-status').textContent      = 'Paused';
-        ao_module_setWindowTitle('Clock — Stopwatch (Paused)');
+        document.getElementById('sw-status').textContent      = getStr('clock/status-paused','Paused');
+        ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + '' + getStr('clock/wintitle-stopwatch-paused','Stopwatch (Paused)'));
     } else {
         swStartTs=Date.now(); swRunning=true;
         swTimer=setInterval(swTick, 47);
         document.getElementById('sw-ico-play').style.display  = 'none';
         document.getElementById('sw-ico-pause').style.display = '';
         document.getElementById('sw-btn-toggle').className    = 'ctrl-btn btn-pause';
-        document.getElementById('sw-status').textContent      = 'Running';
-        ao_module_setWindowTitle('Clock — Stopwatch');
+        document.getElementById('sw-status').textContent      = getStr('clock/status-running','Running');
+        ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + '' + getStr('clock/wintitle-stopwatch','Stopwatch'));
     }
 }
 
@@ -1023,9 +1060,9 @@ function swReset(){
     document.getElementById('sw-ico-play').style.display  = '';
     document.getElementById('sw-ico-pause').style.display = 'none';
     document.getElementById('sw-btn-toggle').className    = 'ctrl-btn btn-play';
-    document.getElementById('sw-status').textContent      = 'Ready';
+    document.getElementById('sw-status').textContent      = getStr('clock/status-ready','Ready');
     swDraw(0);
-    ao_module_setWindowTitle('Clock — Stopwatch');
+    ao_module_setWindowTitle(getStr('clock/wintitle-main','Clock') + '' + getStr('clock/wintitle-stopwatch','Stopwatch'));
 }
 
 function swTick(){ swDraw(swElapsed + (swRunning ? Date.now()-swStartTs : 0)); }
@@ -1051,10 +1088,22 @@ setInterval(function(){
 //  BOOT
 // ════════════════════════════════════════════════════════════════════════
 loadTheme();
-updateDigitalClock();
-initLocalClock();
-wcLoad();
 swDraw(0);
+
+if (typeof applocale !== 'undefined' && applocale) {
+    applocale.init("locale/clock.json", function(){
+        applocale.translate();
+        ao_module_setWindowTitle(getStr('clock/wintitle-main', 'Clock'));
+        updateDigitalClock();
+        initLocalClock();
+        wcLoad();
+    });
+} else {
+    ao_module_setWindowTitle('Clock');
+    updateDigitalClock();
+    initLocalClock();
+    wcLoad();
+}
 </script>
 </body>
 </html>

+ 672 - 0
src/web/Clock/locale/clock.json

@@ -0,0 +1,672 @@
+{
+    "author": "tobychui",
+    "version": "1.1",
+    "keys": {
+        "en-us": {
+            "name": "English (US)",
+            "strings": {
+                "clock/tab-clock": "Clock",
+                "clock/tab-world": "World",
+                "clock/tab-countdown": "Countdown",
+                "clock/tab-stopwatch": "Stopwatch",
+                "clock/other-clocks": "Other Clocks",
+                "clock/add-city": "Add City",
+                "clock/no-clocks-empty": "No clocks yet.<br>Tap <b>+ Add City</b> to get started.",
+                "clock/cd-hours": "Hours",
+                "clock/cd-minutes": "Minutes",
+                "clock/cd-seconds": "Seconds",
+                "clock/tz-sheet-title": "Choose a City",
+                "clock/status-ready": "Ready",
+                "clock/status-running": "Running",
+                "clock/status-paused": "Paused",
+                "clock/no-results": "No results",
+                "clock/wintitle-main": "Clock",
+                "clock/wintitle-world": "World Clock",
+                "clock/wintitle-countdown": "Countdown",
+                "clock/wintitle-stopwatch": "Stopwatch",
+                "clock/wintitle-countdown-paused": "Countdown (Paused)",
+                "clock/wintitle-times-up": "⏰ Time's up!",
+                "clock/wintitle-stopwatch-paused": "Stopwatch (Paused)",
+                "clock/btn-remove": "Remove",
+                "clock/day-suffix": "d",
+                "clock/region-asia": "Asia",
+                "clock/region-america": "America",
+                "clock/region-americas": "Americas",
+                "clock/region-europe": "Europe",
+                "clock/region-africa": "Africa",
+                "clock/region-australia": "Australia",
+                "clock/region-pacific": "Pacific",
+                "clock/region-utc": "UTC",
+                "clock/region-indian": "Indian Ocean",
+                "clock/region-atlantic": "Atlantic",
+                "clock/region-arctic": "Arctic"
+            },
+            "titles": {
+                "Start": "Start",
+                "Pause": "Pause",
+                "Reset": "Reset",
+                "Start / Pause": "Start / Pause",
+                "Toggle dark / light theme": "Toggle dark / light theme",
+                "Remove": "Remove"
+            },
+            "placeholder": {
+                "Search city or region…": "Search city or region…"
+            }
+        },
+        "zh-cn": {
+            "name": "简体中文",
+            "fontFamily": "\"Microsoft YaHei\", \"PingFang SC\", \"SimHei\", sans-serif",
+            "strings": {
+                "clock/tab-clock": "时钟",
+                "clock/tab-world": "世界",
+                "clock/tab-countdown": "倒计时",
+                "clock/tab-stopwatch": "秒表",
+                "clock/other-clocks": "其他时钟",
+                "clock/add-city": "添加城市",
+                "clock/no-clocks-empty": "还没有时钟。<br>点击 <b>+ 添加城市</b> 开始。",
+                "clock/cd-hours": "小时",
+                "clock/cd-minutes": "分钟",
+                "clock/cd-seconds": "秒",
+                "clock/tz-sheet-title": "选择城市",
+                "clock/status-ready": "就绪",
+                "clock/status-running": "运行中",
+                "clock/status-paused": "已暂停",
+                "clock/no-results": "无结果",
+                "clock/wintitle-main": "时钟",
+                "clock/wintitle-world": "世界时钟",
+                "clock/wintitle-countdown": "倒计时",
+                "clock/wintitle-stopwatch": "秒表",
+                "clock/wintitle-countdown-paused": "倒计时(已暂停)",
+                "clock/wintitle-times-up": "⏰ 时间到!",
+                "clock/wintitle-stopwatch-paused": "秒表(已暂停)",
+                "clock/btn-remove": "删除",
+                "clock/day-suffix": "天",
+                "clock/region-asia": "亚洲",
+                "clock/region-america": "美洲",
+                "clock/region-americas": "美洲",
+                "clock/region-europe": "欧洲",
+                "clock/region-africa": "非洲",
+                "clock/region-australia": "澳洲",
+                "clock/region-pacific": "太平洋",
+                "clock/region-utc": "UTC",
+                "clock/region-indian": "印度洋",
+                "clock/region-atlantic": "大西洋",
+                "clock/region-arctic": "北极",
+                "clock/city-abu-dhabi": "阿布扎比",
+                "clock/city-adelaide": "阿德莱德",
+                "clock/city-almaty": "阿拉木图",
+                "clock/city-amsterdam": "阿姆斯特丹",
+                "clock/city-anchorage": "安克雷奇",
+                "clock/city-athens": "雅典",
+                "clock/city-auckland": "奥克兰",
+                "clock/city-bangkok": "曼谷",
+                "clock/city-beijing": "北京",
+                "clock/city-berlin": "柏林",
+                "clock/city-brisbane": "布里斯班",
+                "clock/city-brussels": "布鲁塞尔",
+                "clock/city-buenos-aires": "布宜诺斯艾利斯",
+                "clock/city-cairo": "开罗",
+                "clock/city-casablanca": "卡萨布兰卡",
+                "clock/city-chicago": "芝加哥",
+                "clock/city-copenhagen": "哥本哈根",
+                "clock/city-denver": "丹佛",
+                "clock/city-dubai": "迪拜",
+                "clock/city-dublin": "都柏林",
+                "clock/city-frankfurt": "法兰克福",
+                "clock/city-helsinki": "赫尔辛基",
+                "clock/city-ho-chi-minh-city": "胡志明市",
+                "clock/city-hong-kong": "香港",
+                "clock/city-honolulu": "火奴鲁鲁",
+                "clock/city-istanbul": "伊斯坦布尔",
+                "clock/city-jakarta": "雅加达",
+                "clock/city-johannesburg": "约翰内斯堡",
+                "clock/city-karachi": "卡拉奇",
+                "clock/city-kathmandu": "加德满都",
+                "clock/city-kolkata": "加尔各答",
+                "clock/city-kuala-lumpur": "吉隆坡",
+                "clock/city-lagos": "拉各斯",
+                "clock/city-lima": "利马",
+                "clock/city-lisbon": "里斯本",
+                "clock/city-london": "伦敦",
+                "clock/city-los-angeles": "洛杉矶",
+                "clock/city-madrid": "马德里",
+                "clock/city-manila": "马尼拉",
+                "clock/city-melbourne": "墨尔本",
+                "clock/city-mexico-city": "墨西哥城",
+                "clock/city-moscow": "莫斯科",
+                "clock/city-mumbai": "孟买",
+                "clock/city-nairobi": "内罗毕",
+                "clock/city-new-delhi": "新德里",
+                "clock/city-new-york": "纽约",
+                "clock/city-oslo": "奥斯陆",
+                "clock/city-paris": "巴黎",
+                "clock/city-perth": "珀斯",
+                "clock/city-phoenix": "菲尼克斯",
+                "clock/city-prague": "布拉格",
+                "clock/city-riyadh": "利雅得",
+                "clock/city-rome": "罗马",
+                "clock/city-san-francisco": "旧金山",
+                "clock/city-santiago": "圣地亚哥",
+                "clock/city-sao-paulo": "圣保罗",
+                "clock/city-seoul": "首尔",
+                "clock/city-shanghai": "上海",
+                "clock/city-singapore": "新加坡",
+                "clock/city-stockholm": "斯德哥尔摩",
+                "clock/city-sydney": "悉尼",
+                "clock/city-taipei": "台北",
+                "clock/city-tehran": "德黑兰",
+                "clock/city-tel-aviv": "特拉维夫",
+                "clock/city-tokyo": "东京",
+                "clock/city-toronto": "多伦多",
+                "clock/city-vancouver": "温哥华",
+                "clock/city-vienna": "维也纳",
+                "clock/city-warsaw": "华沙",
+                "clock/city-wellington": "惠灵顿",
+                "clock/city-zurich": "苏黎世"
+            },
+            "titles": {
+                "Start": "开始",
+                "Pause": "暂停",
+                "Reset": "重置",
+                "Start / Pause": "开始 / 暂停",
+                "Toggle dark / light theme": "切换深色 / 浅色主题",
+                "Remove": "删除"
+            },
+            "placeholder": {
+                "Search city or region…": "搜索城市或地区…"
+            }
+        },
+        "zh-tw": {
+            "name": "繁體中文(台灣)",
+            "fontFamily": "\"Microsoft JhengHei\", \"Apple LiGothic Medium\", \"STHeiti\", sans-serif",
+            "strings": {
+                "clock/tab-clock": "時鐘",
+                "clock/tab-world": "世界",
+                "clock/tab-countdown": "倒數計時",
+                "clock/tab-stopwatch": "碼錶",
+                "clock/other-clocks": "其他時鐘",
+                "clock/add-city": "新增城市",
+                "clock/no-clocks-empty": "尚無時鐘。<br>點擊 <b>+ 新增城市</b> 開始。",
+                "clock/cd-hours": "小時",
+                "clock/cd-minutes": "分鐘",
+                "clock/cd-seconds": "秒",
+                "clock/tz-sheet-title": "選擇城市",
+                "clock/status-ready": "就緒",
+                "clock/status-running": "執行中",
+                "clock/status-paused": "已暫停",
+                "clock/no-results": "無結果",
+                "clock/wintitle-main": "時鐘",
+                "clock/wintitle-world": "世界時鐘",
+                "clock/wintitle-countdown": "倒數計時",
+                "clock/wintitle-stopwatch": "碼錶",
+                "clock/wintitle-countdown-paused": "倒數計時(已暫停)",
+                "clock/wintitle-times-up": "⏰ 時間到!",
+                "clock/wintitle-stopwatch-paused": "碼錶(已暫停)",
+                "clock/btn-remove": "移除",
+                "clock/day-suffix": "日",
+                "clock/region-asia": "亞洲",
+                "clock/region-america": "美洲",
+                "clock/region-americas": "美洲",
+                "clock/region-europe": "歐洲",
+                "clock/region-africa": "非洲",
+                "clock/region-australia": "澳洲",
+                "clock/region-pacific": "太平洋",
+                "clock/region-utc": "UTC",
+                "clock/region-indian": "印度洋",
+                "clock/region-atlantic": "大西洋",
+                "clock/region-arctic": "北極",
+                "clock/city-abu-dhabi": "阿布達比",
+                "clock/city-adelaide": "阿得雷德",
+                "clock/city-almaty": "阿拉木圖",
+                "clock/city-amsterdam": "阿姆斯特丹",
+                "clock/city-anchorage": "安克拉治",
+                "clock/city-athens": "雅典",
+                "clock/city-auckland": "奧克蘭",
+                "clock/city-bangkok": "曼谷",
+                "clock/city-beijing": "北京",
+                "clock/city-berlin": "柏林",
+                "clock/city-brisbane": "布里斯本",
+                "clock/city-brussels": "布魯塞爾",
+                "clock/city-buenos-aires": "布宜諾斯艾利斯",
+                "clock/city-cairo": "開羅",
+                "clock/city-casablanca": "卡薩布蘭卡",
+                "clock/city-chicago": "芝加哥",
+                "clock/city-copenhagen": "哥本哈根",
+                "clock/city-denver": "丹佛",
+                "clock/city-dubai": "杜拜",
+                "clock/city-dublin": "都柏林",
+                "clock/city-frankfurt": "法蘭克福",
+                "clock/city-helsinki": "赫爾辛基",
+                "clock/city-ho-chi-minh-city": "胡志明市",
+                "clock/city-hong-kong": "香港",
+                "clock/city-honolulu": "檀香山",
+                "clock/city-istanbul": "伊斯坦堡",
+                "clock/city-jakarta": "雅加達",
+                "clock/city-johannesburg": "約翰尼斯堡",
+                "clock/city-karachi": "喀拉蚩",
+                "clock/city-kathmandu": "加德滿都",
+                "clock/city-kolkata": "加爾各答",
+                "clock/city-kuala-lumpur": "吉隆坡",
+                "clock/city-lagos": "拉哥斯",
+                "clock/city-lima": "利馬",
+                "clock/city-lisbon": "里斯本",
+                "clock/city-london": "倫敦",
+                "clock/city-los-angeles": "洛杉磯",
+                "clock/city-madrid": "馬德里",
+                "clock/city-manila": "馬尼拉",
+                "clock/city-melbourne": "墨爾本",
+                "clock/city-mexico-city": "墨西哥城",
+                "clock/city-moscow": "莫斯科",
+                "clock/city-mumbai": "孟買",
+                "clock/city-nairobi": "奈洛比",
+                "clock/city-new-delhi": "新德里",
+                "clock/city-new-york": "紐約",
+                "clock/city-oslo": "奧斯陸",
+                "clock/city-paris": "巴黎",
+                "clock/city-perth": "伯斯",
+                "clock/city-phoenix": "鳳凰城",
+                "clock/city-prague": "布拉格",
+                "clock/city-riyadh": "利雅德",
+                "clock/city-rome": "羅馬",
+                "clock/city-san-francisco": "舊金山",
+                "clock/city-santiago": "聖地牙哥",
+                "clock/city-sao-paulo": "聖保羅",
+                "clock/city-seoul": "首爾",
+                "clock/city-shanghai": "上海",
+                "clock/city-singapore": "新加坡",
+                "clock/city-stockholm": "斯德哥爾摩",
+                "clock/city-sydney": "雪梨",
+                "clock/city-taipei": "台北",
+                "clock/city-tehran": "德黑蘭",
+                "clock/city-tel-aviv": "特拉維夫",
+                "clock/city-tokyo": "東京",
+                "clock/city-toronto": "多倫多",
+                "clock/city-vancouver": "溫哥華",
+                "clock/city-vienna": "維也納",
+                "clock/city-warsaw": "華沙",
+                "clock/city-wellington": "威靈頓",
+                "clock/city-zurich": "蘇黎世"
+            },
+            "titles": {
+                "Start": "開始",
+                "Pause": "暫停",
+                "Reset": "重置",
+                "Start / Pause": "開始 / 暫停",
+                "Toggle dark / light theme": "切換深色 / 淺色主題",
+                "Remove": "移除"
+            },
+            "placeholder": {
+                "Search city or region…": "搜尋城市或地區…"
+            }
+        },
+        "zh-hk": {
+            "name": "繁體中文(香港)",
+            "fontFamily": "\"Microsoft JhengHei\", \"Apple LiGothic Medium\", \"STHeiti\", sans-serif",
+            "strings": {
+                "clock/tab-clock": "時鐘",
+                "clock/tab-world": "世界",
+                "clock/tab-countdown": "倒數計時",
+                "clock/tab-stopwatch": "秒錶",
+                "clock/other-clocks": "其他時鐘",
+                "clock/add-city": "新增城市",
+                "clock/no-clocks-empty": "暫時冇時鐘。<br>點擊 <b>+ 新增城市</b> 開始。",
+                "clock/cd-hours": "小時",
+                "clock/cd-minutes": "分鐘",
+                "clock/cd-seconds": "秒",
+                "clock/tz-sheet-title": "選擇城市",
+                "clock/status-ready": "就緒",
+                "clock/status-running": "運行中",
+                "clock/status-paused": "已暫停",
+                "clock/no-results": "無結果",
+                "clock/wintitle-main": "時鐘",
+                "clock/wintitle-world": "世界時鐘",
+                "clock/wintitle-countdown": "倒數計時",
+                "clock/wintitle-stopwatch": "秒錶",
+                "clock/wintitle-countdown-paused": "倒數計時(已暫停)",
+                "clock/wintitle-times-up": "⏰ 時間到!",
+                "clock/wintitle-stopwatch-paused": "秒錶(已暫停)",
+                "clock/btn-remove": "移除",
+                "clock/day-suffix": "日",
+                "clock/region-asia": "亞洲",
+                "clock/region-america": "美洲",
+                "clock/region-americas": "美洲",
+                "clock/region-europe": "歐洲",
+                "clock/region-africa": "非洲",
+                "clock/region-australia": "澳洲",
+                "clock/region-pacific": "太平洋",
+                "clock/region-utc": "UTC",
+                "clock/region-indian": "印度洋",
+                "clock/region-atlantic": "大西洋",
+                "clock/region-arctic": "北極",
+                "clock/city-abu-dhabi": "阿布達比",
+                "clock/city-adelaide": "阿德雷德",
+                "clock/city-almaty": "阿拉木圖",
+                "clock/city-amsterdam": "阿姆斯特丹",
+                "clock/city-anchorage": "安克雷奇",
+                "clock/city-athens": "雅典",
+                "clock/city-auckland": "奧克蘭",
+                "clock/city-bangkok": "曼谷",
+                "clock/city-beijing": "北京",
+                "clock/city-berlin": "柏林",
+                "clock/city-brisbane": "布里斯本",
+                "clock/city-brussels": "布魯塞爾",
+                "clock/city-buenos-aires": "布宜諾斯艾利斯",
+                "clock/city-cairo": "開羅",
+                "clock/city-casablanca": "卡薩布蘭卡",
+                "clock/city-chicago": "芝加哥",
+                "clock/city-copenhagen": "哥本哈根",
+                "clock/city-denver": "丹佛",
+                "clock/city-dubai": "杜拜",
+                "clock/city-dublin": "都柏林",
+                "clock/city-frankfurt": "法蘭克福",
+                "clock/city-helsinki": "赫爾辛基",
+                "clock/city-ho-chi-minh-city": "胡志明市",
+                "clock/city-hong-kong": "香港",
+                "clock/city-honolulu": "火奴魯魯",
+                "clock/city-istanbul": "伊斯坦堡",
+                "clock/city-jakarta": "雅加達",
+                "clock/city-johannesburg": "約翰尼斯堡",
+                "clock/city-karachi": "卡拉奇",
+                "clock/city-kathmandu": "加德滿都",
+                "clock/city-kolkata": "加爾各答",
+                "clock/city-kuala-lumpur": "吉隆坡",
+                "clock/city-lagos": "拉各斯",
+                "clock/city-lima": "利馬",
+                "clock/city-lisbon": "里斯本",
+                "clock/city-london": "倫敦",
+                "clock/city-los-angeles": "洛杉磯",
+                "clock/city-madrid": "馬德里",
+                "clock/city-manila": "馬尼拉",
+                "clock/city-melbourne": "墨爾本",
+                "clock/city-mexico-city": "墨西哥城",
+                "clock/city-moscow": "莫斯科",
+                "clock/city-mumbai": "孟買",
+                "clock/city-nairobi": "奈洛比",
+                "clock/city-new-delhi": "新德里",
+                "clock/city-new-york": "紐約",
+                "clock/city-oslo": "奧斯陸",
+                "clock/city-paris": "巴黎",
+                "clock/city-perth": "柏斯",
+                "clock/city-phoenix": "鳳凰城",
+                "clock/city-prague": "布拉格",
+                "clock/city-riyadh": "利雅德",
+                "clock/city-rome": "羅馬",
+                "clock/city-san-francisco": "三藩市",
+                "clock/city-santiago": "聖地牙哥",
+                "clock/city-sao-paulo": "聖保羅",
+                "clock/city-seoul": "首爾",
+                "clock/city-shanghai": "上海",
+                "clock/city-singapore": "新加坡",
+                "clock/city-stockholm": "斯德哥爾摩",
+                "clock/city-sydney": "悉尼",
+                "clock/city-taipei": "台北",
+                "clock/city-tehran": "德黑蘭",
+                "clock/city-tel-aviv": "特拉維夫",
+                "clock/city-tokyo": "東京",
+                "clock/city-toronto": "多倫多",
+                "clock/city-vancouver": "溫哥華",
+                "clock/city-vienna": "維也納",
+                "clock/city-warsaw": "華沙",
+                "clock/city-wellington": "威靈頓",
+                "clock/city-zurich": "蘇黎世"
+            },
+            "titles": {
+                "Start": "開始",
+                "Pause": "暫停",
+                "Reset": "重置",
+                "Start / Pause": "開始 / 暫停",
+                "Toggle dark / light theme": "切換深色 / 淺色主題",
+                "Remove": "移除"
+            },
+            "placeholder": {
+                "Search city or region…": "搜尋城市或地區…"
+            }
+        },
+        "ja-jp": {
+            "name": "日本語",
+            "fontFamily": "\"Hiragino Kaku Gothic Pro\", \"Meiryo\", \"MS Gothic\", sans-serif",
+            "strings": {
+                "clock/tab-clock": "時計",
+                "clock/tab-world": "世界",
+                "clock/tab-countdown": "カウントダウン",
+                "clock/tab-stopwatch": "ストップウォッチ",
+                "clock/other-clocks": "その他の時計",
+                "clock/add-city": "都市を追加",
+                "clock/no-clocks-empty": "時計がありません。<br><b>+ 都市を追加</b> をタップして開始してください。",
+                "clock/cd-hours": "時間",
+                "clock/cd-minutes": "分",
+                "clock/cd-seconds": "秒",
+                "clock/tz-sheet-title": "都市を選択",
+                "clock/status-ready": "準備完了",
+                "clock/status-running": "実行中",
+                "clock/status-paused": "一時停止中",
+                "clock/no-results": "結果なし",
+                "clock/wintitle-main": "時計",
+                "clock/wintitle-world": "世界時計",
+                "clock/wintitle-countdown": "カウントダウン",
+                "clock/wintitle-stopwatch": "ストップウォッチ",
+                "clock/wintitle-countdown-paused": "カウントダウン(一時停止中)",
+                "clock/wintitle-times-up": "⏰ 時間切れ!",
+                "clock/wintitle-stopwatch-paused": "ストップウォッチ(一時停止中)",
+                "clock/btn-remove": "削除",
+                "clock/day-suffix": "日",
+                "clock/region-asia": "アジア",
+                "clock/region-america": "アメリカ",
+                "clock/region-americas": "南北アメリカ",
+                "clock/region-europe": "ヨーロッパ",
+                "clock/region-africa": "アフリカ",
+                "clock/region-australia": "オセアニア",
+                "clock/region-pacific": "太平洋",
+                "clock/region-utc": "UTC",
+                "clock/region-indian": "インド洋",
+                "clock/region-atlantic": "大西洋",
+                "clock/region-arctic": "北極",
+                "clock/city-abu-dhabi": "アブダビ",
+                "clock/city-adelaide": "アデレード",
+                "clock/city-almaty": "アルマティ",
+                "clock/city-amsterdam": "アムステルダム",
+                "clock/city-anchorage": "アンカレッジ",
+                "clock/city-athens": "アテネ",
+                "clock/city-auckland": "オークランド",
+                "clock/city-bangkok": "バンコク",
+                "clock/city-beijing": "北京",
+                "clock/city-berlin": "ベルリン",
+                "clock/city-brisbane": "ブリスベン",
+                "clock/city-brussels": "ブリュッセル",
+                "clock/city-buenos-aires": "ブエノスアイレス",
+                "clock/city-cairo": "カイロ",
+                "clock/city-casablanca": "カサブランカ",
+                "clock/city-chicago": "シカゴ",
+                "clock/city-copenhagen": "コペンハーゲン",
+                "clock/city-denver": "デンバー",
+                "clock/city-dubai": "ドバイ",
+                "clock/city-dublin": "ダブリン",
+                "clock/city-frankfurt": "フランクフルト",
+                "clock/city-helsinki": "ヘルシンキ",
+                "clock/city-ho-chi-minh-city": "ホーチミン",
+                "clock/city-hong-kong": "香港",
+                "clock/city-honolulu": "ホノルル",
+                "clock/city-istanbul": "イスタンブール",
+                "clock/city-jakarta": "ジャカルタ",
+                "clock/city-johannesburg": "ヨハネスブルク",
+                "clock/city-karachi": "カラチ",
+                "clock/city-kathmandu": "カトマンズ",
+                "clock/city-kolkata": "コルカタ",
+                "clock/city-kuala-lumpur": "クアラルンプール",
+                "clock/city-lagos": "ラゴス",
+                "clock/city-lima": "リマ",
+                "clock/city-lisbon": "リスボン",
+                "clock/city-london": "ロンドン",
+                "clock/city-los-angeles": "ロサンゼルス",
+                "clock/city-madrid": "マドリード",
+                "clock/city-manila": "マニラ",
+                "clock/city-melbourne": "メルボルン",
+                "clock/city-mexico-city": "メキシコシティ",
+                "clock/city-moscow": "モスクワ",
+                "clock/city-mumbai": "ムンバイ",
+                "clock/city-nairobi": "ナイロビ",
+                "clock/city-new-delhi": "ニューデリー",
+                "clock/city-new-york": "ニューヨーク",
+                "clock/city-oslo": "オスロ",
+                "clock/city-paris": "パリ",
+                "clock/city-perth": "パース",
+                "clock/city-phoenix": "フェニックス",
+                "clock/city-prague": "プラハ",
+                "clock/city-riyadh": "リヤド",
+                "clock/city-rome": "ローマ",
+                "clock/city-san-francisco": "サンフランシスコ",
+                "clock/city-santiago": "サンティアゴ",
+                "clock/city-sao-paulo": "サンパウロ",
+                "clock/city-seoul": "ソウル",
+                "clock/city-shanghai": "上海",
+                "clock/city-singapore": "シンガポール",
+                "clock/city-stockholm": "ストックホルム",
+                "clock/city-sydney": "シドニー",
+                "clock/city-taipei": "台北",
+                "clock/city-tehran": "テヘラン",
+                "clock/city-tel-aviv": "テルアビブ",
+                "clock/city-tokyo": "東京",
+                "clock/city-toronto": "トロント",
+                "clock/city-vancouver": "バンクーバー",
+                "clock/city-vienna": "ウィーン",
+                "clock/city-warsaw": "ワルシャワ",
+                "clock/city-wellington": "ウェリントン",
+                "clock/city-zurich": "チューリッヒ"
+            },
+            "titles": {
+                "Start": "開始",
+                "Pause": "一時停止",
+                "Reset": "リセット",
+                "Start / Pause": "開始 / 一時停止",
+                "Toggle dark / light theme": "ダーク / ライトテーマ切替",
+                "Remove": "削除"
+            },
+            "placeholder": {
+                "Search city or region…": "都市または地域を検索…"
+            }
+        },
+        "ko-kr": {
+            "name": "한국어",
+            "fontFamily": "\"Malgun Gothic\", \"Apple SD Gothic Neo\", sans-serif",
+            "strings": {
+                "clock/tab-clock": "시계",
+                "clock/tab-world": "세계",
+                "clock/tab-countdown": "카운트다운",
+                "clock/tab-stopwatch": "스톱워치",
+                "clock/other-clocks": "다른 시계",
+                "clock/add-city": "도시 추가",
+                "clock/no-clocks-empty": "시계가 없습니다.<br><b>+ 도시 추가</b>를 탭하여 시작하세요.",
+                "clock/cd-hours": "시간",
+                "clock/cd-minutes": "분",
+                "clock/cd-seconds": "초",
+                "clock/tz-sheet-title": "도시 선택",
+                "clock/status-ready": "준비됨",
+                "clock/status-running": "실행 중",
+                "clock/status-paused": "일시 정지됨",
+                "clock/no-results": "결과 없음",
+                "clock/wintitle-main": "시계",
+                "clock/wintitle-world": "세계 시계",
+                "clock/wintitle-countdown": "카운트다운",
+                "clock/wintitle-stopwatch": "스톱워치",
+                "clock/wintitle-countdown-paused": "카운트다운 (일시 정지됨)",
+                "clock/wintitle-times-up": "⏰ 시간 초과!",
+                "clock/wintitle-stopwatch-paused": "스톱워치 (일시 정지됨)",
+                "clock/btn-remove": "제거",
+                "clock/day-suffix": "일",
+                "clock/region-asia": "아시아",
+                "clock/region-america": "아메리카",
+                "clock/region-americas": "아메리카",
+                "clock/region-europe": "유럽",
+                "clock/region-africa": "아프리카",
+                "clock/region-australia": "오세아니아",
+                "clock/region-pacific": "태평양",
+                "clock/region-utc": "UTC",
+                "clock/region-indian": "인도양",
+                "clock/region-atlantic": "대서양",
+                "clock/region-arctic": "북극",
+                "clock/city-abu-dhabi": "아부다비",
+                "clock/city-adelaide": "애들레이드",
+                "clock/city-almaty": "알마티",
+                "clock/city-amsterdam": "암스테르담",
+                "clock/city-anchorage": "앵커리지",
+                "clock/city-athens": "아테네",
+                "clock/city-auckland": "오클랜드",
+                "clock/city-bangkok": "방콕",
+                "clock/city-beijing": "베이징",
+                "clock/city-berlin": "베를린",
+                "clock/city-brisbane": "브리즈번",
+                "clock/city-brussels": "브뤼셀",
+                "clock/city-buenos-aires": "부에노스아이레스",
+                "clock/city-cairo": "카이로",
+                "clock/city-casablanca": "카사블랑카",
+                "clock/city-chicago": "시카고",
+                "clock/city-copenhagen": "코펜하겐",
+                "clock/city-denver": "덴버",
+                "clock/city-dubai": "두바이",
+                "clock/city-dublin": "더블린",
+                "clock/city-frankfurt": "프랑크푸르트",
+                "clock/city-helsinki": "헬싱키",
+                "clock/city-ho-chi-minh-city": "호치민",
+                "clock/city-hong-kong": "홍콩",
+                "clock/city-honolulu": "호놀룰루",
+                "clock/city-istanbul": "이스탄불",
+                "clock/city-jakarta": "자카르타",
+                "clock/city-johannesburg": "요하네스버그",
+                "clock/city-karachi": "카라치",
+                "clock/city-kathmandu": "카트만두",
+                "clock/city-kolkata": "콜카타",
+                "clock/city-kuala-lumpur": "쿠알라룸푸르",
+                "clock/city-lagos": "라고스",
+                "clock/city-lima": "리마",
+                "clock/city-lisbon": "리스본",
+                "clock/city-london": "런던",
+                "clock/city-los-angeles": "로스앤젤레스",
+                "clock/city-madrid": "마드리드",
+                "clock/city-manila": "마닐라",
+                "clock/city-melbourne": "멜버른",
+                "clock/city-mexico-city": "멕시코시티",
+                "clock/city-moscow": "모스크바",
+                "clock/city-mumbai": "뭄바이",
+                "clock/city-nairobi": "나이로비",
+                "clock/city-new-delhi": "뉴델리",
+                "clock/city-new-york": "뉴욕",
+                "clock/city-oslo": "오슬로",
+                "clock/city-paris": "파리",
+                "clock/city-perth": "퍼스",
+                "clock/city-phoenix": "피닉스",
+                "clock/city-prague": "프라하",
+                "clock/city-riyadh": "리야드",
+                "clock/city-rome": "로마",
+                "clock/city-san-francisco": "샌프란시스코",
+                "clock/city-santiago": "산티아고",
+                "clock/city-sao-paulo": "상파울루",
+                "clock/city-seoul": "서울",
+                "clock/city-shanghai": "상하이",
+                "clock/city-singapore": "싱가포르",
+                "clock/city-stockholm": "스톡홀름",
+                "clock/city-sydney": "시드니",
+                "clock/city-taipei": "타이베이",
+                "clock/city-tehran": "테헤란",
+                "clock/city-tel-aviv": "텔아비브",
+                "clock/city-tokyo": "도쿄",
+                "clock/city-toronto": "토론토",
+                "clock/city-vancouver": "밴쿠버",
+                "clock/city-vienna": "빈",
+                "clock/city-warsaw": "바르샤바",
+                "clock/city-wellington": "웰링턴",
+                "clock/city-zurich": "취리히"
+            },
+            "titles": {
+                "Start": "시작",
+                "Pause": "일시 정지",
+                "Reset": "초기화",
+                "Start / Pause": "시작 / 일시 정지",
+                "Toggle dark / light theme": "다크 / 라이트 테마 전환",
+                "Remove": "제거"
+            },
+            "placeholder": {
+                "Search city or region…": "도시 또는 지역 검색…"
+            }
+        }
+    }
+}