资产文件的本地化

57

我有几个HTML文件在assets文件夹中。如何将它们本地化?我的唯一选择是在代码中硬编码以根据语言环境选择正确的文件吗?


1
我需要使用资产,因为在HTML中嵌入的图像无法从原始资源中工作。如果是简单的HTML,则原始方式是可行的。这里有一个教程,供那些想知道如何从原始文件读取HTML的人参考 monocube.com/2011/02/08/android-tutorial-html-file-in-webview - mishkin
6个回答

66

这个并不直接支持,但我所做的是...

将文件按国家代码分组(就像正常资源文件一样),然后在每个本地化的string.xml文件中创建一个本地化字符串,例如叫做"prefix"(其中"prefix"可以是英语的"en")。

然后当你构建资产文件名时,简单地使用类似于getString("prefix") + "-" + "<name-of-asset->这样的东西。

至少上述某种变体应该适用于您。


16
好的,我会尽力进行翻译。以下是需要翻译的内容:thanks! improving your idea a bit, I can use Locale.getLanguage().substring(0, 2).toLowerCase() to get that prefix so I do not need to add prefix var to string.xml谢谢!结合您的建议,我可以使用Locale.getLanguage().substring(0, 2).toLowerCase()来获取该前缀,因此我不需要将前缀变量添加到string.xml文件中。 - mishkin
10
请注意,在Android中,getLanguage().substring(0, 2)不是一个万能的方法,因为它无法处理zh-rTW(台湾地区的繁体中文,也称为传统中文;而zh表示简体中文)。 - Youngjae
3
非常好的解决方案。我正在使用这个的变体将东西放在 assets/en/...,assets/es/... 等下面。一个好处是,如果你在 strings.xml 中定义了前缀,你就知道目录在 assets 中存在(只要你每次添加新的本地化 strings.xml 时创建目录)。我不仅用它来本地化 html,还用它来本地化一些其他资源。 - mkasberg
2
我使用Locale.getDefault().getLanguage()来获取前缀,因为Locale.getLanguage()无法正常工作。 - Plugie
+1 给 mkasberg,这个解决方案确保您在资产目录中使用的文件始终对应于您正在使用的 strings.xml 文件。 - Lyck

46
将您的文件放入带有本地后缀的资产文件夹中。为每个文件定义一个String资源'myLocalizedFileName',并通过R.string.myLocalizedFileName获取文件名。
示例:
文件夹结构:
assets/
assets/help.html
assets/help_de.htlm

每种语言的字符串资源在res/values/strings.xml中:

<resource>
  <string name=helpFile>help.html</string>
</resource>

WebView调用:

public class HelpActivity extends AppCompatActivity {
  protected void onCreate(Bundle savedInstanceState) {
    ...
    findViewById(R.id.helpWebView)
      .loadUrl("file:///android_asset/" 
         + getString(R.string.helpFile));
  }
}

感谢您的方法,简洁明了。 - Robin Vinzenz
在字符串资源中保存路径是一个好的解决方案。 - Gurgen Hakobyan
1
@Shadow 首先是 help_de.html,其次,这是它的工作方式:在默认值中打开翻译编辑器,你会看到 help.html,在德语中,你只需要编写 help_de.html。就这样,它就能为你工作了。 - Waheed Akhtar
但是使用原始资源持有者呢?这样你就不需要处理本地化的问题了。https://developer.android.com/reference/android/content/res/Resources.html#openRawResource(int) - user1221256

31
如果您想本地化 HTML 文件,只需将其放置在 res/raw-<language>/filename.html(其中 <language>=en, es, fr, it, 等)下,然后通过资源 ID R.raw.filename 从代码中访问。框架将根据语言环境选择正确的文件。

1
请看我上面关于HTML嵌入图像的评论 - 这就是为什么我不能使用资源的原因。 - mishkin
1
这个解决方案非常适合像PDF这样的文件。谢谢。 - Hong
同样适用于txt文件... getResources().openRawResource(R.raw.filename) - Veener

3

如果不想使用每个国家代码一个文件的方式(如Andrew WhitePJ_Finnegan的回答中所述),则可以定义一次HTML(例如在assets文件夹中),并在其中使用@string ID,如下所示。

