【问题标题】:Controller I18n encoding控制器 I18n 编码
【发布时间】:2018-11-02 14:53:43
【问题描述】:

在 Rails 控制器中,我想计算要发送到前端的 i18n 消息。我进行如下操作:

flash[:notice] = I18n.t 'programs.update.program_saved'

在我的翻译文件 (fr.yml) 中,翻译如下:'Programme sauvegardé'。

在这一行设置断点,然后在我的控制台中键入它时,我遇到了编码问题:

0> I18n.t 'programs.update.program_saved'
=> "Programme sauvegardé"

我已经实现了AJAX pattern 来处理flash 消息,并且在前端,我可以看到相同的编码问题。

除此之外,当我在 rails console 中输入相同的内容时,我没有编码问题。

我在Ruby 2.4.4Rails 5.2.1

什么会导致这个编码问题,以及如何摆脱它?

编辑:添加更多详细信息

我使用RubyMine 2018.2 进行开发。我的 Rails 服务器在 WSL (Windows Subsystem for Linux)Ubuntu 16 下运行。我从Windows 一侧的RubyMine 运行我的rails 服务器。使用的rails SDKLinuxrails serverLinux 一侧运行。

当显示 HTTP 请求响应中设置的 flash 消息时,我的问题的根源是浏览器中的编码问题。这些 flash 消息按说明计算,即:I18n.t 'programs.update.program_saved'

当我从 RubyMine 或直接从 Linux 终端启动 Rails 服务器时,问题也是一样的。

为了进行调查,我想调试并使用 RubyMine 控制台。从 RubyMine 调试器控制台执行此命令时,仍然存在编码问题:I18n.t 'programs.update.program_saved'。从rails consoleLinuxWindows(在RubyMine 中,rails 控制台在Linux 一侧执行)执行此操作时,我没有编码问题。

此外,在 Heroku 实例上运行应用程序时问题仍然存在,所以我想知道这是否与我的本地配置有关。

