CTeX-org / ctex-kit

Macro Packages and Scripts for Chinese TeX users
976 stars 124 forks source link

xpinyin: 包功能的模块化 #550

Open MathMapMat opened 3 years ago

MathMapMat commented 3 years ago

目前 xpinyin 包的功能是直接在汉字上方加拼音, 希望可以把一部分功能拆分出来:

  1. 提供一个命令, 返回一个汉字的拼音.
  2. 提供一个命令, 返回一个汉字的首字母.
  3. 提供一个命令, 返回一个汉字的声母.
  4. 提供一个命令, 返回一个汉字的韵母. 以上命令可提供一个option, 决定是否加声调, 声调直接修饰在字母上还是用数字表示.

提供以上功能以后可以更好的处理中文索引问题.

muzimuzhi commented 3 years ago

提供以上功能以后可以更好的处理中文索引问题.

两者的联系是?能否给个例子

stone-zeng commented 3 years ago

一个粗浅的实现(主要是韵母没有处理,声调的选项也没管):

% 拼音
\cs_new:Npn \PrintPinyin #1
  { \use:c { c__xpinyin_ \int_to_arabic:n { `#1 } _tl } }

% 首字母
\cs_new_protected:Npn \PrintPinyinAbbr #1
  {
    \tl_set:Nx \l_tmpa_tl { \tl_head:f { \PrintPinyin {#1} } }
    \exp_args:Nx \tl_head:n { \exp_args:NV \char_to_nfd:N \l_tmpa_tl }
  }

% 声母
\cs_new_protected:Npn \PrintPinyinInital #1
  {
    \exp_args:Nf \__pinyin_split:n { \PrintPinyin {#1} }
    \seq_item:Nn \l__pinyin_split_seq {2}
  }

% 韵母
\cs_new_protected:Npn \PrintPinyinFinal #1
  {
    \exp_args:Nf \__pinyin_split:n { \PrintPinyin {#1} }
    \seq_item:Nn \l__pinyin_split_seq {3}
  }

\cs_new_protected:Npn \__pinyin_split:n #1
  {
    \seq_clear:N \l__pinyin_split_seq
    \regex_extract_once:NnN \c__pinyin_split_regex {#1} \l__pinyin_split_seq
  }

\regex_const:Nn \c__pinyin_split_regex { \A ([b|p|m|f|d|t|n|l|g|k|h|j|q|x|r|z|c|s]h?) (.+) \Z }
\seq_new:N \l__pinyin_split_seq
MathMapMat commented 3 years ago

提供以上功能以后可以更好的处理中文索引问题.

两者的联系是?能否给个例子

在我查了一遍 ctan 中各类处理中文索引的 package 后, 我发现的最优解是用 glossaries 和 bib2gls 包. xindy 包没人写中文 style 文件, 类比韩文的 style 文件已很恐怖, 估计中文处理效率有限. zhmakeindex 可用, 但还不够好. bib2gls 可以按 UCA-CLDR 标准处理 Unicode 在各 locale 里的排序. 目前 CLDR 的标准中, 中文和英文是合在一起排序的. 比如说 '汉字' 和 'happy' 都会列在 'H' 条目中. 可以说目前的解决方案已经很不错了, 但是我觉得书里的索引还是把英文和中文分开比较好, 按 符号, A-Z, 汉字拼音A-Z 的顺序. 在没有一个包可以自动处理汉字的拼音首字母的情况下, 需要手动给每个索引项加个前缀, 另加 26 个索引组. 如果可以自动给出汉字的拼音首字母(如果是一串字符的首字符的首字母就更好了), 那么就可以自动加前缀, 只需要建立索引组就行.

muzimuzhi commented 3 years ago

zhmakeindex 可用, 但还不够好.

具体哪里不够好呢?

MathMapMat commented 3 years ago

zhmakeindex 可用, 但还不够好.

具体哪里不够好呢?

多语言同时索引时兼顾中文和非英文文字会有些麻烦. 另外多层级的条目遇到各级都需要指定键和输出值时好像有问题(可能是我没弄清语法, 不过 makeindex 里好像也弄不出来). 我非常感谢 CTeX 的贡献, 并不是有意冒犯.

MathMapMat commented 3 years ago

一个粗浅的实现(主要是韵母没有处理,声调的选项也没管):

% 拼音
\cs_new:Npn \PrintPinyin #1
  { \use:c { c__xpinyin_ \int_to_arabic:n { `#1 } _tl } }

% 首字母
\cs_new_protected:Npn \PrintPinyinAbbr #1
  {
    \tl_set:Nx \l_tmpa_tl { \tl_head:f { \PrintPinyin {#1} } }
    \exp_args:Nx \tl_head:n { \exp_args:NV \char_to_nfd:N \l_tmpa_tl }
  }

% 声母
\cs_new_protected:Npn \PrintPinyinInital #1
  {
    \exp_args:Nf \__pinyin_split:n { \PrintPinyin {#1} }
    \seq_item:Nn \l__pinyin_split_seq {2}
  }

% 韵母
\cs_new_protected:Npn \PrintPinyinFinal #1
  {
    \exp_args:Nf \__pinyin_split:n { \PrintPinyin {#1} }
    \seq_item:Nn \l__pinyin_split_seq {3}
  }

\cs_new_protected:Npn \__pinyin_split:n #1
  {
    \seq_clear:N \l__pinyin_split_seq
    \regex_extract_once:NnN \c__pinyin_split_regex {#1} \l__pinyin_split_seq
  }

\regex_const:Nn \c__pinyin_split_regex { \A ([b|p|m|f|d|t|n|l|g|k|h|j|q|x|r|z|c|s]h?) (.+) \Z }
\seq_new:N \l__pinyin_split_seq

我对 TeX 的底层实现不太了解, 下面是我用 python 处理声调的方法. 有几个特殊的字音调标在 n 和 m 上.

def tones_to_numbers(s):
    ret = s
    ret = re.sub(r"([a-z]*)ā([a-z]*)", r"\g<1>a\g<2>1", ret)
    ret = re.sub(r"([a-z]*)á([a-z]*)", r"\g<1>a\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ǎ([a-z]*)", r"\g<1>a\g<2>3", ret)
    ret = re.sub(r"([a-z]*)à([a-z]*)", r"\g<1>a\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ō([a-z]*)", r"\g<1>o\g<2>1", ret)
    ret = re.sub(r"([a-z]*)ó([a-z]*)", r"\g<1>o\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ǒ([a-z]*)", r"\g<1>o\g<2>3", ret)
    ret = re.sub(r"([a-z]*)ò([a-z]*)", r"\g<1>o\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ē([a-z]*)", r"\g<1>e\g<2>1", ret)
    ret = re.sub(r"([a-z]*)é([a-z]*)", r"\g<1>e\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ě([a-z]*)", r"\g<1>e\g<2>3", ret)
    ret = re.sub(r"([a-z]*)è([a-z]*)", r"\g<1>e\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ī([a-z]*)", r"\g<1>i\g<2>1", ret)
    ret = re.sub(r"([a-z]*)í([a-z]*)", r"\g<1>i\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ǐ([a-z]*)", r"\g<1>i\g<2>3", ret)
    ret = re.sub(r"([a-z]*)ì([a-z]*)", r"\g<1>i\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ū([a-z]*)", r"\g<1>u\g<2>1", ret)
    ret = re.sub(r"([a-z]*)ú([a-z]*)", r"\g<1>u\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ǔ([a-z]*)", r"\g<1>u\g<2>3", ret)
    ret = re.sub(r"([a-z]*)ù([a-z]*)", r"\g<1>u\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ü([a-z]*)", r"\g<1>v\g<2>", ret)
    ret = re.sub(r"([a-z]*)ǖ([a-z]*)", r"\g<1>v\g<2>1", ret)
    ret = re.sub(r"([a-z]*)ǘ([a-z]*)", r"\g<1>v\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ǚ([a-z]*)", r"\g<1>v\g<2>3", ret)
    ret = re.sub(r"([a-z]*)ǜ([a-z]*)", r"\g<1>v\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ń([a-z]*)", r"\g<1>n\g<2>2", ret)
    ret = re.sub(r"([a-z]*)ň([a-z]*)", r"\g<1>n\g<2>3", ret)
    ret = re.sub(r"([a-z]*)ǹ([a-z]*)", r"\g<1>n\g<2>4", ret)
    ret = re.sub(r"([a-z]*)ḿ([a-z]*)", r"\g<1>m\g<2>2", ret)
    return ret