多语言和本地化
在全球化的电商环境中,多语言支持是必不可少的功能。Shopify 提供了强大的多语言和本地化功能,让您能够为不同地区的客户提供本地化的购物体验。
基础多语言设置
语言检测
<!-- 当前语言信息 -->
<div class="language-info" data-current-locale="{{ request.locale.iso_code }}">
<p>当前语言: {{ request.locale.name }}</p>
<p>语言代码: {{ request.locale.iso_code }}</p>
<p>根URL: {{ request.locale.root_url }}</p>
</div>
<!-- 语言切换器 -->
<div class="language-switcher">
<label for="language-select">选择语言:</label>
<select id="language-select" onchange="switchLanguage(this.value)">
{% for locale in shop.published_locales %}
<option value="{{ locale.iso_code }}"
{% if locale.iso_code == request.locale.iso_code %}selected{% endif %}>
{{ locale.name }}
</option>
{% endfor %}
</select>
</div>
<script>
function switchLanguage(localeCode) {
const currentUrl = window.location.pathname + window.location.search
window.location.href = `/${localeCode}${currentUrl}`
}
</script>
文本翻译
<!-- 基本文本翻译 -->
<div class="site-navigation">
<a href="{{ routes.root_url }}">{{ 'general.navigation.home' | t }}</a>
<a href="{{ routes.all_products_collection_url }}">{{ 'general.navigation.catalog' | t }}</a>
<a href="{{ routes.cart_url }}">
{{ 'general.navigation.cart' | t }} ({{ cart.item_count }})
</a>
<a href="/pages/about">{{ 'general.navigation.about' | t }}</a>
<a href="/pages/contact">{{ 'general.navigation.contact' | t }}</a>
</div>
<!-- 带参数的翻译 -->
<div class="product-price">
{% assign discount_percentage = product.compare_at_price | minus: product.price | times: 100 | divided_by: product.compare_at_price %}
<span class="original-price">
{{ 'products.product.original_price' | t: price: product.compare_at_price | money }}
</span>
<span class="sale-price">
{{ 'products.product.sale_price' | t: price: product.price | money }}
</span>
<span class="discount">
{{ 'products.product.save_percentage' | t: percentage: discount_percentage }}
</span>
</div>
货币本地化
货币格式
<!-- 货币显示 -->
<div class="currency-display">
<div class="price-primary">
{{ product.price | money_with_currency }}
</div>
<div class="price-local">
{{ product.price | money: request.locale.currency.symbol }}
</div>
<!-- 多货币支持 -->
{% if shop.enabled_currencies.size > 1 %}
<div class="currency-selector">
<select onchange="changeCurrency(this.value)">
{% for currency in shop.enabled_currencies %}
<option value="{{ currency.iso_code }}"
{% if currency.iso_code == cart.currency.iso_code %}selected{% endif %}>
{{ currency.name }} ({{ currency.symbol }})
</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
<script>
function changeCurrency(currencyCode) {
fetch('/cart/update.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
currency: currencyCode
})
}).then(() => {
location.reload()
})
}
</script>
价格格式化
<!-- 自定义价格格式 -->
{% capture price_format %}
{% case request.locale.iso_code %}
{% when 'zh-CN' %}
¥{{ price | divided_by: 100.0 | round: 2 }}
{% when 'ja' %}
¥{{ price | divided_by: 100.0 | round: 0 }}
{% when 'en-US' %}
${{ price | divided_by: 100.0 | round: 2 }}
{% when 'en-GB' %}
£{{ price | divided_by: 100.0 | round: 2 }}
{% else %}
{{ price | money }}
{% endcase %}
{% endcapture %}
<div class="localized-price">{{ price_format }}</div>
日期和时间本地化
日期格式
<!-- 本地化日期显示 -->
<div class="date-display">
{% case request.locale.iso_code %}
{% when 'zh-CN' %}
<time>{{ article.published_at | date: '%Y年%m月%d日' }}</time>
{% when 'en-US' %}
<time>{{ article.published_at | date: '%B %d, %Y' }}</time>
{% when 'en-GB' %}
<time>{{ article.published_at | date: '%d %B %Y' }}</time>
{% when 'ja' %}
<time>{{ article.published_at | date: '%Y年%m月%d日' }}</time>
{% else %}
<time>{{ article.published_at | date: '%Y-%m-%d' }}</time>
{% endcase %}
</div>
<!-- 相对时间 -->
<div class="relative-time">
{% assign days_ago = 'now' | date: '%s' | minus: article.published_at | date: '%s' | divided_by: 86400 %}
{% if days_ago == 0 %}
{{ 'general.date.today' | t }}
{% elsif days_ago == 1 %}
{{ 'general.date.yesterday' | t }}
{% elsif days_ago < 7 %}
{{ 'general.date.days_ago' | t: count: days_ago }}
{% else %}
{{ article.published_at | date: '%Y-%m-%d' }}
{% endif %}
</div>
地址格式化
本地化地址
<!-- 地址格式本地化 -->
<address class="localized-address">
{% case request.locale.iso_code %}
{% when 'zh-CN' %}
<!-- 中文地址格式 -->
<div class="address-country">{{ address.country }}</div>
<div class="address-province">{{ address.province }}</div>
<div class="address-city">{{ address.city }}</div>
<div class="address-street">{{ address.address1 }}</div>
{% if address.address2 %}
<div class="address-street2">{{ address.address2 }}</div>
{% endif %}
<div class="address-zip">{{ address.zip }}</div>
<div class="address-name">{{ address.first_name }} {{ address.last_name }}</div>
{% when 'ja' %}
<!-- 日文地址格式 -->
<div class="address-zip">〒{{ address.zip }}</div>
<div class="address-country">{{ address.country }}</div>
<div class="address-province">{{ address.province }}</div>
<div class="address-city">{{ address.city }}</div>
<div class="address-street">{{ address.address1 }}</div>
{% if address.address2 %}
<div class="address-street2">{{ address.address2 }}</div>
{% endif %}
<div class="address-name">{{ address.last_name }} {{ address.first_name }}</div>
{% else %}
<!-- 西方地址格式 -->
<div class="address-name">{{ address.first_name }} {{ address.last_name }}</div>
<div class="address-street">{{ address.address1 }}</div>
{% if address.address2 %}
<div class="address-street2">{{ address.address2 }}</div>
{% endif %}
<div class="address-city-state">
{{ address.city }}, {{ address.province }} {{ address.zip }}
</div>
<div class="address-country">{{ address.country }}</div>
{% endcase %}
</address>
内容本地化
产品信息本地化
<!-- 本地化产品内容 -->
<div class="product-info">
<h1>{{ product.title }}</h1>
<!-- 本地化描述 -->
{% assign localized_description = product.metafields.translations[request.locale.iso_code].description %}
{% if localized_description %}
<div class="product-description">{{ localized_description }}</div>
{% else %}
<div class="product-description">{{ product.description }}</div>
{% endif %}
<!-- 本地化特性列表 -->
{% assign features_key = 'features_' | append: request.locale.iso_code %}
{% assign localized_features = product.metafields.custom[features_key] %}
{% if localized_features %}
<div class="product-features">
<h3>{{ 'products.product.features' | t }}</h3>
<ul>
{% for feature in localized_features %}
<li>{{ feature }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
集合本地化
<!-- 本地化集合信息 -->
<div class="collection-header">
{% assign localized_title = collection.metafields.translations[request.locale.iso_code].title %}
<h1>{% if localized_title %}{{ localized_title }}{% else %}{{ collection.title }}{% endif %}</h1>
{% assign localized_description = collection.metafields.translations[request.locale.iso_code].description %}
{% if localized_description %}
<div class="collection-description">{{ localized_description }}</div>
{% elsif collection.description %}
<div class="collection-description">{{ collection.description }}</div>
{% endif %}
</div>
SEO 本地化
hreflang 标签
<!-- hreflang 实现 -->
<head>
{% for locale in shop.published_locales %}
<link rel="alternate"
hreflang="{{ locale.iso_code }}"
href="{{ locale.root_url }}{{ request.path }}">
{% endfor %}
<!-- 默认语言 -->
<link rel="alternate"
hreflang="x-default"
href="{{ shop.primary_locale.root_url }}{{ request.path }}">
</head>
本地化 meta 标签
<!-- 本地化 SEO 标签 -->
<head>
{% assign page_title_key = 'meta.title.' | append: template %}
{% assign page_description_key = 'meta.description.' | append: template %}
<title>
{% if page_title_key != blank %}
{{ page_title_key | t: shop_name: shop.name, page_title: page_title }}
{% else %}
{{ page_title }}{% unless page_title contains shop.name %} - {{ shop.name }}{% endunless %}
{% endif %}
</title>
<meta name="description" content="{{ page_description_key | t | default: page_description | truncate: 160 }}">
<!-- Open Graph 本地化 -->
<meta property="og:locale" content="{{ request.locale.iso_code | replace: '-', '_' }}">
{% for locale in shop.published_locales %}
{% unless locale.iso_code == request.locale.iso_code %}
<meta property="og:locale:alternate" content="{{ locale.iso_code | replace: '-', '_' }}">
{% endunless %}
{% endfor %}
</head>
RTL 支持
右到左语言支持
<!-- RTL 语言支持 -->
{% assign rtl_languages = 'ar,he,fa,ur' | split: ',' %}
{% assign is_rtl = false %}
{% for rtl_lang in rtl_languages %}
{% if request.locale.iso_code contains rtl_lang %}
{% assign is_rtl = true %}
{% break %}
{% endif %}
{% endfor %}
<html dir="{% if is_rtl %}rtl{% else %}ltr{% endif %}" lang="{{ request.locale.iso_code }}">
<head>
<style>
{% if is_rtl %}
body {
direction: rtl;
text-align: right;
}
.container {
padding-right: 20px;
padding-left: 20px;
}
.navigation {
float: right;
}
.breadcrumbs {
direction: rtl;
}
.breadcrumbs::before {
content: "←";
margin-left: 5px;
margin-right: 0;
}
{% endif %}
</style>
</head>
表单本地化
多语言表单
<!-- 本地化表单 -->
<form action="/contact" method="post" class="contact-form">
<div class="form-group">
<label for="contact-name">{{ 'contact.form.name' | t }}</label>
<input type="text"
id="contact-name"
name="contact[name]"
placeholder="{{ 'contact.form.name_placeholder' | t }}"
required>
</div>
<div class="form-group">
<label for="contact-email">{{ 'contact.form.email' | t }}</label>
<input type="email"
id="contact-email"
name="contact[email]"
placeholder="{{ 'contact.form.email_placeholder' | t }}"
required>
</div>
<div class="form-group">
<label for="contact-subject">{{ 'contact.form.subject' | t }}</label>
<select id="contact-subject" name="contact[subject]">
<option value="">{{ 'contact.form.subject_choose' | t }}</option>
<option value="general">{{ 'contact.form.subject_general' | t }}</option>
<option value="support">{{ 'contact.form.subject_support' | t }}</option>
<option value="wholesale">{{ 'contact.form.subject_wholesale' | t }}</option>
</select>
</div>
<div class="form-group">
<label for="contact-message">{{ 'contact.form.message' | t }}</label>
<textarea id="contact-message"
name="contact[body]"
placeholder="{{ 'contact.form.message_placeholder' | t }}"
required></textarea>
</div>
<button type="submit" class="btn btn-primary">
{{ 'contact.form.send' | t }}
</button>
</form>
本地化工具
翻译文件结构
<!-- 翻译文件路径检查 -->
{% comment %}
翻译文件位置:
/locales/en.default.json
/locales/zh-CN.json
/locales/ja.json
/locales/fr.json
{% endcomment %}
<!-- 翻译键检查 -->
<div class="translation-debug" style="display: none;">
{% assign test_keys = 'general.navigation.home,products.product.add_to_cart,cart.general.title' | split: ',' %}
{% for key in test_keys %}
<p>{{ key }}: "{{ key | t }}"</p>
{% endfor %}
</div>
翻译管理
<!-- 翻译管理助手 -->
{% if request.design_mode %}
<div class="translation-helper" style="position: fixed; top: 10px; right: 10px; background: #fff; padding: 10px; border: 1px solid #ccc; z-index: 9999;">
<h4>翻译助手</h4>
<p><strong>当前语言:</strong> {{ request.locale.name }} ({{ request.locale.iso_code }})</p>
<p><strong>可用语言:</strong></p>
<ul>
{% for locale in shop.published_locales %}
<li>
<a href="{{ locale.root_url }}{{ request.path }}">
{{ locale.name }} ({{ locale.iso_code }})
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
性能优化
本地化缓存
<script>
// 翻译缓存
const TranslationCache = {
cache: {},
get: function(key, locale = null) {
const currentLocale = locale || document.documentElement.lang
const cacheKey = `${currentLocale}.${key}`
if (this.cache[cacheKey]) {
return this.cache[cacheKey]
}
// 从服务器获取翻译
return this.fetch(key, currentLocale)
},
fetch: function(key, locale) {
return fetch(`/locales/${locale}.json`)
.then(response => response.json())
.then(translations => {
const value = this.getNestedValue(translations, key)
this.cache[`${locale}.${key}`] = value
return value
})
},
getNestedValue: function(obj, path) {
return path.split('.').reduce((o, p) => o && o[p], obj)
}
}
// 动态翻译
function t(key, params = {}) {
return TranslationCache.get(key).then(translation => {
if (typeof translation === 'string') {
// 替换参数
Object.keys(params).forEach(param => {
translation = translation.replace(new RegExp(`{{\\s*${param}\\s*}}`, 'g'), params[param])
})
}
return translation || key
})
}
</script>
测试和调试
本地化测试
<!-- 本地化测试工具 -->
{% if request.design_mode %}
<div class="localization-test" style="margin: 20px 0; padding: 20px; background: #f8f9fa; border: 1px solid #dee2e6;">
<h3>本地化测试</h3>
<div class="test-section">
<h4>语言信息</h4>
<ul>
<li>当前语言: {{ request.locale.name }} ({{ request.locale.iso_code }})</li>
<li>默认语言: {{ shop.primary_locale.name }} ({{ shop.primary_locale.iso_code }})</li>
<li>可用语言数: {{ shop.published_locales.size }}</li>
</ul>
</div>
<div class="test-section">
<h4>货币信息</h4>
<ul>
<li>当前货币: {{ cart.currency.iso_code }}</li>
<li>货币符号: {{ cart.currency.symbol }}</li>
<li>可用货币数: {{ shop.enabled_currencies.size }}</li>
</ul>
</div>
<div class="test-section">
<h4>翻译测试</h4>
<p>导航.首页: {{ 'general.navigation.home' | t }}</p>
<p>产品.加入购物车: {{ 'products.product.add_to_cart' | t }}</p>
<p>购物车.标题: {{ 'cart.general.title' | t }}</p>
</div>
</div>
{% endif %}
下一步学习
现在您已经掌握了多语言和本地化功能,建议继续学习:
- Ajax 与 Liquid - 学习动态内容加载
- 高级 Liquid 技巧 - 学习更高级的模板技巧
- 性能优化 - 优化多语言站点性能
- 最佳实践 - 了解本地化最佳实践
最后更新时间: