Android 多语言表格转 xml/JSON

Android 多语言 Excel 表格转 JSON

Android 系统开发中,经常会遇到客户提供多语言词条表格,我们再将其转换成xml放到res文件夹下;

通常这个工作量会比较大,因为一般提供过来的词条如果不是开发新需求新增的话,数量会有几百至上千条,这个时候我们手动转并不太现实了,所以可以用到一些脚本或者工具;

这里就用python实现将excel表格转成xml或者json格式文件;

表格格式

为了能输出正确的文件格式,首先需要对表格格式做规范,表格的规范也需要同步到客户,便于拿到表格可以直接使用,减少开发时间;

表格格式通常如下:第一列为key,后面每列对应语言缩写

key en zh-CN zh-TW
app_name MyApp 我的应用 我的應用
welcome_msg Welcome, %s! 欢迎,%s! 歡迎,%s!

准备 python 环境

python 环境搭建比较简单,官网下载对应exe安装即可

官网:https://www.python.org/

脚本源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/usr/bin/env python3
"""
excel_to_android_json.py
把多语言表格转换为 Android values-xx/strings.json
"""
import os
import json
import argparse
import pandas as pd

def normalize_col_name(name: str) -> str:
"""去掉前后空格、统一成小写"""
if name is None:
return ""
return str(name).strip().lower()

def main():
parser = argparse.ArgumentParser()
parser.add_argument("input", help="Excel/CSV 文件路径")
parser.add_argument("--out-dir", default="out", help="输出目录")
parser.add_argument("--key-col", default="key", help="key 列名(默认 key)")
parser.add_argument("--skip-empty", action="store_true", help="跳过空翻译")
args = parser.parse_args()

if not os.path.exists(args.input):
raise SystemExit(f"文件不存在: {args.input}")

# 读取 Excel 或 CSV
if args.input.lower().endswith((".xls", ".xlsx")):
df = pd.read_excel(args.input, dtype=str)
else:
df = pd.read_csv(args.input, dtype=str)

df = df.fillna("")

# 规范化表头
df.columns = [normalize_col_name(c) for c in df.columns]
key_col_norm = normalize_col_name(args.key_col)

# 打印表头方便排查
print("表头列名(规范化后):", df.columns.tolist())

if key_col_norm not in df.columns:
raise SystemExit(f"未找到 key 列 '{args.key_col}',请检查表头")

# 找到语言列(排除 key 和 comment)
locale_cols = [c for c in df.columns if c not in (key_col_norm, "comment")]

os.makedirs(args.out_dir, exist_ok=True)

for locale in locale_cols:
kv = {}
for _, row in df.iterrows():
key = row[key_col_norm].strip()
val = row[locale].strip()
if not key:
continue
if args.skip_empty and val == "":
continue
kv[key] = val

dir_name = "values" if locale in ("en", "default", "") else f"values-{locale}"
full_dir = os.path.join(args.out_dir, dir_name)
os.makedirs(full_dir, exist_ok=True)
out_file = os.path.join(full_dir, "strings.json")

with open(out_file, "w", encoding="utf-8") as f:
json.dump(kv, f, ensure_ascii=False, indent=2)

print(f"已生成 {out_file}")

if __name__ == "__main__":
main()

运行

1
2
pip install pandas openpyxl
python excel_to_android_json.py 多语言表.xlsx --out-dir out

生成的目录结构:

1
2
3
4
5
6
7
out/
values-en/
strings.json
values-zh-CN/
strings.json
values-zh-TW/
strings.json

Android 多语言 Excel 表格转 xml

只需要对上面的脚本做一些修改即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python3
"""
excel_to_android_xml.py
把多语言表格转换为 Android values-xx/strings.xml
支持:
- 自动识别 key 列 (大小写/空格/隐藏字符)
- 转义 &, <, >, ", '
- 自动映射 Android 标准语言目录名
"""
import os
import argparse
import pandas as pd
import xml.etree.ElementTree as ET

# Android 标准语言代码映射
ANDROID_LOCALE_MAP = {
"zh-cn": "values-zh-rCN",
"zh": "values-zh",
"zh-tw": "values-zh-rTW",
"zh-hk": "values-zh-rHK",
"en": "values",
"default": "values"
}

def normalize_col_name(name: str) -> str:
"""去掉前后空格、统一成小写"""
if name is None:
return ""
return str(name).strip().lower()

def safe_android_string(s: str) -> str:
"""转义 Android strings.xml 特殊字符"""
if pd.isna(s):
return ""
s = str(s)
s = s.replace('&', '&amp;') # & 必须先转义
s = s.replace('<', '&lt;').replace('>', '&gt;')
s = s.replace('"', '\\"') # 双引号
s = s.replace("'", "\\'") # 单引号
return s

def get_android_values_dir(locale: str) -> str:
"""根据语言代码生成 Android 规范的 values-xx 目录"""
locale_norm = locale.lower()
if locale_norm in ANDROID_LOCALE_MAP:
return ANDROID_LOCALE_MAP[locale_norm]
else:
return f"values-{locale_norm}"

def main():
parser = argparse.ArgumentParser()
parser.add_argument("input", help="Excel/CSV 文件路径")
parser.add_argument("--out-dir", default="out", help="输出目录")
parser.add_argument("--key-col", default="key", help="key 列名(默认 key)")
parser.add_argument("--skip-empty", action="store_true", help="跳过空翻译")
args = parser.parse_args()

if not os.path.exists(args.input):
raise SystemExit(f"文件不存在: {args.input}")

# 读取 Excel 或 CSV
if args.input.lower().endswith((".xls", ".xlsx")):
df = pd.read_excel(args.input, dtype=str)
else:
df = pd.read_csv(args.input, dtype=str)

df = df.fillna("")

# 规范化表头
df.columns = [normalize_col_name(c) for c in df.columns]
key_col_norm = normalize_col_name(args.key_col)

print("表头列名(规范化后):", df.columns.tolist())

if key_col_norm not in df.columns:
raise SystemExit(f"未找到 key 列 '{args.key_col}',请检查表头")

# 找到语言列(排除 key 和 comment)
locale_cols = [c for c in df.columns if c not in (key_col_norm, "comment")]

os.makedirs(args.out_dir, exist_ok=True)

for locale in locale_cols:
kv = {}
for _, row in df.iterrows():
key = row[key_col_norm].strip()
val = row[locale].strip()
if not key:
continue
if args.skip_empty and val == "":
continue
kv[key] = safe_android_string(val)

dir_name = get_android_values_dir(locale)
full_dir = os.path.join(args.out_dir, dir_name)
os.makedirs(full_dir, exist_ok=True)
out_file = os.path.join(full_dir, "strings.xml")

root = ET.Element("resources")
for k, v in kv.items():
el = ET.SubElement(root, "string", attrib={"name": k})
el.text = v
tree = ET.ElementTree(root)
tree.write(out_file, encoding="utf-8", xml_declaration=True)

print(f"已生成 {out_file}")

if __name__ == "__main__":
main()

运行

1
2
pip install pandas openpyxl
python excel_to_android_json.py 多语言表.xlsx --out-dir out