<html>
<body>
    <p>@string/message_text</p>
</body>
</html>

将资产加载到字符串中后,您可以将其内容传递给replaceResourceStrings()

/**
 * Regex that matches a resource string such as <code>@string/a-b_c1</code>.
 */
private static final String REGEX_RESOURCE_STRING = "@string/([A-Za-z0-9-_]*)";

/** Name of the resource type "string" as in <code>@string/...</code> */
private static final String DEF_TYPE_STRING = "string";

/**
 * Recursively replaces resources such as <code>@string/abc</code> with
 * their localized values from the app's resource strings (e.g.
 * <code>strings.xml</code>) within a <code>source</code> string.
 * 
 * Also works recursively, that is, when a resource contains another
 * resource that contains another resource, etc.
 * 
 * @param source
 * @return <code>source</code> with replaced resources (if they exist)
 */
public static String replaceResourceStrings(Context context, String source) {
    // Recursively resolve strings
    Pattern p = Pattern.compile(REGEX_RESOURCE_STRING);
    Matcher m = p.matcher(source);
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        String stringFromResources = getStringByName(context, m.group(1));
        if (stringFromResources == null) {
            Log.w(Constants.LOG,
                    "No String resource found for ID \"" + m.group(1)
                            + "\" while inserting resources");
            /*
             * No need to try to load from defaults, android is trying that
             * for us. If we're here, the resource does not exist. Just
             * return its ID.
             */
            stringFromResources = m.group(1);
        }
        m.appendReplacement(sb, // Recurse
                replaceResourceStrings(context, stringFromResources));
    }
    m.appendTail(sb);
    return sb.toString();
}

/**
 * Returns the string value of a string resource (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param name
 * @return the value of the string resource or <code>null</code> if no
 *         resource found for id
 */
public static String getStringByName(Context context, String name) {
    int resourceId = getResourceId(context, DEF_TYPE_STRING, name);
    if (resourceId != 0) {
        return context.getString(resourceId);
    } else {
        return null;
    }
}

/**
 * Finds the numeric id of a string resource (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param defType
 *            Optional default resource type to find, if "type/" is not
 *            included in the name. Can be null to require an explicit type.
 * 
 * @param name
 *            the name of the desired resource
 * @return the associated resource identifier. Returns 0 if no such resource
 *         was found. (0 is not a valid resource ID.)
 */
private static int getResourceId(Context context, String defType,
        String name) {
    return context.getResources().getIdentifier(name, defType,
            context.getPackageName());
}

这种方法的好处在于只需一次指定HTML结构并使用Android的本地化机制。此外,它允许在strings.xml中递归引用字符串,而Context.getResources()不支持。例如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="message_text">Some string @string/another_one.</string>
</resources>

缺点是解析是在运行时完成的,因此为每种语言指定专用的HTML在应用内使用时具有更好的性能。

使用此代码从资产文件转换HTML以生成可在TextView中显示的“可样式化”CharSequence(使用{{link1:Kuitsi's TagHandler}}),请参见{{link2:TextUtil }}的示例。


2
将文件放在 raw-LOCALE 文件夹中会自动选择正确的位置,但是如果您在 webView 中加载这些文件,则在使用 Proguard 进行混淆后路径将会破损。 Proguard Breaks Android WebView, Why? 那么唯一的解决方案是使用 assets 文件夹并使用 file-LOCALE 并选择正确的文件。

1
尝试使用assets-ja进行本地化不起作用,因为这些不是资源文件。最好的选择是通过正确的语言环境进行编程本地化。或者,HTML文件的内容只是纯文本。如果适合您的项目,您可以尝试将此字符串存储为新的values-ja文件夹中的strings.xml(或您自己的myHtmlText.xml?)文件的条目。

4
我需要使用资产,因为 HTML 中嵌入的图像在原始格式下无法使用。但如果只是简单的 HTML,则可以使用原始格式。这里有一个教程,供那些想知道如何从原始格式读取 HTML 的人参考:http://www.monocube.com/2011/02/08/android-tutorial-html-file-in-webview/ - mishkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接