本文是《WordPress主题开发从入门到精通》系列教程的第十三篇。我们将学习如何为WordPress主题添加多语言支持,让全世界的用户都能使用你的主题。
你是否想过让你的主题被全世界的用户使用?无论是英语、西班牙语、中文还是其他语言,国际化(Internationalization,简称i18n)让你的主题能够轻松适应不同语言环境。
今天我们就来学习如何为我们的主题添加完整的国际化支持。
什么是国际化?为什么需要它?
国际化(i18n) 是指开发软件时,使其能够无需修改代码即可适应不同语言和地区的过程。本地化(l10n) 则是指为特定语言或地区创建翻译文件的过程。
为什么需要国际化?
- 全球市场:让你的主题可以被全世界用户使用
- WordPress官方要求:提交到WordPress官方主题目录必须支持国际化
- 专业标准:国际化是专业主题开发的基本要求
- 用户体验:用户可以使用自己熟悉的语言
第一步:基础准备 - 设置文本域
文本域(Text Domain)是国际化的核心,它告诉WordPress哪个翻译文件对应哪个主题。
在style.css中设置文本域:
/*
Theme Name: My First Theme
Text Domain: my-first-theme
Domain Path: /languages
*/
/* 其余样式代码... */
在functions.php中加载文本域:
/**
* 加载主题文本域
*/
function mft_load_theme_textdomain() {
// 加载主题语言文件
load_theme_textdomain('my-first-theme', get_template_directory() . '/languages');
}
add_action('after_setup_theme', 'mft_load_theme_textdomain');
第二步:准备翻译函数
WordPress提供了一系列翻译函数,我们需要在主题中正确使用它们。
基本翻译函数:
/**
* 翻译函数示例
*/
// 1. __() - 返回翻译后的字符串
function mft_basic_translation_example() {
// 错误做法 - 硬编码字符串
echo '<h1>欢迎来到我们的网站</h1>';
// 正确做法 - 可翻译字符串
echo '<h1>' . __('欢迎来到我们的网站', 'my-first-theme') . '</h1>';
}
// 2. _e() - 直接输出翻译后的字符串
function mft_e_translation_example() {
// 错误做法
echo '阅读更多';
// 正确做法
_e('阅读更多', 'my-first-theme');
}
// 3. _x() - 带上下文的翻译
function mft_context_translation_example() {
// "Post" 在动词和名词情况下需要不同翻译
$verb = _x('Post', 'verb', 'my-first-theme'); // 发表
$noun = _x('Post', 'noun', 'my-first-theme'); // 文章
echo '<button>' . $verb . '</button>';
echo '<div>' . $noun . '</div>';
}
// 4. _n() - 单复数处理
function mft_plural_translation_example($comment_count) {
// 根据数量显示不同的文本
printf(
_n(
'有1条评论', // 单数
'有%s条评论', // 复数
$comment_count,
'my-first-theme'
),
number_format_i18n($comment_count) // 本地化数字格式
);
}
// 5. esc_html__() - 翻译并转义HTML
function mft_safe_translation_example($user_input) {
// 安全做法:翻译并转义
echo '<div>' . esc_html__('用户输入', 'my-first-theme') . ': ' . esc_html($user_input) . '</div>';
}
第三步:全面国际化我们的主题
现在让我们系统地国际化整个主题。我们将从模板文件开始。
3.1 国际化header.php
<?php
/**
* 头部模板 - 国际化版本
*
* @package My_First_Theme
*/
?><!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php
// 国际化页面标题
if (is_front_page() || is_home()) {
$page_title = get_bloginfo('name');
if (get_bloginfo('description')) {
$page_title .= ' - ' . get_bloginfo('description');
}
} elseif (is_singular()) {
$page_title = wp_title('', false) . ' - ' . get_bloginfo('name');
} else {
$page_title = wp_title('', false);
if ($page_title) {
$page_title .= ' - ' . get_bloginfo('name');
} else {
$page_title = get_bloginfo('name');
}
}
?>
<title><?php echo esc_html($page_title); ?></title>
<link rel="stylesheet" href="<?php echo get_stylesheet_uri(); ?>">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<!-- 无障碍跳过链接 -->
<a class="skip-link screen-reader-text" href="#main-content">
<?php esc_html_e('跳过导航直接到内容', 'my-first-theme'); ?>
</a>
<header class="site-header" role="banner">
<div class="header-inner">
<div class="site-branding">
<?php if (has_custom_logo()) : ?>
<div class="site-logo">
<?php the_custom_logo(); ?>
</div>
<?php else : ?>
<h1 class="site-title">
<a href="<?php echo esc_url(home_url('/')); ?>" rel="home">
<?php bloginfo('name'); ?>
</a>
</h1>
<?php
$description = get_bloginfo('description', 'display');
if ($description || is_customize_preview()) : ?>
<p class="site-description">
<?php echo $description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
<?php endif; ?>
<?php endif; ?>
</div>
<nav class="main-navigation" role="navigation" aria-label="<?php esc_attr_e('主导航', 'my-first-theme'); ?>">
<?php
wp_nav_menu(array(
'theme_location' => 'primary',
'menu_class' => 'primary-menu',
'container' => false,
'fallback_cb' => false,
));
?>
</nav>
</div>
</header>
3.2 国际化index.php
<?php
/**
* 主模板文件 - 国际化版本
*
* @package My_First_Theme
*/
get_header(); ?>
<div class="content-wrapper">
<main id="main-content" class="main-content" role="main">
<?php if (have_posts()) : ?>
<header class="page-header">
<?php
if (is_home() && !is_front_page()) :
?>
<h1 class="page-title">
<?php single_post_title(); ?>
</h1>
<?php
elseif (is_search()) :
?>
<h1 class="page-title">
<?php
printf(
/* translators: %s: 搜索关键词 */
esc_html__('搜索结果:%s', 'my-first-theme'),
'<span>' . get_search_query() . '</span>'
);
?>
</h1>
<?php
elseif (is_archive()) :
the_archive_title('<h1 class="page-title">', '</h1>');
the_archive_description('<div class="archive-description">', '</div>');
endif;
?>
</header>
<?php
// 主循环
while (have_posts()) : the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class('article-item'); ?>>
<header class="article-header">
<?php
if (is_singular()) :
the_title('<h1 class="article-title">', '</h1>');
else :
the_title('<h2 class="article-title"><a href="' . esc_url(get_permalink()) . '">', '</a></h2>');
endif;
?>
<div class="article-meta">
<?php
// 作者
if ('post' === get_post_type()) :
printf(
'<span class="byline">%s <span class="author vcard"><a class="url fn n" href="%s">%s</a></span></span>',
esc_html__('作者:', 'my-first-theme'),
esc_url(get_author_posts_url(get_the_author_meta('ID'))),
esc_html(get_the_author())
);
endif;
// 发布时间
$time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';
if (get_the_time('U') !== get_the_modified_time('U')) {
$time_string = '<time class="entry-date published" datetime="%1$s">%2$s</time><time class="updated" datetime="%3$s">%4$s</time>';
}
printf(
'<span class="posted-on">%s %s</span>',
esc_html__('发布于:', 'my-first-theme'),
$time_string
);
// 分类
$categories_list = get_the_category_list(esc_html__(', ', 'my-first-theme'));
if ($categories_list) :
printf(
'<span class="cat-links">%s %s</span>',
esc_html__('分类:', 'my-first-theme'),
$categories_list
);
endif;
?>
</div>
</header>
<?php if (has_post_thumbnail()) : ?>
<div class="article-thumbnail">
<?php the_post_thumbnail('large'); ?>
</div>
<?php endif; ?>
<div class="article-content">
<?php
if (is_singular()) {
the_content();
// 分页链接
wp_link_pages(array(
'before' => '<div class="page-links">' . esc_html__('页面:', 'my-first-theme'),
'after' => '</div>',
));
} else {
the_excerpt();
}
?>
</div>
<footer class="article-footer">
<?php
// 标签
$tags_list = get_the_tag_list('', esc_html_x(', ', '标签分隔符', 'my-first-theme'));
if ($tags_list) :
printf(
'<span class="tags-links">%s %s</span>',
esc_html__('标签:', 'my-first-theme'),
$tags_list
);
endif;
// 评论链接
if (!is_singular() && !post_password_required() && (comments_open() || get_comments_number())) :
?>
<span class="comments-link">
<?php
comments_popup_link(
esc_html__('发表评论', 'my-first-theme'),
esc_html__('1 条评论', 'my-first-theme'),
esc_html__('% 条评论', 'my-first-theme')
);
?>
</span>
<?php
endif;
// 编辑链接
edit_post_link(
esc_html__('编辑', 'my-first-theme'),
'<span class="edit-link">',
'</span>'
);
?>
</footer>
</article>
<?php endwhile; ?>
<?php
// 分页导航
the_posts_pagination(array(
'prev_text' => esc_html__('上一页', 'my-first-theme'),
'next_text' => esc_html__('下一页', 'my-first-theme'),
'before_page_number' => '<span class="meta-nav screen-reader-text">' . esc_html__('第', 'my-first-theme') . ' </span>',
));
?>
<?php else : ?>
<section class="no-results not-found">
<header class="page-header">
<h1 class="page-title">
<?php esc_html_e('没有找到内容', 'my-first-theme'); ?>
</h1>
</header>
<div class="page-content">
<?php if (is_search()) : ?>
<p><?php esc_html_e('抱歉,没有找到匹配的搜索结果。请尝试其他关键词。', 'my-first-theme'); ?></p>
<?php get_search_form(); ?>
<?php else : ?>
<p><?php esc_html_e('看起来没有找到您要的内容。或许搜索可以帮助您。', 'my-first-theme'); ?></p>
<?php get_search_form(); ?>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
</main>
<?php get_sidebar(); ?>
</div>
<?php get_footer(); ?>
3.3 国际化footer.php
<?php
/**
* 页脚模板 - 国际化版本
*
* @package My_First_Theme
*/
?>
</div><!-- .content-wrapper -->
<footer class="site-footer" role="contentinfo">
<?php if (is_active_sidebar('footer-1') || is_active_sidebar('footer-2') || is_active_sidebar('footer-3')) : ?>
<div class="footer-widgets">
<div class="footer-widgets-inner">
<?php if (is_active_sidebar('footer-1')) : ?>
<div class="footer-widget-area">
<?php dynamic_sidebar('footer-1'); ?>
</div>
<?php endif; ?>
<?php if (is_active_sidebar('footer-2')) : ?>
<div class="footer-widget-area">
<?php dynamic_sidebar('footer-2'); ?>
</div>
<?php endif; ?>
<?php if (is_active_sidebar('footer-3')) : ?>
<div class="footer-widget-area">
<?php dynamic_sidebar('footer-3'); ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="site-info">
<div class="site-info-inner">
<?php
// 版权信息 - 支持多语言
$copyright_text = get_theme_mod('footer_copyright', sprintf(
/* translators: 1: 当前年份, 2: 网站名称 */
esc_html__('© %1$s %2$s. 保留所有权利。', 'my-first-theme'),
date('Y'),
get_bloginfo('name')
));
?>
<div class="footer-copyright">
<?php echo wp_kses_post($copyright_text); ?>
</div>
<div class="footer-credits">
<?php
printf(
/* translators: %s: WordPress */
esc_html__('自豪地采用 %s', 'my-first-theme'),
'<a href="' . esc_url(__('https://wordpress.org/', 'my-first-theme')) . '">WordPress</a>'
);
?>
<span class="sep"> | </span>
<?php
printf(
/* translators: 1: 主题名称, 2: 主题作者 */
esc_html__('主题: %1$s by %2$s', 'my-first-theme'),
'My First Theme',
'<a href="https://example.com/">Your Name</a>'
);
?>
</div>
<?php if (get_theme_mod('back_to_top', true)) : ?>
<button id="back-to-top" class="back-to-top" aria-label="<?php esc_attr_e('回到顶部', 'my-first-theme'); ?>">
<?php esc_html_e('↑', 'my-first-theme'); ?>
</button>
<?php endif; ?>
</div>
</div>
</footer>
<?php wp_footer(); ?>
</body>
</html>
3.4 国际化comments.php
<?php
/**
* 评论模板 - 国际化版本
*
* @package My_First_Theme
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 如果文章需要密码保护,不显示评论
if (post_password_required()) {
return;
}
if (have_comments() || comments_open()) : ?>
<div id="comments" class="comments-area">
<?php if (have_comments()) : ?>
<h2 class="comments-title">
<?php
$comment_count = get_comments_number();
if ('1' === $comment_count) {
printf(
/* translators: 1: 标题 */
esc_html__('有一条评论', 'my-first-theme'),
'<span>' . get_the_title() . '</span>'
);
} else {
printf(
/* translators: 1: 评论数量, 2: 标题 */
esc_html(_n('%1$s 条评论', '%1$s 条评论', $comment_count, 'my-first-theme')),
number_format_i18n($comment_count),
'<span>' . get_the_title() . '</span>'
);
}
?>
</h2>
<ol class="comment-list">
<?php
wp_list_comments(array(
'avatar_size' => 60,
'style' => 'ol',
'short_ping' => true,
'reply_text' => esc_html__('回复', 'my-first-theme'),
));
?>
</ol>
<?php
the_comments_pagination(array(
'prev_text' => esc_html__('上一页', 'my-first-theme'),
'next_text' => esc_html__('下一页', 'my-first-theme'),
));
?>
<?php endif; ?>
<?php if (!comments_open()) : ?>
<p class="no-comments">
<?php esc_html_e('评论已关闭。', 'my-first-theme'); ?>
</p>
<?php endif; ?>
<?php
// 评论表单
comment_form(array(
'title_reply' => esc_html__('发表评论', 'my-first-theme'),
'title_reply_to' => esc_html__('回复 %s', 'my-first-theme'),
'cancel_reply_link' => esc_html__('取消回复', 'my-first-theme'),
'label_submit' => esc_html__('提交评论', 'my-first-theme'),
'comment_notes_before' => '<p class="comment-notes">' .
esc_html__('您的电子邮箱地址不会被公开。', 'my-first-theme') . '</p>',
));
?>
</div>
<?php endif; ?>
第四步:国际化JavaScript文件
创建assets/js/i18n.js文件处理前端国际化:
/**
* 前端国际化处理
*/
var MFT_i18n = MFT_i18n || {};
MFT_i18n = {
// 翻译字符串
strings: {
loading: '加载中...',
loadMore: '加载更多',
noMorePosts: '没有更多文章了',
searchPlaceholder: '搜索...',
readMore: '阅读更多',
close: '关闭',
expand: '展开',
collapse: '收起'
},
// 日期格式
dateFormat: 'Y年m月d日',
// 初始化
init: function() {
this.localizeStrings();
this.localizeDates();
},
// 本地化字符串
localizeStrings: function() {
// 这里可以通过PHP传递翻译后的字符串
if (typeof mft_l10n !== 'undefined') {
this.strings = {...this.strings, ...mft_l10n};
}
},
// 本地化日期
localizeDates: function() {
// 日期本地化处理
jQuery('.localized-date').each(function() {
var date = new Date(jQuery(this).data('timestamp') * 1000);
var formattedDate = date.toLocaleDateString();
jQuery(this).text(formattedDate);
});
},
// 获取翻译字符串
__: function(string) {
return this.strings[string] || string;
},
// 带上下文的翻译
_x: function(string, context) {
var key = string + '|' + context;
return this.strings[key] || string;
},
// 单复数处理
_n: function(single, plural, number) {
if (number === 1) {
return this.strings[single] || single;
} else {
return this.strings[plural] || plural;
}
}
};
// 初始化
jQuery(document).ready(function($) {
MFT_i18n.init();
});
在functions.php中传递翻译字符串给JavaScript:
/**
* 传递翻译字符串到JavaScript
*/
function mft_localize_script() {
wp_localize_script('mft-main-js', 'mft_l10n', array(
'loading' => esc_html__('加载中...', 'my-first-theme'),
'loadMore' => esc_html__('加载更多', 'my-first-theme'),
'noMorePosts' => esc_html__('没有更多文章了', 'my-first-theme'),
'readMore' => esc_html__('阅读更多', 'my-first-theme'),
'close' => esc_html__('关闭', 'my-first-theme'),
'expand' => esc_html__('展开', 'my-first-theme'),
'collapse' => esc_html__('收起', 'my-first-theme'),
// 带上下文的翻译
'Post|verb' => _x('发表', 'verb', 'my-first-theme'),
'Post|noun' => _x('文章', 'noun', 'my-first-theme'),
));
}
add_action('wp_enqueue_scripts', 'mft_localize_script');
第五步:创建POT文件和翻译
5.1 安装gettext工具
首先需要安装gettext工具来生成POT文件:
# Ubuntu/Debian
sudo apt-get install gettext
# macOS with Homebrew
brew install gettext
# Windows: 下载Poedit软件
5.2 创建POT文件
在主题根目录创建languages文件夹,然后使用WP-CLI生成POT文件:
# 使用WP-CLI(推荐)
wp i18n make-pot . languages/my-first-theme.pot
# 或者手动创建makepot.php文件
手动创建makepot.php文件:
<?php
/**
* 生成POT文件的脚本
*/
require_once 'wp-load.php';
$domain = 'my-first-theme';
$pot_file = get_template_directory() . '/languages/' . $domain . '.pot';
// 确保languages目录存在
if (!is_dir(get_template_directory() . '/languages')) {
mkdir(get_template_directory() . '/languages', 0755, true);
}
// 使用WordPress的翻译工具
if (!class_exists('WP_Translations')) {
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
}
// 生成POT文件的基本命令
$command = sprintf(
'xgettext --package-name="%s" --package-version="%s" --msgid-bugs-address="%s" --from-code=UTF-8 -o %s -L PHP %s',
escapeshellarg('My First Theme'),
escapeshellarg('1.0.0'),
escapeshellarg('your-email@example.com'),
escapeshellarg($pot_file),
escapeshellarg('*.php')
);
exec($command, $output, $return_var);
if ($return_var === 0) {
echo "POT文件生成成功: " . $pot_file . "\n";
} else {
echo "POT文件生成失败\n";
}
5.3 创建翻译文件
创建中文翻译文件languages/zh_CN.po:
# Chinese (Simplified) translations for My First Theme
# My First Theme 的中文(简体)翻译
# Copyright (C) 2024 Your Name
# This file is distributed under the same license as the My First Theme package.
# Your Name <your-email@example.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: My First Theme 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-01 12:00+0800\n"
"PO-Revision-Date: 2024-01-01 12:00+0800\n"
"Last-Translator: Your Name <your-email@example.com>\n"
"Language-Team: Chinese (Simplified)\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: header.php:15
msgid "跳过导航直接到内容"
msgstr "跳过导航直接到内容"
#: header.php:32
msgid "主导航"
msgstr "主导航"
#: index.php:25
msgid "搜索结果:%s"
msgstr "搜索结果:%s"
#: index.php:55
msgid "作者:"
msgstr "作者:"
#: index.php:67
msgid "发布于:"
msgstr "发布于:"
#: index.php:75
msgid "分类:"
msgstr "分类:"
#: index.php:109
msgid "标签:"
msgstr "标签:"
#: index.php:118
msgid "发表评论"
msgstr "发表评论"
#: index.php:119
msgid "1 条评论"
msgstr "1 条评论"
#: index.php:120
msgid "% 条评论"
msgstr "% 条评论"
#: index.php:128
msgid "编辑"
msgstr "编辑"
#: index.php:140
msgid "上一页"
msgstr "上一页"
#: index.php:141
msgid "下一页"
msgstr "下一页"
#: index.php:142
msgid "第"
msgstr "第"
#: index.php:151
msgid "没有找到内容"
msgstr "没有找到内容"
#: index.php:156
msgid "抱歉,没有找到匹配的搜索结果。请尝试其他关键词。"
msgstr "抱歉,没有找到匹配的搜索结果。请尝试其他关键词。"
#: index.php:161
msgid "看起来没有找到您要的内容。或许搜索可以帮助您。"
msgstr "看起来没有找到您要的内容。或许搜索可以帮助您。"
使用Poedit编译为MO文件,或使用命令行:
msgfmt languages/zh_CN.po -o languages/zh_CN.mo
第六步:测试国际化功能
6.1 设置网站语言
在wp-config.php中设置语言:
define('WPLANG', 'zh_CN');
或在后台"设置"→"常规"中设置网站语言。
6.2 创建语言切换器(可选)
创建多语言切换小工具:
/**
* 语言切换器小工具
*/
class MFT_Language_Switcher_Widget extends WP_Widget {
function __construct() {
parent::__construct(
'mft_language_switcher',
__('语言切换器', 'my-first-theme'),
array('description' => __('显示可用语言列表', 'my-first-theme'))
);
}
public function widget($args, $instance) {
echo $args['before_widget'];
if (!empty($instance['title'])) {
echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title'];
}
$languages = apply_filters('wpml_active_languages', null);
if (!empty($languages)) {
echo '<ul class="language-switcher">';
foreach ($languages as $language) {
$class = $language['active'] ? 'class="active"' : '';
printf(
'<li %s><a href="%s">%s</a></li>',
$class,
esc_url($language['url']),
esc_html($language['native_name'])
);
}
echo '</ul>';
}
echo $args['after_widget'];
}
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : __('选择语言', 'my-first-theme');
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>">
<?php _e('标题:', 'my-first-theme'); ?>
</label>
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
name="<?php echo $this->get_field_name('title'); ?>"
type="text" value="<?php echo esc_attr($title); ?>">
</p>
<?php
}
public function update($new_instance, $old_instance) {
$instance = array();
$instance['title'] = (!empty($new_instance['title'])) ? strip_tags($new_instance['title']) : '';
return $instance;
}
}
// 注册小工具
function mft_register_language_switcher() {
register_widget('MFT_Language_Switcher_Widget');
}
add_action('widgets_init', 'mft_register_language_switcher');
第七步:国际化最佳实践
7.1 代码规范
// ✅ 正确做法
printf(
_n(
'%d 条评论',
'%d 条评论',
$comment_count,
'my-first-theme'
),
$comment_count
);
// ❌ 错误做法
echo $comment_count . ' 条评论';
// ✅ 正确做法 - 带HTML转义
printf(
'<a href="%s">%s</a>',
esc_url($link),
esc_html__('阅读更多', 'my-first-theme')
);
// ❌ 错误做法 - 不安全
echo '<a href="' . $link . '">阅读更多</a>';
7.2 上下文使用
// 菜单标签
_x('Home', 'menu item', 'my-first-theme');
_x('Blog', 'menu item', 'my-first-theme');
// 按钮标签
_x('Save', 'button', 'my-first-theme');
_x('Delete', 'button', 'my-first-theme');
// 日期格式
_x('F j, Y', 'date format', 'my-first-theme');
总结:国际化完整流程
通过今天的学习,你已经掌握了WordPress主题国际化的完整技能:
- 文本域设置:在
style.css中定义文本域 - 翻译函数使用:
__(),_e(),_x(),_n()等 - 模板文件国际化:系统化处理所有字符串
- JavaScript国际化:前端字符串本地化
- POT文件生成:创建翻译模板
- 翻译文件创建:制作PO/MO文件
- 测试和优化:确保翻译正确工作
国际化检查清单:
- [ ] 所有用户可见的字符串都使用了翻译函数
- [ ] 正确设置了文本域和语言路径
- [ ] 创建了POT翻译模板文件
- [ ] 提供了至少一种语言翻译
- [ ] 测试了不同语言环境下的显示
- [ ] 处理了单复数形式和上下文翻译
现在你的主题已经具备了国际化的能力!在下一篇文章中,我们将学习WordPress主题安全开发最佳实践,确保主题的安全性。

评论框