【问题标题】:Translating source code into a foreign language将源代码翻译成外语
【发布时间】:2011-08-27 15:27:02
【问题描述】:

我正在运营一个教育网站,向孩子(12-15 岁)教授编程。

由于他们在解决方案的代码源中并非都说英语,因此我们使用法语变量和函数名称。 然而,我们计划将内容翻译成其他语言(德语、西班牙语、英语)。为此,我想尽快翻译源代码。 我们主要有 C/C++ 代码。

我打算使用的解决方案:

  1. 从源代码中提取所有变量/函数名称,以及它们在文件中的位置(它们在哪里声明、使用、调用...)
  2. 删除所有语言关键字和库函数
  3. 请翻译人员为其余姓名提供翻译
  4. 替换文件中的名称

是否已经有一些开源代码/项目可以做到这一点? (对于第 1,2 和 4 点)

如果没有,第一个中最困难的一点:使用 C/C++ 解析器构建语法树,然后提取变量及其位置似乎是要走的路。你有其他想法吗?

感谢您的建议。

编辑: 正如评论中所指出的,我还需要处理 cmets,但只有少数几个:完整的解决方案已经用纯文本进行了解释,然后我们展示了带有自我解释变量/函数的代码源名字。源代码的长度很少超过 30/40 行,如果您已经知道代码在做什么,那么好的名称必须在没有 cmets 的情况下使其易于理解。

附加信息:对于感兴趣的人来说,该网站是国际信息学奥林匹克竞赛的培训平台,C/C++(至少是编程竞赛所需的最低要求)并不难学12 岁。

【问题讨论】:

  • 尝试将代码直接放入谷歌翻译。它只翻译单词做得很好。它“意外”翻译的事情可以通过运行代码来处理,该代码用已知的替代品替换它们。
  • 有些人会质疑使用 C/C++ 来教那个年龄的孩子的决定,但据我所知,我在 15 岁时编写了 C 并且没有受到任何损害。 (在更早的时候写 Pascal 对我的伤害更大,因为那里的许多关键字我没有任何发音指南。我花了好几年才停止发音“begin”,就好像它是以色列总理一样)。
  • 我认为这不是一个好主意。一个正确的外来词比一个不正确的本地词好。翻译者在翻译时将有 0 个上下文。很多词都有同音字,怎么解决?我不会翻译源代码,我会保持原样。此外,12-15 岁的孩子都已经在学校学习英语了。
  • @Karl Knechtel:我在这里使用 C/C++ 作为快捷方式。在实践中,可以认为我们正在用 C I/O(为了速度)和仅限于 struct 的类(+ 一些方法,如 < operator)来教授 C++。重点不是“C”或“C++”,而是要利用每种语言的优点和一个目标做一些 C++:用简短的代码快速编写算法挑战,没有任何错误。
  • WTF 工作你有初学者在 I/O 速度重要的地方做吗?

标签: c++ c localization


【解决方案1】:

你确定你需要一个完整的语法树吗?我认为进行词法分析以找到标识符就足够了,这要容易得多。然后排除也出现在包含的头文件中的关键字和标识符。

原则上,您可能希望将具有相同英文名称的不同变量翻译成法语/德语中的不同单词 - 但对于教育用途,这种风险可能很小,一开始可以忽略。您可以通过使用一些消除歧义的准匈牙利语前缀编写原始来源来回避该问题,然后使用相同的翻译机制将其删除以显示给讲英语的最终用户。

在选择翻译之前,请务必让翻译人员看到他们正在翻译的名称​​完整的上下文

【讨论】:

  • 确实源代码很小(但有很多)并且变量总是很好命名,特别是:如果含义不同,永远不要使用相同的名称,没有一个字母的变量.. . 是的,翻译者将有完整的上下文,这个翻译工具只是为了帮助他。
【解决方案2】:

我真的认为你可以使用 clang (libclang) 来解析你的源代码并做你想做的事 (see here for more information),好消息是他们有 python 绑定,这将使你的生活如果您想访问翻译服务或类似服务,则更容易。

【讨论】:

    【解决方案3】:

    您实际上并不需要 C/C++ 解析器,只需一个简单的词法分析器即可为您逐个提供代码元素。然后你会得到很多 {[213) 等,你只需忽略它们并将其写入结果文件。您翻译仅包含字母(关键字除外)的任何内容,然后将它们放入输出中。

    现在想来,就这么简单:

    bool is_letter(char c)
    {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    }
    bool is_keyword(string &s)
    {
        return s == "if" || s == "else" || s == "void" /* rest of them */;
    }
    void translateCode(istream &in, ostream &out)
    {
        while (!in.eof())
        {
            char c = in.get();
            if (is_letter(c))
            {
                string name = "";
                do
                {
                    name += c;
                    c = in.get();
                } while (is_letter(c) && !in.eof());
                if (is_keyword(name))
                    out << name;
                else
                    out << translate(name);
            }
            out << c;  // even if is_letter(c) was true, there is a new c from the
                       // while inside that was read (which was not letter), but
                       // not written, so would be written here.
        }
    }
    

    我在编辑器中写了代码,所以可能会有一些小错误。有的话告诉我,我会解决的。

    编辑:解释:

    代码所做的只是逐个字符地读取输入,输出它读取的任何非字母字符(包括空格、制表符和换行符)。如果它确实看到一个字母,它将开始将所有以下字母放在一个字符串中(直到它到达另一个非字母)。然后,如果字符串是关键字,它将输出关键字本身。如果不是,则将其翻译并输出。

    输出将与输入具有完全相同的格式。

    【讨论】:

    • void translateCode(istream &in, ostream &out);,去掉最后的;.
    • 所有关键字的列表对于 cpp:en.cppreference.com/w/cpp/keywords 和对于 c:tigcc.ticalc.org/doc/keywords.html - 但除此之外,您必须注意所有标准符号,例如 cincoutprintf 等(大量)以及头文件名。不过,这可能是一个好的开始
    • 感谢 ;.另外,很好地提醒一下 c/c++ 已经定义的函数和对象@shlubu。只需收集他们在程序中编写的应从翻译中排除的内容列表并将其放入程序中即可。
    • 说到标准符号,头文件名也要排除。
    • 有一个标准的 C isalpha 函数可以用来代替“is_letter”。
    【解决方案4】:

    我认为替换代码中的标识符不是一个好主意。

    首先,您不会得到像样的翻译。这里非常重要的一点是翻译(尤其是自动或非常愚蠢的翻译)会丢失和扭曲信息。实际上,您最终可能会得到比原来更糟糕的东西。

    其次,如果要再次编译代码,编译器可能无法编译在翻译后的标识符中包含非英文字母的代码。

    第三,如果您用其他东西替换标识符,您需要确保不要用同一个词替换 2 个或更多不同的标识符。这要么使代码不可编译,要么破坏其逻辑。

    第四,您必须确保您也不翻译来自该语言标准库的保留字和标识符。翻译这些将使代码不可编译且不可读。区分程序员定义的标识符与语言及其标准库提供的标识符可能不是一项非常简单的任务。

    我要做的不是用他们的翻译替换标识符,而是将翻译作为他们旁边的 cmets 提供,例如:

    void eat/*comer*/(int* food/*comida*/)
    {
      if (*food/*comida*/ <= 0)
      {
        printf("nothing to eat!"/*no hay que comer!*/);
        exit/*salir*/(-1);
      }
      (*food/*comida*/)--;
    }
    

    这样您就不会因翻译错误而丢失信息,也不会破坏代码。

    【讨论】: