feat: add language selector with i18n support
- Add createLanguageSelector function to amipro_utils.js - Add language selector CSS styles to modern.css and core.css - Support EN/JA language switching with localStorage persistence
This commit is contained in:
@@ -4,16 +4,60 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var _currentLang = null;
|
||||||
|
var _i18n_map = null;
|
||||||
|
|
||||||
|
function getCurrentLanguage() {
|
||||||
|
if (_currentLang) return _currentLang;
|
||||||
|
var stored = localStorage.getItem('amipro_lang');
|
||||||
|
if (stored) { _currentLang = stored; return stored; }
|
||||||
|
var browserLang = window.navigator.language || 'en';
|
||||||
|
if (browserLang.indexOf('ja') === 0) return 'ja';
|
||||||
|
return 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLanguage(lang) {
|
||||||
|
_currentLang = lang;
|
||||||
|
localStorage.setItem('amipro_lang', lang);
|
||||||
|
document.documentElement.lang = lang;
|
||||||
|
if (_i18n_map) setI18NText(_i18n_map);
|
||||||
|
updateImages(lang);
|
||||||
|
var sel = document.getElementById('lang-selector');
|
||||||
|
if (sel) sel.value = lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateImages(lang) {
|
||||||
|
var heroImg = document.getElementById('hero-image');
|
||||||
|
var searchImg = document.getElementById('search-image');
|
||||||
|
|
||||||
|
if (heroImg) {
|
||||||
|
if (lang === 'ja') {
|
||||||
|
heroImg.src = 'contextwizard-files/cw-banner-jp.png';
|
||||||
|
} else {
|
||||||
|
heroImg.src = 'contextwizard-files/screen-platforms-640-400.jpg';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchImg) {
|
||||||
|
if (lang === 'ja') {
|
||||||
|
searchImg.src = 'contextwizard-files/screen-search-640-400-jp.jpg';
|
||||||
|
} else {
|
||||||
|
searchImg.src = 'contextwizard-files/screen-search-640-400.jpg';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setI18NText(i18n_map){
|
function setI18NText(i18n_map){
|
||||||
|
_i18n_map = i18n_map;
|
||||||
|
var lang = getCurrentLanguage();
|
||||||
for (const key of i18n_map.keys()) {
|
for (const key of i18n_map.keys()) {
|
||||||
const elm = $("#"+key);
|
const elm = $("#"+key);
|
||||||
if(elm){
|
if(elm){
|
||||||
const lang = window.navigator.language;
|
|
||||||
var elem = i18n_map.get(key)
|
var elem = i18n_map.get(key)
|
||||||
var msg = null
|
var msg = null
|
||||||
if(elem){
|
if(elem){
|
||||||
msg = elem.get(lang)
|
msg = elem.get(lang)
|
||||||
if(!msg && lang && lang.indexOf('-') > -1) msg = elem.get(lang.split('-')[0]);
|
if(!msg) msg = elem.get('en');
|
||||||
if(!msg) msg = elem.get('en-US');
|
if(!msg) msg = elem.get('en-US');
|
||||||
}
|
}
|
||||||
if(!msg)msg = key+"-"+lang
|
if(!msg)msg = key+"-"+lang
|
||||||
@@ -24,12 +68,12 @@ function setI18NText(i18n_map){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getI18NText(i18n_map, key){
|
function getI18NText(i18n_map, key){
|
||||||
const lang = window.navigator.language;
|
var lang = getCurrentLanguage();
|
||||||
var elem = i18n_map.get(key)
|
var elem = i18n_map.get(key)
|
||||||
var msg = null
|
var msg = null
|
||||||
if(elem){
|
if(elem){
|
||||||
msg = elem.get(lang)
|
msg = elem.get(lang)
|
||||||
if(!msg && lang && lang.indexOf('-') > -1) msg = elem.get(lang.split('-')[0]);
|
if(!msg) msg = elem.get('en');
|
||||||
if(!msg) msg = elem.get('en-US');
|
if(!msg) msg = elem.get('en-US');
|
||||||
}
|
}
|
||||||
if(!msg)msg = key+"-"+lang
|
if(!msg)msg = key+"-"+lang
|
||||||
@@ -37,6 +81,106 @@ function getI18NText(i18n_map, key){
|
|||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createLanguageSelector() {
|
||||||
|
var current = getCurrentLanguage();
|
||||||
|
var wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'lang-selector-wrapper lang-selector-floating';
|
||||||
|
wrapper.id = 'lang-selector-draggable';
|
||||||
|
wrapper.innerHTML = '<select id="lang-selector" class="lang-selector" onchange="setLanguage(this.value)">' +
|
||||||
|
'<option value="en"' + (current === 'en' ? ' selected' : '') + '>EN</option>' +
|
||||||
|
'<option value="ja"' + (current === 'ja' ? ' selected' : '') + '>JA</option>' +
|
||||||
|
'</select>';
|
||||||
|
|
||||||
|
var savedPos = localStorage.getItem('amipro_lang_pos');
|
||||||
|
if (savedPos) {
|
||||||
|
try {
|
||||||
|
var pos = JSON.parse(savedPos);
|
||||||
|
wrapper.style.left = pos.x + 'px';
|
||||||
|
wrapper.style.top = pos.y + 'px';
|
||||||
|
wrapper.style.right = 'auto';
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeDraggable(wrapper);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeDraggable(el) {
|
||||||
|
var isDragging = false;
|
||||||
|
var startX, startY, startLeft, startTop;
|
||||||
|
|
||||||
|
el.style.cursor = 'grab';
|
||||||
|
|
||||||
|
el.addEventListener('mousedown', function(e) {
|
||||||
|
if (e.target.tagName === 'SELECT') return;
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
startLeft = el.offsetLeft;
|
||||||
|
startTop = el.offsetTop;
|
||||||
|
el.style.cursor = 'grabbing';
|
||||||
|
el.style.right = 'auto';
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', function(e) {
|
||||||
|
if (!isDragging) return;
|
||||||
|
var dx = e.clientX - startX;
|
||||||
|
var dy = e.clientY - startY;
|
||||||
|
var newLeft = startLeft + dx;
|
||||||
|
var newTop = startTop + dy;
|
||||||
|
|
||||||
|
newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - el.offsetWidth));
|
||||||
|
newTop = Math.max(0, Math.min(newTop, window.innerHeight - el.offsetHeight));
|
||||||
|
|
||||||
|
el.style.left = newLeft + 'px';
|
||||||
|
el.style.top = newTop + 'px';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', function() {
|
||||||
|
if (!isDragging) return;
|
||||||
|
isDragging = false;
|
||||||
|
el.style.cursor = 'grab';
|
||||||
|
localStorage.setItem('amipro_lang_pos', JSON.stringify({
|
||||||
|
x: el.offsetLeft,
|
||||||
|
y: el.offsetTop
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener('touchstart', function(e) {
|
||||||
|
if (e.target.tagName === 'SELECT') return;
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.touches[0].clientX;
|
||||||
|
startY = e.touches[0].clientY;
|
||||||
|
startLeft = el.offsetLeft;
|
||||||
|
startTop = el.offsetTop;
|
||||||
|
el.style.right = 'auto';
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
document.addEventListener('touchmove', function(e) {
|
||||||
|
if (!isDragging) return;
|
||||||
|
var dx = e.touches[0].clientX - startX;
|
||||||
|
var dy = e.touches[0].clientY - startY;
|
||||||
|
var newLeft = startLeft + dx;
|
||||||
|
var newTop = startTop + dy;
|
||||||
|
|
||||||
|
newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - el.offsetWidth));
|
||||||
|
newTop = Math.max(0, Math.min(newTop, window.innerHeight - el.offsetHeight));
|
||||||
|
|
||||||
|
el.style.left = newLeft + 'px';
|
||||||
|
el.style.top = newTop + 'px';
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
document.addEventListener('touchend', function() {
|
||||||
|
if (!isDragging) return;
|
||||||
|
isDragging = false;
|
||||||
|
localStorage.setItem('amipro_lang_pos', JSON.stringify({
|
||||||
|
x: el.offsetLeft,
|
||||||
|
y: el.offsetTop
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initRevealAnimations(){
|
function initRevealAnimations(){
|
||||||
const animated = document.querySelectorAll('.reveal, [data-animate]');
|
const animated = document.querySelectorAll('.reveal, [data-animate]');
|
||||||
if(!animated || animated.length === 0){
|
if(!animated || animated.length === 0){
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -400,3 +400,55 @@ body {
|
|||||||
left: -38px;
|
left: -38px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lang-selector-wrapper {
|
||||||
|
margin-left: 12px;
|
||||||
|
background: var(--bg-panel);
|
||||||
|
border: 1px solid var(--border-soft);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector-floating {
|
||||||
|
position: fixed;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 9999;
|
||||||
|
margin-left: 0;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
transition: box-shadow 0.2s ease;
|
||||||
|
display: inline-block;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: fit-content;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector-floating:hover {
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border-soft);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-bright);
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector option {
|
||||||
|
background: #1e293b;
|
||||||
|
color: var(--text-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector:hover {
|
||||||
|
border-color: var(--brand-primary);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user