具有多个值的字符串变量

具有多个值的字符串变量

我正在寻找一个命令定义,该命令可以具有多个值以用于不同的上下文。此用例将是在同时使用两种语言的自定义类中,或者我猜是任何变量可能根据环境具有不同值的情况。在搜索了相关信息\newcommand和与之相关的东西后,我仍然对事情有点困惑。

据我了解,一个简单的实现是:

\newcommand\varname{text value}

但是,在我看来,对相同的东西使用不同的命令会很不好看。例如,我面前有一个旧类,其中包含芬兰语版本的命令DoctypeDoctypefin。相反,我想使用这样的命令:

% Definitions
\title{en}{Title Of The Document}
\title{fi}{Dokumentin otsikko}

% Use in class environments or tex files
\title{en} % -> "Title Of The Document"
\title{fi} % -> "Dokumentin otsikko"

有什么方法可以构建这样的宏或定义吗?

理想情况下,指定未见过的类别(这里是语言)不会有问题,但我认为要求在类文件中指定可接受的值是合理的。此外,就像下面的示例一样,如果这是一个问题,我认为使用单独的命令来输出变量是可以接受的。


我尝试记录其他类命令的构造方式。有一个方便的MakeStringVar命令可以构造一个变量,如果未设置,则显示默认文本。该默认文本功能对于新命令非常有用,因为类中的许多环境都使用定义将文本输出到标题页等。