【问题讨论】:

    标签: ruby-on-rails encoding rails-i18n


    【解决方案1】:

    很明显,您的字符串被解释为“ISO-8859-1”,尽管它实际上是“UTF-8”。 您可以在 irb 或 Rails 控制台中使用以下代码 sn-p 检查事实:

    s=[0x64,0xc3,0xa9].pack('c*')  # => "d\xC3\xA9" ("dé" if UTF-8)
    s.encoding    # => #<Encoding:ASCII-8BIT>
    s.encode "UTF-8", "UTF-8"      # => "dé"  ("de'")
    s.encode "UTF-8", "ISO-8859-1" # => "dé" ("d~A(c)")
    

    对于出错的原因,我可以想到两种可能性。

    案例一

    运行 Rails 控制台的终端要么无法解释 UTF-8 字符串,要么设置错误。

    试试下面的代码 sn-p (nb.,它可以被任何人运行,即使翻译未定义):

    s2 = I18n.t('programs.update.program_saved', :default => nil)
    s2 ||= [0x64,0xc3,0xa9].pack('c*').encode("UTF-8", "UTF-8")  # => "dé"  ("de'")
    p s2[-2,2].bytes  # => [100, 195, 169]  if the object is in UTF-8
                      # => [100, 233]       if the object is in ISO-8859-1
    

    你可以看到 String 对象的(内部)编码是怎样的。 如果它是[100, 195, 169],那么编码是UTF-8,因此Ruby 和Rails 都将翻译后的String 对象正确地视为UTF-8,所以问题出在你的终端上。您的终端错误地将它从 Rails 收到的字节字符串 [100, 195, 169] 解释为 ISO-8859-1 并选择相应的字符和字体来显示。

    在终端上的 Rails consolei 中,你可以试试这个;如果终端兼容 UTF-8,它应该正确显示字符:

    [0x64,0xc3,0xa9].pack('c*').force_encoding('UTF-8')
      # => "dé" ("de'") should be displayed.
    

    看看你的终端确实能够显示 UTF-8 字符串(大多数现代终端应该能够,但旧的可能不行)。 另外,检查您的终端设置。这个answer to "How to input Unicode character in Rails console?" 可能会有所帮助。

    案例 2

    Ruby 将输入字符串解释为“ISO-8859-1”并在内部将其转换为“UTF-8”(尽管在默认设置中不应该发生这种情况)。 在这种情况下,您的 yml 文件可能包含一些看起来像“ISO-8859-1”的字符;那么 Rails 可能会将整个文件解释为“ISO-8859-1”(尽管不太可能)。

    您可以检查您读取的文件 (config/locales/fr.yml) 是否确实是 UTF-8 格式,如下所示:

    fn = 'config/locales/fr.yml'
    IO.binread(fn).force_encoding('UTF-8').valid_encoding?  # => should be true
    IO.binread(fn).force_encoding('ISO-8859-1').valid_encoding?  # => false
    

    不幸的是,有一点缺陷。一些 UTF-8 字符可以合法地解释为 ISO-8859-1,在这种情况下,代码(Rails)如何解释它可能会有所不同。如果您怀疑是这种情况,您可以查看上述命令的输出,如IO.binread(fn).force_encoding('UTF-8'),看看每个字符是否都符合预期。

    如果文件包含一些非 UTF-8 字符,请修复它,希望一切都会好起来。

    或者,在您的特定情况下,您也许可以将其修复为像这样的拙劣工作

    I18n.t('programs.update.program_saved').encode('UTF-8', 'ISO-8859-1')
    

    注意

    只要您想将 Rails 设为默认为 UTF-8(强烈建议),请确保您的应用的默认编码为 UTF-8。通过

    查看
    MyApp::Application.config.encoding  # => #<Encoding:UTF-8>
    

    (参考:Configuring Rails Applications

    另外,如果您使用 Heroku,请将默认字符集设置为 UTF-8。请参阅answer to "Set UTF-8 as default string encoding in Heroku"


    请注意,在 2018 年 11 月 5 日进行了重大更新以添加案例 1。

    【讨论】:

    • IO.binread(fn).force_encoding('ISO-8859-1').valid_encoding? 返回true.IO.binread(fn).force_encoding('UTF-8').valid_encoding? 返回trueI18n.t('programs.update.program_saved').encode('UTF-8', 'ISO-8859-1') 返回Programme sauvegardéI18n.t('programs.update.program_saved').encode('ISO-8859-1', 'UTF-8') 按我的预期返回 Programme sauvegardé。我用 UTF8 从头开始​​重新创建了我的fr.yml,但它没有改变任何东西。 MyApp::Application.config.encoding 按预期返回 UTF-8。这意味着编码ISO-8859-1 有问题。还没找到。
    • @RémiDoolaeghe 那么问题可能出在您的终端上。我对我的答案进行了重大更新(添加案例 1)。看看吧。
    • 听起来它植根于您的(统一)开发/部署环境,而不是 Rails 本身。我从未使用过 Heroku,但似乎 Heroku 有自己的编码问题。 this answer 有什么帮助吗?
    • @RémiDoolaeghe 您是否检查过 HTTP 响应标头,尤其是 Content-Type 中的 charset?它应该是 UTF-8。如果不是,则可能是您的 HTTP 服务器(如 Apache)的配置有问题。 “Content-Type”的 HTML 标头的 meta 标签怎么样?另外,您是否检查过您在浏览器中看到的实际字节字符串? I18n.t 的输出如预期的那样采用 UTF-8 格式,这表明 Rails 正在正确处理翻译。所以,问题出在其他地方,无论是浏览器、HTTP 服务器、Heroku 还是 Rails 中的最外层接口……
    • 请求中的字符集设置为UTF-8。它不在响应中,但Content-Type 不会出现在请求响应标头中,即使我手动设置它也是如此。在 HTML 元中,我将 charset 设置为 utf-8。我已经实现了一种解决方法,它可以解决编码问题,但我对此并不满意:我在请求响应标头中强制执行编码,它可以工作:response.headers['X-Message'] = flash_message.encode('ISO-8859-1', 'UTF-8')。所以问题不在于浏览器。它介于翻译和请求响应构建之间。
    【解决方案2】:

    确保您以 utf-8 格式打开 fr.yml 文件,以便您写入的内容正确保存在 utf-8 中。这可能是您的浏览器正在使用的编码。

    您可以转到 Linux 控制台并通过查看 LANG 变量的值来查找当前配置。我有例如LANG="ca_ES.UTF-8"。也许您还可以检查终端窗口的编码属性。

    还要检查您正在查看您的网站的编码。例如,在 Firefox 中,检查查看/编码选项。

    最后,您需要以与保存内容相同的编码查看内容。

    【讨论】:

    • 我的fr.yml 是UTF8,我可以在RubyMine 中看到它。我的 Linux LANG 也是 UTF8。我看不到任何不是 UTF8 的东西,但我必须错过一些东西。
    • 您是否检查过您的浏览器使用哪种编码来查看页面?通常浏览器使用统计确定来选择编码,因此如果他们发现 iso 特定字符,他们会猜测这是正确的编码。所以你必须确保没有 ISO 字符被渲染到视图中
    • 问题似乎出在浏览器收到请求之前。直接在后端调试时,我有编码问题。但奇怪的是,这不会发生在rails console
    • 我可能是当你打开rails console Ruby 猜到了正确的编码。当您打开调试控制台时,没有人会猜到这一点,并且会呈现您的默认编码,这与文件中的编码不同。
    猜你喜欢
    • 2014-02-12
    • 1970-01-01
    • 2018-01-01
    • 1970-01-01
    • 2018-08-13
    • 2021-06-16
    • 2018-02-20
    • 2014-10-17
    • 1970-01-01
    相关资源
    最近更新 更多