首页 文章

在Android中区分CJK语言(中文,日文,韩文)

提问于
浏览
5

我希望能够识别中文,日文和韩文书写字符,包括一般组和细分语言 . 原因如下:

  • Recognize CJK as a general group: 我正在制作蒙古文 TextView 的垂直脚本 . 为此,我需要将文本行旋转90度,因为字形水平存储在字体中 . 但是,对于CJK语言,我需要再次将它们旋转回来,以便它们以正确的方向书写,但只是在线上堆叠在一起 .

  • Differentiate CJK into specific languages: 我'm also making a Mongolian dictionary and when users enter a CJK character to lookup I would like to automatically recognize the language. Because Chinese characters are also used by Japanese and Koreans, I'猜测我无法完全实现这一点,但我想在编码允许的最大程度上做到这一点 .

在语言方面,我所知道的子类别是

为了完整起见,Vietnamese中也使用了汉字(因此CJK也称为CJKV) . 对于我目前的目的,我不需要担心它,但它可能是未来的考虑因素 . 我也忽略了像中文pinyin或日文romaji这样的罗马化脚本 . 它们将在TextView中与英语和蒙古语相同(即与其余部分一起旋转90度) . 在台湾使用的Bopomofo也可能是未来的考虑因素,但我暂时不理会它 . 有关语言示例,另请参见herehere .

我已经看到了一些相关的问题,这些问题通常涉及Java或Android中的一种特定语言,但没有一个与规范性答案有关的首要问题 . 其他问题对于Unicode更为通用,但没有说明如何在Java和Android中执行此操作 . 以下是一些具体的内容 .

所以我的问题是,我可以使用Unicode代码点区分CJK语言多少?如何在Android中测试它们?我在Java和Android上看到了一些较新的测试,虽然这些测试很有用,但我还需要支持较旧的Android设备 .

1 回答

  • 13

    Unicode

    Unicode中的CJK(和CJKV)指汉字表意文字,即汉语,日语,韩语和越南语中使用的汉字(汉字) . 对于Unicode脚本命名,它不涉及日语片假名和平假名或韩语韩语等语音书写脚本 . 据说Han Ideagraphs是统一的 . 这意味着每个表意文字只有一个Unicode代码点,无论它使用哪种语言 .

    这意味着Unicode(以及相反的Android / Java)无法单独基于单个表意文字来确定语言 . 即使是简体中文/繁体字也不容易与编码区别开来 . 这与无法知道字符“a”是否属于英语,法语或西班牙语的想法相同 . 需要更多背景来确定这一点 .

    但是,您可以使用Unicode编码来确定日语平假名/片假名和韩语韩语 . 这些角色的存在将很好地表明附近的汉字表意文字属于同一种语言 .

    Android

    您可以在某个索引处找到代码点

    int codepoint = Character.codePointAt(myString, offset)
    

    如果你想iterate through the codepoints in a string

    final int length = myString.length();
    for (int offset = 0; offset < length; ) {
        final int codepoint = Character.codePointAt(myString, offset);
    
        // use codepoint here
    
        offset += Character.charCount(codepoint);
    }
    

    获得代码点后,您可以查找它所在的代码块

    Character.UnicodeBlock block = Character.UnicodeBlock.of(codepoint);
    

    然后,您可以使用代码块来测试表意文字或语言 .

    CJK

    扫描Unicode代码块,我认为它们涵盖了所有的CJK表意文字 . 如果我错过任何一个,那么随时编辑我的答案或发表评论 .

    private boolean isCJK(int codepoint) {
        Character.UnicodeBlock block = Character.UnicodeBlock.of(codepoint);
        return (
                Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS.equals(block)||
                Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A.equals(block) ||
                Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B.equals(block) ||
                Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C.equals(block) || // api 19
                Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D.equals(block) || // api 19
                Character.UnicodeBlock.CJK_COMPATIBILITY.equals(block) ||
                Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS.equals(block) ||
                Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS.equals(block) ||
                Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT.equals(block) ||
                Character.UnicodeBlock.CJK_RADICALS_SUPPLEMENT.equals(block) ||
                Character.UnicodeBlock.CJK_STROKES.equals(block) ||                        // api 19
                Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION.equals(block) ||
                Character.UnicodeBlock.ENCLOSED_CJK_LETTERS_AND_MONTHS.equals(block) ||
                Character.UnicodeBlock.ENCLOSED_IDEOGRAPHIC_SUPPLEMENT.equals(block) ||    // api 19
                Character.UnicodeBlock.KANGXI_RADICALS.equals(block) ||
                Character.UnicodeBlock.IDEOGRAPHIC_DESCRIPTION_CHARACTERS.equals(block));
    }
    

    带注释的(向右滚动)仅可从API级别19获得 . 但是,如果您需要支持早期版本,则可能会安全地删除它们,因为它们很少使用 . 此外,Unicode定义了CJK扩展E,但在撰写本文时,它在Android / Java中不受支持 . 如果您确实需要包含所有内容,则可以直接将代码点与Unicode块范围进行比较 . This site is a convenient place to browse them.您也可以在Unicode site看到它们 .

    如果您不需要支持API 19以下,那么 isIdeographic 使测试非常简单(虽然我不知道它是否真的返回与上述方法相同的匹配) .

    private boolean isCJK(int codepoint) {
        return Character.isIdeographic(codepoint);
    }
    

    或者这个API 24:

    private boolean isCJK(int codepoint) {
        return (Character.UnicodeScript.of(codepoint) == Character.UnicodeScript.HAN);
    }
    

    Japanese

    为了测试平假名或片假名,这应该工作正常:

    private boolean isJapaneseKana(int codepoint) {
        Character.UnicodeBlock block = Character.UnicodeBlock.of(codepoint);
        return (
                Character.UnicodeBlock.HIRAGANA.equals(block) ||
                Character.UnicodeBlock.KATAKANA.equals(block) ||
                Character.UnicodeBlock.KATAKANA_PHONETIC_EXTENSIONS.equals(block));
    }
    

    或者,如果您支持API 24:

    (这需要更多测试 . 请参阅下面的评论 . )

    private boolean isJapaneseKana(int codepoint) {
        return (Character.UnicodeScript.of(codepoint) == Character.UnicodeScript.HIRAGANA || 
                Character.UnicodeScript.of(codepoint) == Character.UnicodeScript.KATAKANA);
    }
    

    Korean

    要在较低的API上测试Hangul,您可以使用

    private boolean isKoreanHangul(int codepoint) {
        Character.UnicodeBlock block = Character.UnicodeBlock.of(codepoint);
        return (Character.UnicodeBlock.HANGUL_JAMO.equals(block) ||
                Character.UnicodeBlock.HANGUL_JAMO_EXTENDED_A.equals(block) || // api 19
                Character.UnicodeBlock.HANGUL_JAMO_EXTENDED_B.equals(block) || // api 19
                Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO.equals(block) ||
                Character.UnicodeBlock.HANGUL_SYLLABLES.equals(block));
    }
    

    如有必要,删除标记为API 19的行 .

    或者对于API 24:

    private boolean isKoreanHangul(int codepoint) {
        return (Character.UnicodeScript.of(codepoint) == Character.UnicodeScript.HANGUL);
    }
    

    进一步研究

相关问题