\newcommand\MakeStringVar[2][\relax]{%
  \ifx#1\relax%
    \expandafter\newcommand\csname Emit#2\endcsname{%
      {\scriptsize (Use {\tt\textbackslash #2} to replace this text.)}}%
  \else%
    \expandafter\newcommand\csname Emit#2\endcsname{#1}%
  \fi%
  \expandafter\newcommand\csname #2\endcsname[1]{%
     \expandafter\renewcommand\csname Emit#2\endcsname{##1}%
  }%
}

它的使用方式如下:

\MakeStringVar{Major} % Definition in class
\Major{Major subject name} % Set value in pre-document
\EmitMajor % Used in environments in class -> "Major subject name"

但我对 LaTeX 还是个新手,所以我不知道从哪里开始。我觉得上面的命令可以以某种方式扩展,但我真的不知道 LaTeX 宏的局限性。

答案1

您可以考虑使用以下方法\@namedef

\documentclass{article}

\makeatletter
\newcommand\deftitle[2][en]{%
    \global\@namedef{title:#1}{#2}%
}
\newcommand\usetitle[1][en]{\@nameuse{title:#1}}
\makeatother

\begin{document}

\deftitle{Default Language (English) Title} % same as \deftitle[en]{...}
\deftitle[it]{Italian Title}
\deftitle[fr]{French Title}

\usetitle[it]
\usetitle % same as \usetitle[en]
\usetitle[fr]

\end{document}

当用户调用时,会定义\deftitle[en]{<content>}一个新的宏,并且当通过调用时它会扩展为。title:en<content>\@nameuse

编辑:这里有一个构造此类宏的通用方法:

\makeatletter
\newcommand\newconstructor[1]{%
    \expandafter\newcommand\csname def#1\endcsname[2][en]{%
        \global\@namedef{#1:##1}{##2}%
    }%
    \expandafter\newcommand\csname use#1\endcsname[1][en]{\@nameuse{#1:##1}}%
}
\makeatother

现在,例如,像以前一样\newconstructor{title}定义\deftitle\usetitle

答案2

在此实现中,可以使用方便的键值界面输入各种版本(如果值包含逗号,则用括号括起来)。

还可以定义键的别名。对于语言,我认为最好使用带有完整语言名称的长键,这样\languagename可以用来获取相关字符串。但是,也可以在文档中使用键的别名,前提是事先定义它们。

您不需要在变量定义时添加所有版本,因为您可以\addtovarstring稍后使用。

\documentclass{article}
\usepackage[english,finnish]{babel}

\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\definevarstring}{mO{}}
 {
  \prop_new:c { g_felix_varstring_#1_prop }
  \felix_varstring_add:nn { #1 } { #2 }
 }
\NewDocumentCommand{\addtovarstring}{mm}
 {
  \felix_varstring_add:nn { #1 } { #2 }
 }
\NewDocumentCommand{\definealias}{m}
 {
  \prop_gset_from_keyval:Nn \g_felix_varstring_alias_prop { #1 }
 }
\NewExpandableDocumentCommand{\getvarstring}{mm}
 {
  \prop_if_in:cfTF { g_felix_varstring_#1_prop } { #2 }
   {
    \prop_item:cf { g_felix_varstring_#1_prop } { #2 }
   }
   {
    \prop_if_in:NnT \g_felix_varstring_alias_prop { #2 }
     {
      \prop_item:cf { g_felix_varstring_#1_prop }
       {
        \prop_item:Nn \g_felix_varstring_alias_prop { #2 }
       }
     }
   }
 }

\cs_generate_variant:Nn \prop_item:Nn { cf }
\prg_generate_conditional_variant:Nnn \prop_if_in:Nn { cf } { T,F,TF,p }
\prop_new:N \g_felix_varstring_alias_prop

\cs_new_protected:Nn \felix_varstring_add:nn
 {
  \prop_gset_from_keyval:cn { g_felix_varstring_#1_prop } { #2 }
 }

\ExplSyntaxOff

\definealias{fi=finnish,en=english}

\definevarstring{title}[% long versions for languages
  english=Title of the document,
  finnish=Dokumentin otsikko,
]

\begin{document}

\author{A. Uthor}
\title{\getvarstring{title}{\languagename}}
\maketitle

\selectlanguage{english}

\getvarstring{title}{\languagename}

\getvarstring{title}{en}---\getvarstring{title}{english}

\getvarstring{title}{fi}---\getvarstring{title}{finnish}

\end{document}

在此处输入图片描述

答案3

前段时间写了一个\SetProperty——\GetProperty接口。

也许它对你有用。

%% This coding example was written by Ulrich Diez in November 16, 2018.
%% It was modified by Ulrich Diez in February 28, 2019.
%%
%% Copyright 2018, 2019 Ulrich Diez (e-mail: [email protected])
%%
%% This work may be distributed and/or modified under the conditions of
%% the LaTeX Project Public License, either version 1.3c of this license
%% or (at your option) any later version.
%%
%% The latest version of this license is in
%%
%%   <http://www.latex-project.org/lppl.txt>
%%
%% and version 1.3x or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%% This work has the LPPL maintenance status `unmaintained'.
%%
%% This work consists of this coding example.
%%
\errorcontextlines=10000
\documentclass[landscape]{article}

\makeatletter
%%==== Begin of code for the \SetProperty-\GetProperty-Interface =======
\RequirePackage{ifluatex, ifxetex}
%%======================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%%    \UD@exchange, \UD@removespace, \UD@name, \UD@CheckWhetherNull,
%%    \UD@loopcall,
%%......................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%----------------------------------------------------------------------
%% Put a control sequence token in place instead of the string denoting
%% its name.
%%......................................................................
%%  \UD@name<emptiness or tokens other than braces>{<Name of
%%                                                   Control Sequence>}
%%
%% yields:
%%
%%  <emptiness or tokens other than braces>\Controlsequence
%%
%% E.g.,
%%
%%   \UD@name foo{bar} -> foo\bar
%%   \UD@name{bar} -> \bar
%%   \UD@name\newcommand*{wEirdName}[1]{Arg 1: (#1)}
%%       -> \newcommand*\wEirdName[1]{Arg 1: (#1)}
%%
\newcommand\UD@name{}\long\def\UD@name#1#{\romannumeral\UD@@name{#1}}%
\newcommand\UD@@name[2]{%
  \expandafter\UD@exchange\expandafter{\csname#2\endcsname}{0 #1}%
}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% (\romannumeral expansion was introduced in order to overcome the
%%  concerns and worries about improperly balanced
%%  \if..\else..\fi constructs.)
%%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Expandable Loop:
%% \UD@loopcall{<action>}%
%%          {<action if list empty>}%
%%          {<preset>}%
%%          {{<e_k>}{<e_(k+1)>}..{e_n}}% <- this is the list
%%
%% If list is empty: <action if list empty>
%% Else:
%% <action>{<e_k>}<preset> \UD@loopcall{<action>}%
%%                                  {<action if list empty>}%
%%                                  {<preset>}{{<e_(k+1)>}..{e_n}}
%%
%% <action> can be defined to mesh into the iteration-process, e.g.,
%% (ex)changing arguments like the <action if list empty>-argument for
%% the next \UD@loopcall-iteration, e.g., terminating iteration
%% prematurely under some circumstances.
%%......................................................................
\newcommand\UD@RemoveTillUD@nil{}%
\long\def\UD@RemoveTillUD@nil#1#2\UD@nil{{#1}}%
\newcommand\UD@Extractfirstloop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@exchange{#1}}%
  {%
    \expandafter\UD@Extractfirstloop
    \expandafter{\UD@RemoveTillUD@nil#1}%
  }%
}%
\newcommand\UD@loopcall[4]{%
  \UD@CheckWhetherNull{#4}{#2}{%
    \expandafter\UD@exchange
    \expandafter{\expandafter{\UD@firstoftwo{}#4}}%
    {\UD@Extractfirstloop{#4\UD@nil}{#1}#3\UD@loopcall{#1}{#2}{#3}}%
  }%
}%
%%======================================================================
%% Expandable comparison of two strings:
%%
%% Derived from David Kastrup's \ifstrequal-test from the
%% TeX Pearl Diving Site;
%% Pearls of 2005;
%% Title: David Kastrup - Comparing two strings known to consist
%% only of characters ;
%% Url: <http://www.gust.org.pl/projects/pearls/2005p/david-kastrup/bachotex2005-david-kastrup-pearl1.pdf>
%%......................................................................
%% \UD@ifstrequal{<String 1>}{<String 2>}%
%%               {%
%%                  <Tokens to be delivered in case strings are equal>
%%               }%
%%               {%
%%                  <Tokens to be delivered in case strings are
%%                   not equal>
%%               }%
%%
%% (<String 1> gets expanded during comparison.
%%  <String 2> gets not expanded during comparison.
%%
%% I bloated this thing up for ensuring it also takes spaces into
%% account.
%%
\@ifdefinable\UD@strchksp{%
  \long\def\UD@strchksp#1#2#3#4 #5\relax{%
    #1{\if\UD@strequal\UD@secondoftwo{}{#4}%
       \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
       {\if\UD@strequal\UD@secondoftwo{}{#5}%
        \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
        {\UD@strequalstart#1{#2}{#3} \relax}%
        {\UD@strequalstart#1{#2}{#3}{ }#5\relax}}}%
    {\UD@strequalstart#1{#2}{#3}#4 #5\relax}%
  }%
}
\newcommand\UD@strequal[2]{\number\UD@strchksp#1{}{}#2 \relax}
\newcommand\UD@strequalstart[4]{%
  \if#4\relax\UD@strequalstop\fi
  \UD@strchksp#1{\if#4#2}{#3\fi}%
}%
\@ifdefinable\UD@strequalstop{%
  \long\def\UD@strequalstop\fi\UD@strchksp#1#2#3#4{\fi#2#4\relax'#313 }%
}
\newcommand\UD@ifstrequal[2]{%
  \romannumeral0%
  \if\UD@strequal\@firstofone{#2.}{#1.}%
  \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
  {\UD@exchange{ \UD@firstoftwo}}{\UD@exchange{ \UD@secondoftwo}}%
  {\expandafter\expandafter\expandafter}%
}%
%%======================================================================
%% Total expansion and stringification of argument:
%%......................................................................
%% \UD@stringify{<argument that expands to character tokens>}
%%
%% Does \csname..\endcsname with the argument, then \string,
%% then removal of \escapechar if that was added.
%%
\newcommand\UD@stringify[1]{%
  \romannumeral0%
  \expandafter\expandafter\expandafter\UD@exchange
  \expandafter\expandafter\expandafter{%
  \expandafter\string\csname#1\endcsname}%
  {%
    \ifnum\the\escapechar<0 %
    \expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
    {%
      \ifnum\the\escapechar>\ifxetex 1114111 %
      \else\ifluatex 1114111 \else 255 \fi\fi
      \expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
      {%
        \ifnum\the\escapechar=32 %
        \expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
        {\UD@firstoftwo{ }}{}%
      }{ }%
    }{ }%
  }%
}%
%%======================================================================
%% Property-Management:
%%......................................................................
%%
%% The concept is about maintaining macros that expand to lists of
%% 2-tuple-arguments.
%% The first component of the tuple holds the name of a property.
%% The second component holds the value of the property.
%%
%% E.g., the macro \macro could be defined to expand to:
%%
%%   {{property name 1}{property value 1}}%
%%   {{property name 2}{property value 2}}%
%%   ...
%%   {{property name K}{property value K}}%
%%
%%
%% \SetProperty{\macro}%
%%             {<name of property>}%
%%             {<new value of property>}
%%
%%   In case \macro is undefined, \macro will be defined empty.
%%
%%   In case a property <name of property> does not exist within the
%%   macro \macro, it will be added to the macro and it will get the
%%   value <new value of property>.
%%
%%   In case a property <name of property> does exist within the macro
%%   \macro, its value will get replaced by <new value of property>.
%%
%%   Before further evaluation <name of property> will be expanded via
%%   \csname..\endcsname-expansion and afterwards "stringified" by
%%   applying \string and removing a leading escapechar if one was
%%   added.
%%
%%   Example:
%%
%%     \SetProperty{\macro}{property name 2}{changed property value 2}
%%
%%        would make \macro to expand to
%%
%%     {{property name 1}{property value 1}}%
%%     {{property name 2}{changed property value 2}}%
%%     ...
%%     {{property name K}{property value K}}%
%%
%%       and
%%
%%     \SetProperty{\macro}{property name (K+1)}{property value (K+1)}
%%
%%        would make \macro to expand to
%%
%%     {{property name 1}{property value 1}}%
%%     {{property name 2}{changed property value 2}}%
%%     ...
%%     {{property name K}{property value K}}%
%%     {{property name (K+1)}{property value (K+1)}}%
%%
%%
%% \GetProperty{\macro}%
%%             {<name of property>}%
%%             {%
%%               <tokens to be delivered in case
%%                property is not available>
%%             }
%%
%%   In case \macro is undefined or property <name of property> does not
%%   exist within the macro \macro,
%%   <tokens to be delivered in case property is not available>
%%   will be delivered.
%%
%%   In case a property <name of property> does exist within the macro
%%   \macro, its value will be delivered.
%%
%%   Before further evaluation <name of property> will be expanded via
%%   \csname..\endcsname-expansion and afterwards "stringified" by
%%   applying \string and removing a leading escapechar if one was
%%   added.
%%
%%   \GetProperty is expandable and delivers the result after two
%%   expansion steps / after being hit "twice" by \expandafter.
%%
%%   E.g., with the macro \macro being defined to expand to
%%
%%     {{property name 1}{property value 1}}%
%%     {{property name 2}{property value 2}}%
%%     ...
%%     {{property name K}{property value K}}%
%%
%%   , the sequence
%%
%%     \GetProperty{\macro}{property name 2}{Huh?}
%%
%%   will expand to:
%%
%%     property value 2
%%
\newcommand\UD@ExpandProperties[3]{%
  \expandafter\UD@PassFirstToSecond\expandafter{#1}%
  {\expandafter\expandafter\expandafter\UD@PassFirstToSecond
  \expandafter\expandafter\expandafter{%
  \UD@stringify{#2}}{#3}}%
}%
\newcommand\UD@AtIfPropertyListUndefined[1]{%
  \@ifundefined{%
    \expandafter\UD@exchange\expandafter{\string#1}%
    {%
      \ifnum\the\escapechar<0 %
      \expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
      {%
        \ifnum\the\escapechar>\ifxetex 1114111 %
        \else\ifluatex 1114111 \else 255 \fi\fi
        \expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
        {%
          \ifnum\the\escapechar=32 %
          \expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
          {\UD@firstoftwo{}}{\UD@removespace}%
        }{}%
      }{}%
    }%
  }%
}%
\newcommand\SetProperty[2]{%
  \@bsphack
  \UD@AtIfPropertyListUndefined{#1}{\newcommand*#1{}}{}%
  \romannumeral0%
  \UD@ExpandProperties{#1}{#2}{\UD@@setproperty}{#1}%
}%
\newcommand\UD@@setproperty[4]{%
  \UD@loopcall{\UD@@@setproperty}%
              { \global\def#3{{{#1}{#4}}}\@esphack}%
              {{#1}{#4}{ \global\def#3}{}}%
              {#2}%
}%
\newcommand\UD@@@setproperty{}%
\long\def\UD@@@setproperty#1#2#3#4#5\UD@loopcall#6#7#8#9{%
  \UD@ifstrequal{\UD@firstoftwo#1}{#2}%
  {%
    #4{#5{{#2}{#3}}#9}\@esphack
  }{%
    \UD@loopcall{#6}%
                {#4{#5{#1}{{#2}{#3}}}\@esphack}%
                {{#2}{#3}{#4}{#5{#1}}}%
                {#9}%
  }%
}%
\newcommand\GetProperty[3]{%
  \romannumeral0%
  \UD@AtIfPropertyListUndefined{#1}{ #3}{%
    \UD@ExpandProperties{#1}{#2}{\UD@@getproperty}{ #3}%
  }%
}%
\newcommand\UD@@getproperty[3]{%
  \UD@loopcall{\UD@@@getproperty}%
              {#3}%
              {{#1}}%
              {#2}%
}%
\newcommand\UD@@@getproperty{}%
\long\def\UD@@@getproperty#1#2\UD@loopcall#3#4#5#6{%
  \UD@ifstrequal{\UD@firstoftwo#1}{#2}%
    {\UD@exchange{ }\expandafter\UD@secondoftwo#1}%
    {%
      \UD@loopcall{#3}%
                  {#4}%
                  {#5}%
                  {#6}%
    }%
}%
%%==== End of code for the \SetProperty-\GetProperty-Interface =========


%%==== Layout of this example ==========================================
%   - No headers / no footers
\pagestyle{empty}
%   - paragraph-breaking:
\parindent=0ex
\parskip=\medskipamount
%   - horizontal margins:
\setlength\textwidth{\paperwidth}
\addtolength\textwidth{-3cm}
\setlength\oddsidemargin{1.5cm}%
\addtolength\oddsidemargin{-1in}%
\addtolength\oddsidemargin{-\hoffset}%
\setlength\evensidemargin{\oddsidemargin}%
\setlength\marginparwidth{1.5cm}%
\addtolength\marginparwidth{-2\marginparsep}%
%   - vertical margins:
\setlength\topmargin{1.5cm}
\addtolength\topmargin{-1in}
\addtolength\topmargin{-\voffset}
%   - no headheight/headsep etc as there are no headers as
%     pagestyle=empty:
\setlength\headsep{0pt}
\setlength\headheight{0pt}
\setlength\footskip{0pt}
\setlength\textheight{\paperheight}
\addtolength\textheight{-3cm}%
\addtolength\textheight{-\footskip}%
\addtolength\textheight{-\headsep}%
\addtolength\textheight{-\headheight}%
%   - allow linebreaks after closing braces in typewriter font:
\DeclareFontFamily{\encodingdefault}{\ttdefault}{\hyphenchar\font=`\}}
%   - in case of pdftex also adjust the underlying paper
\@ifundefined{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}%
\@ifundefined{pdfpageheight}{}{\pdfpageheight=\paperheight}%
\@ifundefined{pagewidth}{}{\pagewidth=\paperwidth}%
\@ifundefined{pageheight}{}{\pageheight=\paperheight}%
%%==== Layout-changes etc done.=========================================

\newcommand\Errortext[3]{%
  \nfss@text{\reset@font\bfseries#3}%
  \GenericError{\space\@spaces\@spaces}%
               {Error: \string#1: Translation into language #2 not available}%
               {Source for further information on this error is neither available nor needed.}%
               {You can use \string\SetProperty for specifying a translation.}%
}%

\SetProperty{\mymacro}{English}{This is a sentence in English.}
\SetProperty{\mymacro}{Francais}{Ceci est une phrase en fran\c cais.}
\SetProperty{\mymacro}{Italiano}{Questa \`e una frase in italiano.}
\SetProperty{\mymacro}{Deutsch}{Dies ist ein Satz in deutscher Sprache.}

\makeatother

\begin{document}
\verb|\SetProperty{\mymacro}{English}{This is a sentence in English.}|\\
\verb|\SetProperty{\mymacro}{Francais}{Ceci est une phrase en fran\c cais.}|\\
\verb|\SetProperty{\mymacro}{Italiano}{Questa \`e una frase in italiano.}|\\
\verb|\SetProperty{\mymacro}{Deutsch}{Dies ist ein Satz in deutscher Sprache.}|
\\\null\hrulefill\null

\verb|\GetProperty{\mymacro}{English}{\Errortext{\mymacro}{English}{No translation available.}}|\\
\GetProperty{\mymacro}{English}{\Errortext{\mymacro}{English}{No translation available.}}

\verb|\GetProperty{\mymacro}{Francais}{\Errortext{\mymacro}{Francais}{Aucune traduction disponible.}}|\\
\GetProperty{\mymacro}{Francais}{\Errortext{\mymacro}{Francais}{Aucune traduction disponible.}}

\verb|\GetProperty{\mymacro}{Italiano}{\Errortext{\mymacro}{Italiano}{Nessuna traduzione disponibile.}}|\\
\GetProperty{\mymacro}{Italiano}{\Errortext{\mymacro}{Italiano}{Nessuna traduzione disponibile.}}

\verb|\GetProperty{\mymacro}{Deutsch}{\Errortext{\mymacro}{Deutsch}{Keine Übersetzung vorhanden.}}|\\
\GetProperty{\mymacro}{Deutsch}{\Errortext{\mymacro}{Deutsch}{Keine Übersetzung vorhanden.}}

\verb|% This will raise an error:|\\    
\verb|\GetProperty{\mymacro}{Esperanto}{\Errortext{\mymacro}{Esperanto}{Neniu traduko havebla.}}|\\
% This will raise an error:
\GetProperty{\mymacro}{Esperanto}{\Errortext{\mymacro}{Esperanto}{Neniu traduko havebla.}}

\end{document}

在此处输入图片描述

相关内容